1. React简介

  • React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站。做出来以后,发现这套东西很好用,就在2013年5月开源了
  • React 是一个用于构建用户界面的 JAVASCRIPT 库。
  • React主要用于构建UI,很多人认为 React 是 MVC 中的 V(视图)
  • React 拥有较高的性能,代码逻辑非常简单,越来越多的人已开始关注和使用它

React中文官网
React英文官网

1.1 为什么要学React

  1. 使用组件化开发方式,符合现代Web开发的趋势
  2. 技术成熟,社区完善,配件齐全,适用于大型Web项目(生态系统健全)
  3. 灵活性和响应性,React提供最大的灵活性和响应能力。
  4. 虚拟DOM及Diff算法提供很好的性能。
  5. 可拓展性,由于其灵活的结构和可拓展性,React已被证明对大型应用程序更友好。
  6. 不断发展:React得到Facebook专业开发人员的支持,他们不断寻找改进方法致力于使其更先进。
  7. 丰富的JavaScript库,来自世界各地的贡献者正在努力添加更多功能。
  8. Web或移动平台:React提供React Native平台,可以通过相同的React组件模型为iOS和Android开发本机呈现的应用程序。

特别是当

需要构建移动应用程序

需要构建大型应用程序

轻量级,易于版本迁移

专业和出色的社区支持,以解决任何问题React是更理想的选择

除Facebook,Instagram,Netflix,微软等众多国际知名互联网公司都是React.js的拥趸者外,国内很多主流互联网公司如腾讯、蚂蚁金服、京东、360、美团、携程等也在用 React ,即使有大厂由于业务等原因不直接使用,但仍没有脱离React社区。

所以对于每个想进大厂的前端开发者来说,React是个绕不过的坎。React相关问题基本是面试必考,疏于React技术栈的学习会大大降低个人竞争力。如果你能深入理解React的灵魂,包括虚拟DOM, JSX ,函数式编程和immutable,单向数据流,组件化抽象,生命周期等,在面对其他轮子时你也能做到得心应手。

1.2 React特点

  1. 声明式设计 −React采用声明范式,可以轻松描述应用。
    1. 命令式编程:命令“机器”如何去做事情(how),这样不管你想要的是什么(what),它都会按照你的命令实现。
    2. 声明式编程:告诉“机器”你想要的是什么(what),让机器想出如何去做(how)
  2. 高效 −React通过对DOM的模拟,最大限度地减少与DOM的交互。
  3. 灵活 −React可以与已知的库或框架很好地配合。
  4. JSX − JSX 是 JavaScript 语法的扩展。React 开发不一定使用 JSX ,但我们建议使用它。
  5. 组件 − 通过 React 构建组件,使得代码更加容易得到复用,能够很好的应用在大项目的开发中。
  6. 单向响应的数据流 − React 实现了单向响应的数据流,从而减少了重复代码,这也是它为什么比传统数据绑定更简单。

1.3 React对比Vue.js

  1. 首先,这两个框架,都是以组件化的思想进行开发的!
    • 什么是组件化:组件化,就是站在UI的角度,将一个页面拆分为多个互不相关的小组件;将来,我们可以很方便的去引用自己需要的组件,组件只需要定义一次,即可在任何地方去任意的使用。
    • 组件化的好处:随着项目规模扩大,开发会越来越方便;因为组件都是现成的;维护起来比较方便,只需要把组件一修改,那么所有引用组件的地方,都被修改了!对于扩展也方便;
  2. Vue.js是如何实现组件化的呢:在vue.js中,通过一个后缀名为.vue的文件实现组件化;
    • template 定义组件的结构
    • script 定义组件的行为
    • style 定义组件的样式
  3. 在React中如何实现组件化呢:其实,在React中,实现组件化的思想和vue不一样,并没有一个像.vue这样的模板文件,所以,也就没有 tempalte,script,style这些概念
    • 在React中,直接使用JS这门语言,来实现组件化,所有的组件结构、组件的行为、组件的样式,都可以通过JS直接来进行定义;
    • 所以说,在React的学习过程中,只要你会基本的JS,就能够开始学习React!
    • 只要你会用JS,那么就会定义React的组件;
  4. 从开发团队上进行对比:
    • React的开发团队,是Facebook官方大牛团队,团队技术实力雄厚;
    • Vue:第一个版本,主要是作者尤雨溪进行维护的,所以1.X的版本体验并不是很好;但是从2.X版本开始,vue.js也有了一个自己开发维护团队,但是实力之间还是有一定差距的;
  5. 从社区方面进行对比:
    • 由于React开源时间比较早,2013年就开源了,所以它的社区很完善,周边的一些相关插件比较完善;在社区中,相关的文档特别完善,而且一些Bug或者一些开发中常见的坑,好多人都已经踩过了,有了一套完整的技术解决方案;
    • Vue近两年才推出来,所以开源时间不如React,社区相对较小一些,一些相关的文档、插件、Bug的解决方案不如React多;会导致有一些问题,在网上或者社区中找不到对应的解决方案;
  6. 从移动App开发方面:
    • 使用React这门技术,可以分分钟转到ReactNative的开发中;通过React的语法,也为开发人员提供了移动端App开发的体验;
    • VUE 这门技术,也提供了无缝的转移到移动端开发的体验,通过weex可以使用VUE的语法,进行移动端APP开发

1.4 React核心概念

  • 虚拟DOM(Virtual DOM)
  • Diff算法(Diff Algorithm)
  • 单向数据流渲染(Data Flow)
  • 组件生命周期
  • JSX
  • 一切皆为组件

2. 搭建开发环境

mkdir react-demo

cd react-demo

npm init -y

npm install react react-dom -S

npm install webpack webpack-cli webpack-dev-server babel babel-cli babel-core babel-loader@7 babel-preset-react babel-preset-env babel-preset-es2015 -D (解析jsx和es6语法)

注意:babel 6.x版本必须安装babel-loader@7 babel 7.x 必须安装babel-loader@8

  • webpack.config.js
module.exports = {entry: './main.js',output: {path: '/',filename: 'index.js',},module: {rules: [{test: /\.js?$/,exclude: /(node_modules)/,use: {loader: 'babel-loader',options: {presets: ['env', 'react', 'es2015']}}}]}
}
  • index.html

    <!DOCTYPE html>
    <html lang="en"><head><meta charset="UTF-8"><title>React App</title>
    </head><body><div id="root"></div><script src="index.js"></script>
    </body></html>
    
  • main.js

    // React 是 React 库的入口
    // React组件可以通过扩展 React.Component来定义
    import React, { Component } from 'react';
    // react-dom 包提供了 DOM 特定的方法,可以在你的应用程序的顶层使用,如果你需要的话,也可以作为 React模型 之外的特殊操作DOM的接口。 但大多数组件应该不需要使用这个模块。
    import ReactDom from 'react-dom';class App extends Component {render() {return <h1> Hello, world! </h1>}
    }// 渲染一个 React 元素到由 container 提供的 DOM 中,并且返回组件的一个 引用
    ReactDom.render(<App />,document.getElementById('root')
    )
    
  • package.json

    "start": "webpack-dev-server --inline --hot --open --port 8090 --mode development"
    

3. create-react-app

React脚手架 (Facebook官方出品)

npm install -g create-react-app 全局全装脚手架工具

create-react-app my-testproject 生成项目模板,其中my-testproject是项目名称,随意定义

cd my-testproject

npm start or yarn start

4. React版hello world

import React, { Component } from 'react';
import ReactDOM from 'react-dom';// 强调组件的创建方式,里面模板jsx创建的方式
class App extends Component {render() {return (<div><span>Hello, World.</span></div>);}
}ReactDOM.render(<App />, document.getElementById('root'));

5. JSX

5.1 什么是JSX/基本使用

JSX 是一种类似 XML 语法的语法扩展,可以被编译成“合法”的 JavaScript 代码。
那么如何用一段“合法”的 JavaScript 代码来描述这段 JSX 呢?那么让我们把一个 DOM 抽象成一个 JavaScript 对象,用对象的属性来描述这个 DOM 的结构,比如标签名、属性、子 DOM 等等。我们尝试用 JavaScript 对象来描述第一个例子中的 JSX。

{type: 'div',props: null,children: [{type: 'span',text: 'Hello, World.'}]
}

5.2 JSX 嵌套元素

注意:只能包含一个根节点

ReactDOM.render(// 模板只能包含一个根节点<div><h1>hello</h1><h2>jack</h2></div>,document.getElementById('root')
);

5.3 JSX 表达式

import React from 'react';
import ReactDom from 'react-dom';function formatName(user) {return user.firstName + ' ' + user.lastName;
}const user = {firstName: '张',lastName: '三'
};let num = 2ReactDom.render(// 表达式只能放在大括号中,注意和vue一样,同样不支持if else语句// 如果里面放对象,会报错:Objects are not valid as a React child<div><p>{ 1 + 1}</p><p>{ '李' + '宁' }</p><h1>hello {formatName(user)}</h1><p>{num % 2 === 0 ? '偶数' : '奇数'}</p></div>,document.getElementById('root')
)

5.4 JSX注释

    {// 这里是单行注释}{/*这里是多行注释这里是多行注释这里是多行注释这里是多行注释*/}

5.5 JSX属性

因为 JSX 的特性更接近 JavaScript 而不是 HTML , 所以 React DOM 使用 camelCase 小驼峰命名 来定义属性的名称,而不是使用 HTML 的属性名称

  1. html的class属性改为className
  2. html中label标签的for属性改为htmlFor
  3. 标签中的自定义属性使用**data-**开头 查看详情
import React from 'react';
import ReactDom from 'react-dom';const user = {avatar: './avatar.jpg'
}ReactDom.render(<div><h1>hello</h1><h2>jack</h2><img src={user.avatar}></img>{// class ==> className// for ==> htmlFor// 自定义属性需要以data-开头,规范}<p className="myStyle">类名</p><label htmlFor="male">Male</label><input type="radio" name="sex" id="male" /><p data-mytest="123">自定义属性</p></div>,document.getElementById('root')
)

5.6 Boolean属性

一些常用的属性例如 disabled checked 等,我们可以省略值来表示为 true

<input disabled />就等同于 <input disabled={true} />

5.7 使用ES6 拓展运算符

我们可以直接将一个 JavaScript 对象里的属性作为元素的属性合并,使用 ES6 的 rest 特性。

import React, { Component } from 'react';const props = {className: 'app',id: 'app','data-root': 'root'
}
class App extends Component {render() {return (<div {...props}>hello world</div>);}
}export default App;

渲染的 DOM :<div class="app" id="app" data-root="root">hello</div>

5.8 JSX行内样式

import React from 'react';
import ReactDom from 'react-dom';let myStyle = {// React会自动在数值后面加上px// 非数值的属性值都要加上引号// 连字符的属性改为驼峰命名fontSize: 100,color: '#FF0000'
}ReactDom.render(<div><h1>hello</h1><h2 style={myStyle}>jack</h2></div>,document.getElementById('root')
)

5.9 React.createElement()

那么我们第一个例子中的代码,在编译后就会变成这样一段“合法”的 JavaScript 代码。

// React.createElement 方法的作用,就是使用JS创建内存中的虚拟DOM,生成一些普通的对象
// 这个方法接收至少三个参数:
//   第一个参数: 指定要创建的元素标签类型[string]
//   第二个参数: 指定要创建的元素身上的属性[对象/null]
//   第三个参数: 指定当前创建的元素的子元素
class App extends Component {render() {return (React.createElement('div',null,React.createElement('span',null,'Hello, World.')));}
}

createElement 这个函数会将入参进行处理,最终返回一个以 JavaScript 对象为基础的结构描述我们最终想要得到的 DOM 对象。最终,通过 ReactDOM.render 方法,将所得到的 JavaScript 对象转换成真正的 DOM。

到此,我们知道了,我们所写的 JSX,其实最终都会被编译成为 JavaScript 对象,正是因为这层的抽象,所以使跨平台成为了可能,对于所有拥有 JavaScript 运行环境的平台,我们都可以执行它。
此外,因为将它抽象成了 JavaScript 对象,所以我们也可以更方便地进行 diff/patch(React中的比对算法,用于比对后更新DOM结构)。而不是当数据产生变化的时候,我们直接去比对处理 DOM,这也很大程度上优化了它的性能。

6. 组件的创建

6.1 组件-函数创建组件

  • 组件名称必须以大写字母开头,用于区分 DOM 元素与组件元素
  • 标签必须闭合(但标签双标签都可以)
  • jsx必须由一个标签包裹
import React, { Component } from 'react'const Hello = () => {return <div>hello world</div>
}export class App extends Component {render() {return (<div><Hello></Hello></div>)}
}export default App

6.2 组件-类创建组件

6.2.1 es6 类的学习

function Father(firstName) {this.firstName = firstName
}Father.prototype.getFirstName = function () {console.log(this.firstName);
}Father.sayHello = function () {console.log('我是father');
}function Son(firstName) {this.firstName = firstName;
}let father = new Father('李');
father.getFirstName();
Father.sayHello();Son.prototype = father;
let son = new Son('李2');
son.getFirstName();
class Father {// 构造函数,通过new命令生成对象实例时,自动调用该方法。constructor(firstName) {this.firstName = firstName}getFirstName() {console.log(this.firstName);}// static 关键字定义静态方法,不能被实例继承(但可以被类继承),只能通过类来调用// 静态方法中的this指向的是类,而不是实例static sayHello() {console.log('我是father');}
}class Son extends Father {constructor(firstName) {super(firstName);console.log('son');}
}let father = new Father('王');
father.getFirstName();
Father.sayHello();let son = new Son('王2');
son.getFirstName();

6.2.2 React.Component 类

import React, { Component } from 'react'class Hello extends Component {render() {return (<div>hello world</div>)}
}export class App extends Component {render() {return (<div><Hello></Hello></div>)}
}export default App

组件类的组成必须有 render 方法,并且在 render 方法内返回 JSX。

渲染组件内容时,经常会使用到条件渲染。if/else 三元运算符 短路运算符

6.3 组件树

组件和组件可以结合在一起,组件的内部又可以使用其他组件,这样组合嵌套后,就构成了一个所谓的组件树。

我们希望设计组件时能保证组件的专一性即:一个组件只专注做一件事

一个复杂的功能如果可以拆分成等多个小功能,那就可以将每个小功能封装成一个组件,然后通过组件的嵌套/组合实现复杂功能。

当然也不是拆分的越细、颗粒度越小越好,能控制在一个可控的范围内即可。

6.4 组件中的事件处理

  • React 事件使用驼峰命名

  • 通过 JSX , 你传递一个函数作为事件处理程序

    <button onClick={activateLasers}>Activate Lasers
    </button>
    
  • 绑定this(类方法中没有绑定this)

    1. 在构造函数中绑定(建议),这样的好处是仅绑定一次,不存在无用的重复绑定. ⭐

      class Hello extends Component {constructor(props) {super(props)this.sayHello = this.sayHello.bind(this)}sayHello() {console.log('hello world!');}render() {return (<div><button onClick={this.sayHello}>问好</button></div>)}
      }
      
    2. 使用箭头函数(属性初始化语法),箭头函数没有 this,所以需要继承定义箭头函数所在的作用域的 this

      class Hello extends Component {sayHello = () => {console.log('hello world!');}render() {return (<div>{/*  // 不推荐 每次调用 onClick 事件时都会生成一个新的函数 */}<button onClick={this.sayHello}>问好</button></div>)}
      }
      
    3. bind.bind 方法返回一个函数,函数的 this 指向 bind 的第一个参数。缺点是每次调用 onClick 事件时都会生成一个新的函数。

        class Hello extends Component {sayHello() {console.log('hello world!');}render() {return (<div><button onClick={this.sayHello.bind(this)}>问好</button></div>)}
      }
      

6.5 state

作用:用来给组件提供组件内部使用的数据
注意:

  • 只有通过class创建的组件才具有状态
  • 状态是私有的,完全由组件来控制
import React, {Component} from 'react';
import ReactDom from 'react-dom';class Hello extends Component {constructor(props) {super(props)this.state = {msg: 'hello world'}this.changeMsg = this.changeMsg.bind(this)}changeMsg() {this.setState({msg: 'hello itcast'})}render() {return (<div><p>{this.state.msg}</p><button onClick={this.changeMsg}>点击改变msg</button></div>)}
}export default class App extends Component {render () {return (<div><Hello/></div>)}
}
  • 唯一可以分配 this.state 的地方是构造函数。

  • 不要直接修改 state(状态), 类似于这样this.state.comment = 'Hello',用 setState() 代替:this.setState({comment: 'Hello'})

  • this.setState()方法更新是异步的,此时需要给它传递第二个参数,即一个回调来在更新之后执行,即this.setState({}, callback)

    import React, {Component} from 'react';class Hello extends Component {constructor(props) {super(props)this.state = {msg: 'hello world'}this.changeMsg = this.changeMsg.bind(this)}changeMsg() {console.log(this.state);this.setState({msg: 'hello itcast'})// this.setState()是异步的,所以这里的console.log打印出来并不是想要的。出现这种情况是因为 react 并不会马上修改 state,而是将这个对象放入队列,且一个生命周期内所有的 setState 方法都会合并操作。因此你并不需要担心写多个 setState 带来性能问题。console.log(this.state)}render() {return (<div><p>{this.state.msg}</p><button onClick={this.changeMsg}>点击改变msg</button></div>)}
    }export default class App extends Component {render () {return (<div><Hello/></div>)}
    }
  • this.setState()接收函数作为参数

    this.setState({msg: this.state.msg + '1111'
    })
    this.setState({msg: this.state.msg + '1111'
    })// 解决方式,换成一个函数作为参数
    this.setState((prevState) => {console.log(prevState);return {msg: prevState.msg + '1111'}
    })
  • 组件的 state(状态) 可以向下传递,作为其子组件的 props(属性),通常称为一个“从上到下”,或者“单向”的数据流

6.6 props

作用:props给组件传递数据,一般用在父子组件之间

基础用法

函数式组件和类组件中属性的传递

// 函数式组件
import React, {Component} from 'react';const Hello = (props) => {return (<div><p>{props.name}: {props.age}</p></div>)
}const obj = {name: 'rose',age: 19
}
export default class App extends Component {render () {return (<div><Hello name="jack" age={18}/><Hello {...obj}/></div>)}
}
// 类组件
import React, {Component} from 'react';class Hello extends Component {render() {return (<div><p>{this.props.name}: {this.props.age}</p></div>)}
}const obj = {name: 'rose',age: 19
}
export default class App extends Component {render () {return (<div><Hello name="jack" age={18}/><Hello {...obj}/></div>)}
}

props.children

props.children 是 React 内置的 prop,它代表组件的子组件的集合.

class Hello extends Component {render() {return (<div>hello {this.props.children}</div>)}
}// Hello组件innerHTML部分的内容会被传入children
<Hello>world</Hello>

props不可变

注意:props是只读的,无法给props添加或修改属性。如this.props.name = 'jack'是不可行的。当然修改还是需要的,不可以在组件内修改 props,我们可以更改传入的 props,这样组件接受到了新的 props 后就可以主动渲染。

默认属性

给类(或者函数)绑定一个defaultProps属性。例如:

Hello.defaultProps = {name: 'jack'
}// 或者class Hello extends Component {static defaultProps = {name: 'rose'}render() {return (<div>hello {this.props.name}</div>)}
}

属性校验

npm i prop-types -S

import PropTypes from 'prop-types'Hello.propTypes = {optionalArray: PropTypes.array,optionalBool: PropTypes.bool,optionalFunc: PropTypes.func,optionalNumber: PropTypes.number,optionalObject: PropTypes.object,optionalString: PropTypes.string,optionalSymbol: PropTypes.symbol,optionalAny: PropTypes.any,optionalRequired: PropTypes.any.isRequired,
}

props vs state

相同

  1. 二者都作为 React 内更新视图的依据,只有它们变化时,React 才会进行相应的更新。
  2. 二者都不可以通过直接赋值的方式更新。
  3. 二者都可以使用任意类型的值。
  4. 二者都可以设置默认值。

不同

  1. 更新方式不同:state 通过 setState 方法更新(只能在组件内部更新),props 则通过更新传入的值实现(组件内不可变)。
  2. state 只维护组件内部的状态,props 让外部维护组件的状态。

总结:尽量少用state,尽量多用props,这样既能提高组件的可复用性,又能降低维护成本。

6.7 函数式组件和类组件的区别

不同:类允许我们在其中添加本地状态(state)和生命周期钩子

相同:里面props是只读的,无法修改

重点:我们在开发的时候,凡是没有state的组件,就一定要使用函数式组件。为什么呢?因为使用函数的方式创建的组件更易于测试和数据的维护。也就是说,只要我们的组件没有state,我们就要使用函数式组件(也叫无状态组件)

7. 案例:评论列表

在这里插入图片描述

import React, { Component } from 'react'export default class Home extends Component{constructor(props) {super(props)this.state = {commentList: [{ user: '张三', content: '哈哈,沙发' },{ user: '张三2', content: '哈哈,板凳' },{ user: '张三3', content: '哈哈,凉席' },{ user: '张三4', content: '哈哈,砖头' },{ user: '张三5', content: '哈哈,楼下山炮' }]}}createComments = () => {return this.state.commentList.map((item, index) => {return (<div key={index}><h3>评论人:{item.user}</h3><div>评论内容:{item.content}</div></div>)})}render() {return (<div><h1>评论案例列表</h1><div>{this.createComments()}</div></div>)}
}

改造版本:

import React, { Component } from 'react'
const Comment = (props) => {return (<div><h3>评论人:{props.user}</h3><div>评论内容:{props.content}</div></div>)
}
const NumberList = (props) => {return props.list.map((item, index) => {return <Comment {...item} key={index}/>})
}
export default class Home extends Component{constructor(props) {super(props)this.state = {commentList: [{ user: '张三', content: '哈哈,沙发' },{ user: '张三2', content: '哈哈,板凳' },{ user: '张三3', content: '哈哈,凉席' },{ user: '张三4', content: '哈哈,砖头' },{ user: '张三5', content: '哈哈,楼下山炮' }]}}// createComments = () => {//   return this.state.commentList.map((item, index) => <Comment key={index} {...item}></Comment>)// }render() {return (<div><h1>评论案例列表</h1><div><NumberList list={this.state.commentList}></NumberList></div></div>)}
}

8. Virtual DOM

  • DOM

在这里插入图片描述

  • 浏览器渲染流程

在这里插入图片描述

  • 什么是Virtual DOM

    在React中,render执行的结果得到的并不是真正的DOM节点,结果仅仅是轻量级的JavaScript对象,我们称之为virtual DOM。类似于下面这种:

    {type: 'div',props: null,children: [{type: 'span',text: 'Hello, World.'}]
    }
    

    虚拟DOM是React的一大亮点,具有batching(批处理)和高效的Diff算法。这让我们可以无需担心性能问题而”毫无顾忌”的随时“刷新”整个页面,由虚拟 DOM来确保只对界面上真正变化的部分进行实际的DOM操作。在实际开发中基本无需关心虚拟DOM是如何运作的,但是理解其运行机制不仅有助于更好的理解React组件的生命周期,而且对于进一步优化 React程序也会有很大帮助

  • 虚拟DOM VS 原生DOM

    如果没有 Virtual DOM,简单来说就是直接重置 innerHTML。这样操作,在一个大型列表所有数据都变了的情况下,还算是合理,但是,当只有一行数据发生变化时,它也需要重置整个 innerHTML,这时候显然就造成了大量浪费。

    比较innerHTML 和Virtual DOM 的重绘过程如下:

    innerHTML: render html string + 重新创建所有 DOM 元素

    Virtual DOM: render Virtual DOM + diff + 必要的 DOM 更新

    DOM 完全不属于Javascript (也不在Javascript 引擎中存在).。Javascript 其实是一个非常独立的引擎,DOM其实是浏览器引出的一组让Javascript操作HTML文档的API而已。在即时编译的时代,调用DOM的开销是很大的。而Virtual DOM的执行完全都在Javascript 引擎中,完全不会有这个开销。

    React.js 相对于直接操作原生DOM有很大的性能优势, 很大程度上都要归功于virtual DOM的batching 和diff。batching把所有的DOM操作搜集起来,一次性提交给真实的DOM。diff算法时间复杂度也从标准的的Diff算法的O(n^3)降到了O(n)。

  • 关于React 虚拟DOM的误解

    React 从来没有说过 “React 比原生操作 DOM 快”。React给我们的保证是,在不需要手动优化的情况下,它依然可以给我们提供过得去的性能。
    框架的意义在于为你掩盖底层的 DOM 操作,让你用更声明式的方式来描述你的目的,从而让你的代码更容易维护。没有任何框架可以比纯手动的优化 DOM 操作更快,因为框架的 DOM 操作层需要应对任何上层 API 可能产生的操作。
    React掩盖了底层的 DOM 操作,可以用更声明式的方式来描述我们目的,从而让代码更容易维护。

9. Diff 算法

虚拟dom什么时候会被比对?setState()

同层级比较下面的节点不是不会被复用了吗?

Diff 算法会帮助我们计算出 Virtual DOM 中真正变化的部分,并只针对该部分进行实际 DOM 操作,而非重新渲染整个页面,从而保证了每次操作更新后页面的高效渲染,因此 Virtual DOM 与 diff 是保证 React 性能口碑的幕后推手。

  • 传统Diff算法

    传统 diff 算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3),其中 n 是树中节点的总数。O(n^3) 到底有多可怕,这意味着如果要展示1000个节点,就要依次执行上十亿次的比较。这种指数型的性能消耗对于前端渲染场景来说代价太高了!现今的 CPU 每秒钟能执行大约30亿条指令,即便是最高效的实现,也不可能在一秒内计算出差异情况。

  • React Diff 算法

    参考

    • Web UI 中 DOM 节点跨层级的移动操作特别少,可以忽略不计
    • 不同类型的两个元素将产生不同的树(根元素不同结构树一定不同)
    • 开发人员可以在不同渲染之间使用key属性来表示哪些子元素是稳定的
    1. tree diff

在这里插入图片描述

以上两棵树只会对**同一层**次的节点进行比较

在这里插入图片描述

如果两棵树的根元素类型不同,React会销毁旧树,创建新树对于类型相同的React DOM 元素,React会对比两者的属性是否相同,只更新不同的属性React diff 的执行情况:delete A -> create A -> create B -> create C  (React 官方建议不要进行 DOM 节点跨层级的操作)
  1. component diff

    React 是基于组件构建应用的,对于组件间的比较所采取的策略也是简洁高效。

    • 如果是同一类型的组件,按照原策略继续比较 virtual DOM tree。
    • 如果不是,则将该组件判断为 dirty component,从而替换整个组件下的所有子节点。
    • 对于同一类型的组件,有可能其 Virtual DOM 没有任何变化,如果能够确切的知道这点那可以节省大量的 diff 运算时间,因此 React 允许用户通过 shouldComponentUpdate() 来判断该组件是否需要进行 diff。

在这里插入图片描述

 D和G为不同类型的组件,会直接删除组件D,重新创建组件G
  1. element diff

在这里插入图片描述

 如果每个节点都没有唯一的标识,React无法识别每一个节点,那么更新过程会很低效,即,将C更新成F,D更新成C,E更新成D,最后再插入一个E节点,如下图所示。可以看到,React会逐个对节点进行更新,转换到目标节点。而最后插入新的节点E,涉及到的DOM操作非常多。

在这里插入图片描述

 如果给每个节点唯一的标识(key),那么React能够找到正确的位置去插入新的节点

在这里插入图片描述

diff演示

10. 生命周期

在这里插入图片描述

组件的生命周期可分成三个状态:

constructor(props) 初始化state和方法,组件挂载前被调用

render()它是一个仅仅用于渲染的纯函数,返回值完全取决于this.state和this.props,不能在函数中任何修改props、state、拉取数据等具有副作用的操作。render函数返回的是JSX的对象

static getDerivedStateFromProps(nextProps, prevState) 组件每次被rerender的时候,包括在组件构建之后(render之前最后执行),每次获取新的props或state之后。每次接收新的props之后都会返回一个对象作为新的state,返回null则说明不需要更新state。 因为是静态函数,所以无法访问组件实例。它可以根据props发请求获取新的数据重新设置state

shouldComponentUpdate(nextProps, nextState) 判断组件是否需要被更新,返回bool值,true表示要更新,false表示不更新,使用得当将大大提高React组件的性能,避免不需要的渲染。

getSnapshotBeforeUpdate(prevProps, prevState)update发生的时候,在render之后,在组件dom渲染之前。可以在update之前获取dom节点的属性。返回一个值,作为componentDidUpdate的第三个参数。

componentDidMount() 组件挂载之后立即调用,适合在里面发送网络请求

componentDidUpdate(prevProps, prevState, snapshot)组件更新后被立即调用。里面也能够调用setState(),但是需要注意加上条件以免导致死循环

componentWillUnmount()组件被卸载和销毁前调用

11. 组件通讯

11.1 父组件向子组件通讯

通讯是单向的,数据必须是由一方传到另一方。在 React 中,父组件可以向子组件通过传 props 的方式,向子组件进行通讯。

class ChildOne extends Component{render() {return <p>{this.props.msg}</p>}
}class Parent extends Component {constructor() {super()this.state = {val: 'hello world'}}render() {return (<div><ChildOne msg={this.state.val}/></div>);}
}

11.2 子组件向父组件通讯

而子组件向父组件通讯,同样也需要父组件向子组件传递 props 进行通讯,只是父组件传递的,是作用域为父组件自身的函数,子组件调用该函数,将子组件想要传递的信息,作为参数,传递到父组件的作用域中。

class ChildOne extends Component{transferMsg = () => {this.props.getChildMsg('我报名了传智!')}render() {return <button onClick={this.transferMsg}>点击传值给父组件</button>}
}class Parent extends Component {constructor() {super()this.state = {msg: 'hello world'}}handleMsg = (msg) => {this.setState({msg})}render() {return (<div><div>儿子给我说:{this.state.msg}</div><ChildOne getChildMsg={this.handleMsg} /></div>);}
}

11.3 非父子组件间通讯

对于没有直接关联关系的两个节点,他们唯一的关联点,就是拥有相同的父组件。参考之前介绍的两种关系的通讯方式,如果我们要ChildOne和ChildTwo进行通讯,我们可以先通过 ChildOne 向 Parent 组件进行通讯,再由 Parent 向 ChildTwo 组件进行通讯。注意:这个方法有一个问题,由于 Parent 的 state 发生变化,会触发 Parent 及从属于 Parent 的子组件的生命周期。

class ChildOne extends Component{transferMsg = () => {this.props.getChildMsg('我报名了传智!')}render() {return <button onClick={this.transferMsg}>点击传值给父组件</button>}
}class ChildTwo extends Component {render() {return <p>我兄弟说:{this.props.msg}</p>}
}class ChildThree extends Component {componentDidUpdate() {console.log('child 3 updated');}render() {return <p>child 3,我和兄弟1和兄弟2之间的通信没有任何关系</p>}
}class Parent extends Component {constructor() {super()this.state = {msg: 'hello world'}}handleMsg = (msg) => {this.setState({msg})}render() {return (<div><div>儿子给我说:{this.state.msg}</div><ChildOne getChildMsg={this.handleMsg} /><ChildTwo msg={this.state.msg} /><ChildThree/></div>);}
}

12. 受控表单和非受控表单

受控表单:设定了value值的input表单就是一个受控表单,此时的表单是不受你控制的,受react控制

import React, {Component} from 'react';class App extends Component {render() {return (<div>// 这个value值无法改变,要想改变,只能通过onChange事件<input type="text" value="Hello!"/></div>);}
}export default App;
import React, {Component} from 'react';class App extends Component {constructor(props, context) {super(props, context);this.state = {inputVal: 'hello'};this.handleChange = this.handleChange.bind(this);};handleChange(event) {this.setState({inputVal: event.target.value});}render() {return (<div><input type="text" value={this.state.inputVal} onChange={this.handleChange}  /></div>);}
}export default App;

不受控表单:value没有值的input是一个不受控组件。用户的任何输入都会反映到输入框中。默认值设置:<input type="checkbox"><input type="radio"> 支持 defaultChecked,而 <select><textarea> 支持 defaultValue(它仅会被渲染一次,在后续的渲染时并不起作用 )。要获取非受控表单的值,需要借助于ref

class NameForm extends React.Component {constructor(props) {super(props);this.handleSubmit = this.handleSubmit.bind(this);}handleSubmit(event) {alert('A name was submitted: ' + this.input.value);event.preventDefault();}render() {return (<form onSubmit={this.handleSubmit}><label>Name:<input type="text" ref={(input) => this.input = input} /></label><input type="submit" value="Submit" /></form>);}
}
特征不受控制表单(不推荐使用)受控表单
一次性检索(例如表单提交)yesyes
及时验证noyes
有条件的禁用提交按钮noyes
执行输入格式noyes
一个数据的几个输入noyes
动态输入noyes

13. ref

使用refs的场景

  • 处理focus、文本选择或者媒体播放
  • 触发强制动画
  • 集成第三方DOM库

注意:尽量少用ref

Dom元素上使用ref

通过回调函数来实现对dom的引用

定义:ref={(input) => { this.textInput = input; }}

使用:this.textInput.focus()

14. 案例:TODO

在这里插入图片描述

App.js

class App extends Component {constructor(props) {super(props)this.state = {list: [{id: 0, text: 'react'}]}}handleSubmit = (val) => {let id = this.state.list.length === 0 ? 0 : this.state.list[this.state.list.length - 1].id + 1this.setState({list: this.state.list.concat({ id: id, text: val })})}handleDel = (id) => {let idx = this.state.list.findIndex(item => item.id === id)let list = this.state.listlist.splice(idx, 1)this.setState({list})}render() {return (<div><Input onSubmitFn={this.handleSubmit}/>{this.state.list.map((item, index) => {return <List {...item} key={item.id} onDelFn={this.handleDel}/>})}</div>)}
}

Input.js

export default class Input extends Component {constructor(props) {super(props)this.state = {inputVal: ''}}getInputVal = (e) => {this.setState({inputVal: e.target.value})}transferVal = (e) => {let val = this.state.inputValif (e.keyCode === 13 && val.trim()) {this.props.onSubmitFn(val)this.setState({inputVal: ''})}}render() {return (<input type="text"value={this.state.inputVal}onChange={this.getInputVal}onKeyUp={this.transferVal}/>)}
}

List.js

export default class List extends Component {delTodo = (id) => {this.props.onDelFn(id)}render() {return (<li>{this.props.text}<span style={{ color: 'red', marginLeft: '40px' }} onClick={() => this.delTodo(this.props.id)}>X</span></li>)}
}

15. 样式

内联样式

局限:hover等伪类不能够使用

const styleComponent  = {header: {backgroundColor: "#333333",color: "#ffffff","paddingTop": "15px",paddingBottom: "15px"}
}// jsx中这样使用
<header style={styleComponent.header}></header>

从css文件引入

定义一个样式文件,直接引入(通过link标签),然后给相应的元素加上className。它是全局的,会有污染。

也可以直接通过import引入,然后给相应的元素加上className。

css模块化

create-react-app不支持,步骤如下:

npm run eject // 弹出webpack的配置

找到/config/webpack.config.js 中的第395行,在下面添加modules: true,表示开启css模块化

使用使用类似下面这样

import cssHeader from (./style.css)

<header className={cssHeader.header}></header>

css模块化优点

  • 所有样式都是local的,解决了命名冲突和全局污染问题
  • class名生成规则配置灵活,可以以此压缩class名
  • 只需引用组件的js就能搞定js和css
  • 依然是写css代码,没有什么学习成本

styled-jsx

vs-code插件安装vscode-styled-jsx

npm install customize-cra -D这个工具提供了一些方法,可以用于将我们自己添加的webpack规则合并到原有的webpack配置规则中

首先在项目根目录下面使用npm install --save styled-jsx安装styled-jsx,然后添加如下配置:

// config-overrides.js
const { override, addBabelPlugins } = require('customize-cra');
module.exports = override(...addBabelPlugins(["styled-jsx/babel"])
)

最后注意:npm i react-app-rewired -D,再修改package.json文件中的scripts如下:

  "scripts": {"start": "react-app-rewired start","build": "react-app-rewired build","test": "react-app-rewired test","eject": "react-scripts eject"},

16. React Router

React Router是 react 官方推荐的一款路由库。它遵循 react 万物皆组件的理念,声明式(你不需要知道它怎么做,而只需要告诉它怎么做)地控制路由跳转并渲染出指定的页面,而不需要去重载整个应用。

目前 React Router 已经更新至V4.x版本,本教程也主要围绕此版本来做相关介绍,其他版本可参考官方文档

React Router V4.x较之前版本做了较大的改动,其按单代码仓库模型来进行代码规划,打开它的 github 查看其 packages 目录,可以发现React Router分为以下几个独立的部分:

  • 核心部分 react-router
  • 绑定了 DOM 操作的 react-router-dom(常用于 web 应用)
  • 用在 React Native 上的 react-router-native(用于 native App)
  • 用于配置静态路由的 react-router-config

单代码仓库模型的好处就是,你只需要按照自己的需求,用 npm 安装这四个中的一个即可。本文大部分示例使用的都是 react-router-dom,它与 react-router 的区别是多了很多 DOM 类组件(如 等)

路由基本使用

BrowserRouter, Route, Link, Switch

下载 npm i react-router-dom -S

先看一个例子,将项目目录下./src/App.js修改为:

import React, { Component } from 'react';
import { BrowserRouter, Route, Switch, Link } from 'react-router-dom';const Home = () => (<div>我是主页</div>
)
const Content = () => (<div>我是内容</div>
)
const Log = () => (<div>我是日志</div>
)
const Nav = () => (<div><Link to='/home'>主页</Link><Link to='/content'>内容</Link><Link to='/log'>日志</Link></div>
)
class App extends Component {render() {return (<div className="App"><BrowserRouter><Switch> <Route component={Nav} path='/' exact /><Route component={Home} path='/home' /><Route component={Content} path='/content' /><Route component={Log} path='/log' /></Switch></BrowserRouter></div>);}
}export default App;
  • 路由分两种模式:
  1. HashRouter 类似于http://localhost:8080/#/abc/def 是基于 window.location.hash 和 hashchange 事件所封装的路由组件。其特点是兼容性较好。
  2. BrowserRouter 类似于 http://localhost:8080/abc/def 是基于 HTML5 history API 和 popstate 事件所封装的一个高阶组件

如果有服务器端的动态支持,建议使用 BrowserRouter,否则建议使用 HashRouter
原因在于,如果是单纯的静态文件,假如路径从 / 切换到 /a 后,此时刷新页面,页面将无法正常访问。

在使用时二者的替换方法很简单,我们在引入 react-router-dom 时,如以下:

import { BrowserRouter as Router } from 'react-router-dom'
  • 路由组件无法接受两个以上子元素

  • 只想匹配某个路由,加exact参数,表示要求路径与location.pathname必须完全匹配

  • 使用组件来包裹一组。会遍历自身的子元素(即路由)并对第一个匹配当前路径的元素进行渲染,后面的不会再渲染

渲染组件的方式

  • component属性: 通过传入组件渲染,渲染时会调用 React.createElement 来生成 React 元素,适用于大部分场景。
  • render函数: 通过传入对应的 render 函数渲染,render 函数需要返回一个 React 元素。
  • children函数: 与 render 类似,也是传入需要返回一个 React 元素的函数,区别是不管路径是否匹配,传入的 children 都会渲染。

注意:三种方法不要在同一个 组件内使用

三种渲染方法都会传入以下三个参数:

  • match: math对象里面存储了 与 URL 匹配的信息
    • params - (object) URL 动态匹配的参数,如 URL 为 “/user/:name”,你访问 “/user/chenxin”,params为:{ name: ‘chenxin’ }
    • isExact - (boolean) URL是否是严格匹配
    • path - (string) 用来创建嵌套的 的匹配字段
    • url - (string) 用来创建嵌套的 的匹配字段
  • location:location 为你展示了当前页面从哪儿来,到哪里去,以及现在是什么状态。它主要有以下几个属性:
    • key - (string) 随机字符串,作为 location 的id,HashHistory 没有这个属性
    • pathname - (string) 当前页面 URL
    • search - (string) 当前 URL 的字符串参数
    • hash - (string) 当前 URL 的 hash
    • state - (object) 你要传递的 state 参数
  • history: history对象实现了对会话历史的管理。
    • length - (number) 浏览历史堆栈中的数量
    • action - (string) 跳转到当前页面执行的动作(PUSH,REPLACE 或者 POP)
    • location - (object) 当前页面的 location 对象
    • location.pathname - (string) URL路径
    • location.search - (string) URL的字符参数
    • location.hash - (string) URL中的hash
    • location.state - (object) 例如在执行 push(path, state) 操作时,state会被记录到历史记录堆栈中。只适用于browser history 和 memory history
    • push(path, [state]) - (function) 在历史堆栈中加入新的条目
    • replace(path, [state]) - (function) 替换历史堆栈中当前的条目
    • go(n) - (function) 历史堆栈中的指针向前移动n,页面发生前进操作
    • goBack() - (function) 相当于go(-1),返回上一页
    • goForward() - (function) 相当于go(1),前进一页
    • block(prompt) - (function) 阻止跳转

Switch, Redirect

import {Redirect} from 'react-router-dom'<Redirect to="/404">// 编程式导航: `this.props.history.push('/xxx/')`
class Product extends Component {componentDidMount() {console.log(this.props);}jumpTo = () => {this.props.history.push(`${this.props.match.url}/buy`)}render() {return (<div>这里显示商品编号 {this.props.match.params.id} <button onClick={this.jumpTo}>选购</button><Route path="/product/:id/buy" render={() => <div>我们这里有XXXXXXXX</div>}></Route></div>)}
}class App extends Component {constructor(props) {super(props)this.state = {productList: [{id: 11, title: '水果'},{id: 22, title: '肉类'},{id: 33, title: '蔬菜'},]}}render() {return (<Router><div><ul><li><Link to="/">首页</Link></li><li><Link to="/product/11">蔬菜</Link></li><li><Link to="/product/22">水果</Link></li><li><Link to="/product/33">肉类</Link></li></ul><Switch><Route path="/" exact render={() => <div>首页</div>}></Route><Route path="/product/:id" render={(props) => <Product {...props}/>}></Route><Route path="/404" render={() => <div>404</div>}></Route><Redirect to="/404"></Redirect></Switch></div></Router>)}
}

17. Redux

安装:npm i -S redux

Redux基本概念

Redux 是 JavaScript (不是React,其他的像Angular也可以使用,甚至单纯的JavaScript也阔以使用,和react没有半毛钱关系)状态容器,提供可预测化的状态管理。
应用中所有的 state 都以一个对象树的形式储存在一个单一的 store 中。 惟一改变 state 的办法是触发 action,一个描述发生什么的对象。 为了描述 action 如何改变 state 树,你需要编写 reducers。
在这里插入图片描述

Redux核心内容

  • Store
    store是Redux的实例对象,专门用来存放应用的状态,它里面保存有应用的state,action,reducer。注意:应用程序只能有唯一一个store
    store有以下职责:

    • 维持应用的 state;
    • 提供 getState() 方法获取 state;
    • 提供 dispatch(action) 方法更新 state;
    • 通过 subscribe(listener) 注册监听器;
    • 通过 subscribe(listener) 返回的函数注销监听器。
    import { createStore } from 'redux'const store = createStore(userReducer);// store.subscribe()方法可以监听store的更新
    store.subscribe(() => {console.log("Store updated!", store.getState());
    });
    
  • State

    State 是一个普通对象,用来保存应用的数据。例如:

    const initialState = {result: 1,lastValues: [],username: "Max"
    };
    

    state对象中的数据不能随意修改,要想更改state中的数据,需要用到发起一个action,这个action是一个对象,描述了你要干什么事情

  • Action

    • action

      Action 就是一个普通 JavaScript 对象,用来描述发生了什么事情。我们约定,action 内必须使用一个字符串类型的 type 字段来表示将要执行的动作,type 会被定义成字符串常量。

      {type: "SET_AGE",payload: 30
      }// 提交一个action
      store.dispatch({type: "SET_AGE",payload: 30
      });

      但它并没有去修改state,为了去做action描述的事情,我们需要用reducer将action和state联系起来。

    • action创建函数

      每次手动提交一个action会很麻烦,所以定义一个函数来生成action对象,这个函数就叫Action Creator.例如:

      function addTodo(text) {return {type: ADD_TODO,payload: text}
      }
      
  • Reducer

    reducer是一些纯函数,它接收两个参数:当前state和action,返回值是一个新的state。
    注意:

    1. 不要修改state,而是返回一个新的对象
    2. 在 default 情况下返回旧的 state
    
      const userReducer = (state = {name: "Max",age: 27}, action) => {switch (action.type) {case "SET_NAME":state = {...state,name: action.payload};break;case "SET_AGE":state = {...state,age: action.payload};break;}return state;};
    

React 和 Redux连接

npm i react-redux -S

  1. 如何将react 和redux进行连接,也就是如何让react应用程序拥有redux中的state
import {Provider} from 'react-redux'
render(<Provider store={store}><App /></Provider>,document.getElementById('app')
)
  1. 其他组件如何调用redux中的state或者触发reducer函数呢
import {connect} from "react-redux";// 使用 connect() 前,需要先定义 mapStateToProps 这个函数来指定如何把当前 Redux store state 映射到展示组件的 props 中
const mapStateToProps = (state) => {return {// state.userReducer这个userReducer,必须和上面const store = createStore(combineReducers({userReducer}));中的userReducer名字对应user: state.userReducer};
};// 除了读取 state,容器组件还能分发 action。类似的方式,可以定义 mapDispatchToProps() 方法接收 dispatch() 方法并返回期望注入到展示组件的 props 中的回调方法
const mapDispatchToProps = (dispatch) => {return {setName: (name) => {dispatch({type: "SET_NAME",payload: name});}};
};export default connect(mapStateToProps, mapDispatchToProps)(App);// 接下来,可以通过this.props.user.name获取state中的name值
// 通过this.props.etName('xxx')触发一个reducer

redux-logger

安装:npm i --save redux-logger

import { applyMiddleware, createStore } from 'redux';
import logger from 'redux-logger'
const store = createStore(reducer,applyMiddleware(logger))

拆分Reducer

import { combineReducers } from 'redux'
// 创建一个Redux实例,让它去管理应用中的state
// combineReducers()函数会将多个不同的reducer合并成一个大的reducer函数
const store = createStore(combineReducers({userReducer, otherReducer}));

redux-thunk

当处理异步请求时,比如在action中像下面这样写,就会报错,此时需要使用redux-thunk中间件

export function addNum (count) {return dispatch => {setTimeout(() => {dispatch({type: 'ADD_NUM',payload: count})}, 1000)}
}

安装:npm i --save redux-thunk

import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';const store = createStore(rootReducer,applyMiddleware(thunk));
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. 郑州三甲医院割高中毕业割连云港做双眼皮手术后要涂药吗

    ...

    2024/4/21 7:10:32
  2. react 学习

    react_learning 1.state负责存储组件里的数据 / JSX中使用JS表达式需要使用{}包裹表达式 “value{this.state.inputValue}“ / 事件绑定时需要用bind(this)对函数的作用域进行变更 ”onChange{this.handleInputChange.bind(this)} ”ES6语法 / 改变数据项中的内容应使用setStat…...

    2024/4/21 7:10:30
  3. angular1与react生命周期对比

    能否理解一个组件的生命周期是非常重要的&#xff0c;甚至可以说是最重要的内容&#xff0c;他是一切进阶的基石&#xff0c;下面我们就从angular1的生命周期开始吧。 angular1生命周期 angular1的生命周期在1.5之前是很晦涩的&#xff0c;controller、compile、preLink、postL…...

    2024/4/21 7:10:29
  4. 长沙割双眼皮效果美莱

    ...

    2024/4/21 7:10:29
  5. 西安美好整形做上海割双眼皮上华美

    ...

    2024/4/20 18:54:49
  6. 美联臣双眼皮多少钱

    ...

    2024/4/29 9:18:07
  7. 北京美联臣伊美尔紫竹桥北京做双眼皮手术费用

    ...

    2024/4/20 18:54:46
  8. 谁家做双眼皮好

    ...

    2024/4/27 5:58:26
  9. 解决Spring Cloud Zuul过滤器getParameter参数为空

    一、背景最近有个项目,前端调后端是di调用的网关服务,网关做数据校验,在调用request.getParameter的时候,获取的参数为空。后来查明原因是该接口是用于上传文件,content-type为form-data,这时用request.getParameter是获取不到请求参数的,需要做一层转换。二、原理首先要…...

    2024/4/21 7:10:28
  10. 做完双眼皮红薯吗

    ...

    2024/4/21 7:10:26
  11. 做了双眼皮可以吃鸡蛋吗

    ...

    2024/4/21 7:10:25
  12. 榆树陈伟做埋线双眼皮眼头处太紧

    ...

    2024/4/21 7:10:25
  13. 双眼皮有几层

    ...

    2024/4/21 7:10:24
  14. AngularJs中,如何在数据加载完成后,执行Js脚本

    1、自定义服务app.directive(onFinishRenderFilters, function ($timeout) {return {restrict: A,link: function (scope, element, attr) {if (scope.$last true) {$timeout(function () {scope.$emit(ngRepeatFinished);});}}};});2、html 使用ng-repeat循环渲染数据 然后在…...

    2024/4/21 7:10:23
  15. 荆州商丘埋线双眼皮六天了怎么没见消肿啊

    ...

    2024/4/21 7:10:21
  16. 解决request.getparameter中文乱码问题。

    本文是解决request.getparameter中文乱码问题。在Java web编程中经常遇到中文乱码问题。让人一直头疼。重点:全部统一为utf-8编码,兼容性好解决方案分以下几种:1.修改jsp页面头部信息:<%@ page language="java" import="java.util.*" pageEncoding=…...

    2024/4/21 7:10:22
  17. vue 判断页面加载完成_使用Vue做 SPA应用的思考及总结

    0x01前言最近清明节假期&#xff0c;有些时间做知识复盘&#xff0c;复盘也是对以往知识体系的修正。SPA应用也是我的知识复盘计划的组成部分。笔者亲身经历客户端和服务端技术变革&#xff0c;在以前没有所谓的前端开发概念&#xff0c;有些系统页面由网页设计师做好静态页面交…...

    2024/4/21 7:10:19
  18. 埋线双眼皮北京艺星有

    ...

    2024/4/21 7:10:18
  19. 双眼皮手术合肥艺星友

    ...

    2024/5/2 17:59:58
  20. 美莱做双眼皮怎么样

    ...

    2024/4/21 7:10:17

最新文章

  1. 探索“cd”命令:通往数字世界的奇幻之旅

    探索“cd”命令&#xff1a;通往数字世界的奇幻之旅 在数字世界的浩瀚星海中&#xff0c;有一个看似简单却功能强大的工具&#xff0c;它就像一把魔法钥匙&#xff0c;能够带你穿梭于各个角落&#xff0c;无论你是初学者还是资深玩家&#xff0c;它都是你不可或缺的伙伴——它…...

    2024/5/3 5:39:34
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. Oracle备份和还原的几种方式

    1、使用数据泵方式 exp demo/demoorcl buffer1024 filed&#xff1a;\back.dmp fully demo&#xff1a;用户名、密码 buffer: 缓存大小 file: 具体的备份文件地址 full: 是否导出全部文件 ignore: 忽略错误&#xff0c;如果表已经存在&#xff0c;则也是覆盖 exp demo/de…...

    2024/4/30 4:18:57
  4. OpenHarmony实战:Combo解决方案之ASR芯片移植案例

    本方案基于 OpenHarmony LiteOS-M 内核&#xff0c;使用 ASR582X 芯片的 DEV.WIFI.A 开发板进行开发移植。作为典型的 IOT Combo&#xff08;Wi-FiBLE&#xff09;解决方案&#xff0c;本文章介绍 ASR582X 的适配过程。 编译移植 目录规划 本方案的目录结构使用 Board 和 So…...

    2024/5/1 13:53:04
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/2 9:28:15
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/4/27 23:24:42
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57