2_React基础

文章目录

1、基本理解和使用
2、组件三大核心属性:state
3、组件三大核心属性:props
4、组件三大核心属性:ref
5、受控组件和非受控组件
6、高阶函数和函数柯里化
7、组件的生命周期
8、虚拟DOM与DOM Diffing算法

一、基本理解和使用

1、函数式组件(适用于简单组件<无状态组件>)

// 创建函数式组件
function MyComponent() {
    
    
  // 此处的this是undefined,因为babel编译后开启了严格模式
  console.log(this);
  return <h2>函数式组件(简单)</h2>
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

2、类式组件(适用于复杂组件<有状态组件>)

// 创建类式组件
class MyComponent extends React.Component {
    
    
  // 类中的构造器不是必须要写的,要对实例进行初始化的操作,如添加指定属性时才填写
  constructor(props) {
    
    
    // super代表的是父类的构造函数,但它内部的this指向的是当前子类MyComponent的实例对象
    //  super只能在子类的构造函数中调用,且有继承父类的话,必须调用
    // 子类未定义constructor时,super方法会被默认添加
    // ES5 的继承的实质是先创造子类的实例对象this,然后再将父类的方法添加到this上面(Parent.apply(this))
    // ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到this上面(所以必须先调用super方法),然后再用子类的构造函数修改this。
    super(props);
    this.name = props.name;
    this.age = props.age;
  }
  // 类中定义的方法,都是放在原型对象上,供实例去使用
  speak() {
    
    
    console.log(`我叫${
      
      this.name},年龄${
      
      this.age}`)
  }
  render() {
    
    
    // render是放在MyComponent的原型对象上,供实例使用
    // this指向的是MyComponent的实例对象
    console.log('render中的this', this);
    return <h2>类式组件(复杂组件)</h2>
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

3、渲染组件的基本流程

  1. 执行ReactDOM.render( < MyComponent /> , document.getElementById(‘root’))
  2. React解析组件标签,找到了MyComponent组件
  3. 发现组件是使用类定义的,随后new出该类的实例,并通过实例调用到原型上的render方法
  4. 将render返回的虚拟dom转换为真实dom
  5. 插入到指定页面的元素内部

4、注意事项

  • 组件名的首字母必须大写
  • 虚拟DOM元素只能有一个根元素
  • 虚拟DOM元素必须有结束标签

二、组件三大核心属性:state

1、定义

react把组件看成一个状态机,通过与用户的交互,实现不同的状态,然后渲染UI,让用户界面和数据保持一致。此时,就需要一个数据状态state来管理数据。

state是组件对象最重要的属性,值是key-value的对象。

在组件初始化时,可以在构造函数通过赋值给this.state初始化state,或直接在类中赋值给state初始化state。在初次调用render时,会用这个数据来渲染组件

2、更新state

  • 通过赋值方式直接修改state,可以修改state对象的值,但是不能触发react重新调用render()渲染
  • 需要触发react重新调用render()渲染,要通过setState去修改,该方法是异步方式
  • 对象式更新setState(stateChange, [callback])、函数式更新setState(updateFunction, [callback])
// 创建类式组件
class MyComponent extends React.Component {
    
    
  constructor(props) {
    
    
    super(props);
    this.name = props.name;
    this.age = props.age;
    // 初始化状态
    this.state = {
    
     
      value1: 1,
      value2: 1
    }
  }
  // 初始化状态的另一种写法
 //  state = { value1: 1, value2: 1 }
 // 对象式更新setState(stateChange, [callback])
 changeState1 = () => {
    
    
   // 读取状态
   const {
    
     value1 } = this.state;
   // 修改状态
   this.setState({
    
    
     value1: value1 + 1
   })
 }
 // 函数式更新setState(updateFunction, [callback])
 changeState2 = () => {
    
    
   // 修改状态
   this.setState((state, props) => {
    
    
     return {
    
    value2: state.value2 + 1}
   })
 }
 render() {
    
    
    return (
      <div>
        <h2 onClick={
    
    this.changeState1}>{
    
    this.state.value1}</h2>
        <h2 onClick={
    
    this.changeState2}>{
    
    this.state.value2}</h2>
      </div>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

注意:直接在class中声明并初始化变量,相当于给该实例添加了一个属性;而直接定义函数,该函数是在其原型对象上。

setData的坑

class MyComponent extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
    this.addNum = function () {
    
    
      this.setState({
    
    num:this.state.num+1})
      this.setState({
    
    num:this.state.num+1})
      this.setState({
    
    num:this.state.num+1})
    }.bind(this)
  }
  render() {
    
    
    return (
        <button onClick={
    
    this.addNum}>{
    
    this.state.num}</button>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

// 当addNum函数被触发后,num只加了1,并没有加了3
// setData是异步的,调用后,并不会立即映射新的值

解释:

  • 无论调用多少次setState,都不会立即执行更新。而是将要更新的state存入’_pendingStateQuene’,将要更新的组件存入’dirtyComponent’;
  • 当根组件didMount后,批处理机制更新为false。此时再取出’_pendingStateQuene’和’dirtyComponent’中的state和组件进行合并更新;

3、state的作用

主要用于组件保存、控制以及修改自己的属性。

属于组件的私有属性,只能组件内部自己访问,外部是访问不了的。

4、this的指向

class A {
    
    
  constructor(name) {
    
    
    this.name = name
  }
  getName() {
    
    
    console.log('this是', this)
  }
}
const a = new A('小一')
a.getName(); // 可以直接获取到this,指向的是实例a
const f = a.getName;
f(); // 获取不到this,为undefined,不是通过实例直接调用
// 这个就像是react标签绑定的回调函数,不是通过实例直接调用,直接去调函数是获取不到this

5、事件处理

  • 通过onXxx属性指定事件处理函数注意大小写
    1. React使用的是自定义(合成)事件,而不是使用原生的dom事件。为了兼容不同浏览器。如:onClick,其原生dom事件是onclick
    2. React中的事件是通过事件委托方式处理的(委托给组件的最外层元素)
  • 通过event.target得到发生事件的DOM元素对象

6、事件处理绑定的3种方式

在构造器中声明绑定(官方推荐)

class MyComponent extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
    this.addNum = this.addNum.bind(this)
  }
  addNum () {
    
    
    const {
    
     num } = this.state;
    console.log('this', this);
    this.setData({
    
    
      num: num + 1
    })
  }
  render() {
    
    
    return (
        <button onClick={
    
    this.addNum}>{
    
    this.state.num}</button>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

简单方法

扫描二维码关注公众号,回复: 14854428 查看本文章
class MyComponent extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
  }
  addNum () {
    
    
    const {
    
     num } = this.state;
    console.log('this', this);
    this.setData({
    
    
      num: num + 1
    })
  }
  render() {
    
    
    return (
        <button onClick={
    
    this.addNum.bind(this)}>{
    
    this.state.num}</button>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

箭头函数

class MyComponent extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
  }
  addNum () {
    
    
    const {
    
     num } = this.state;
    console.log('this', this);
    this.setData({
    
    
      num: num + 1
    })
  }
  render() {
    
    
    return (
        <button onClick={
    
    () => this.addNum()}>{
    
    this.state.num}</button>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

三、组件三大核心属性:props

1、定义

props是组件(包括函数组件和class组件)间的内置属性,每个组件对象都会有props属性;父组件传值给子组件,放到引用子组件的标签属性上,子组件再通过props接收。

所有React组件都必须像纯函数一样保护它们的props不被更改。

state和props主要的区别在于props是不可变的(组件内部不要修改props数据),而state可以根据与用户交互来改变。组件需要定义state来更新和修改数据,而子组件只能通过props来传递数据。

在React典型的数据流中,props传递是父子组件交互的唯一方式;通过传递一个新的props值来使子组件重新re-render,从而达到父子组件通信。

2、基本使用

  • 简单调用(只读)

    类组件在读取props的值,例如:this.props.value

    函数组件需接收参数props,然后读取props的值,例如:props.value

// 父组件A文件
import C from './C'
class A extends React.Component {
    
    
  render() {
    
    
    return (
      <div>
       <div>我是父组件A</div>
       <B value={
    
    'props测试B'} />
       <C value={
    
    'props测试C'} />
      </div>
    )
  }
}
// 函数组件B
function B(props) {
    
    
  return(
      <div>
        我是子组件B,
        接收到父组件的值:{
    
    props.value}
      </div>
    )
}

// 渲染组件到页面
ReactDOM.render(<A />, document.getElementById('root'));


// 子组件B文件
class C extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
  }
  render() {
    
    
    return (
      <div>
        我是子组件C,
        我的state属性num的值:{
    
    this.state.num}.
        接收到父组件的值:{
    
    this.props.value}
      </div>
    )
  }
}
  • 扩展属性 {…props}

    展开props属性的一种简洁写法

var props = {
    
    a: 1, b: 2};
<C {
    
    ...props} />
//等价于下面的写法
<C a=1 b=1 />
  • props.children指的是组件的子元素
<C>hello,world</C>
function C(props){
    
    
  // props.children指的就是 hello,world
  return <p>{
    
    props.children}</p>
}

3、对props进行限制和默认属性值

react中使用prop-types对props的值的类型和必要性进行校验

// 父组件A文件
import C from './C';
class A extends React.Component {
    
    
  render() {
    
    
    return (
      <div>
       <div>我是父组件A</div>
       <C value={
    
    'props测试C'} name={
    
    '小东'}  />
      </div>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<A />, document.getElementById('root'));


// 子组件B文件
import ProTypes from 'prop-types';
class C extends React.Component {
    
    
  constructor(props){
    
    
    super(props);
    this.state = {
    
     num: 1 }
  };
  static propTypes = {
    
    
    value: ProTypes.number.isRequired,
    name: ProTypes.string
    age: ProTypes.number
  };
  static detaultProps = {
    
    
    age: 18
  };
  render() {
    
    
    return (
      <div>
        我是子组件C,
        我的state属性num的值:{
    
    this.state.num}.
        接收到父组件的值:{
    
    this.props.value}
      </div>
    )
  }
}
// C.propTypes = {
    
    
//   value: ProTypes.number.isRequired,
//   name: ProTypes.string
//   age: ProTypes.number
// }
// C.detaultProps = {
    
    
//   age: 18
// };

四、组件三大核心属性:ref

1、含义

组件内的标签/组件可定义ref属性来标识自己,使用ref可以获取到绑定ref的标签节点/组件,可以去获取该节点上面需要使用的信息。

ref挂到组件上时,表示对组件真正实例的引用,其实就是ReactDOM.render()返回的组件实例

ref挂到HTML标签的dom元素上时,表示具体的dom元素节点

2、用法

ref有3种使用方式:字符串、回调函数、使用createRef

  • 字符串形式的ref(官方文档不建议用)

    绑定:使用 ref=“标识名称”

    获取对应的ref节点:this.refs.标识名称

    class MyComponent extends React.Component {
          
          
      showData () {
          
          
        const {
          
           input1 } = this.refs;
        alert(input1.value)
      }
      render() {
          
          
        return (
          <div>
            <input ref="input" type="text" placeholder="点击按钮提示数据" />
            <button onClick={
          
          () => this.showData()}>点击提示输入框内容</button>
          </div>
        )
      }
    }
    // 渲染组件到页面
    ReactDOM.render(<MyComponent />, document.getElementById('root'));
    
  • 回调函数(官方文档建议使用的方法)

    ref属性接受一个回调函数,在组件被加载或卸载时会立即执行。

    ref回调函数在组件被卸载时或原有的ref属性本身发生变化时,回调立即执行,参数会传入null,以确保内存不会泄漏。

    当给HTML元素添加ref属性时,ref回调接收了底层的dom元素作为参数。

    当给组件添加ref属性时,ref回调函数接收了该组件的实例,在componentDidMount或omponentDidMount这些生命周期前执行。

    class MyComponent extends React.Component {
          
          
      showData () {
          
          
        const {
          
           input1 } = this.refs;
        alert(input1.value)
      }
      render() {
          
          
        return (
          <div>
            <input ref={
          
          node => this.input1=node} type="text" placeholder="点击按钮提示数据" />
            <button onClick={
          
          () => this.showData()}>点击提示输入框内容</button>
          </div>
        )
      }
    }
    // 渲染组件到页面
    ReactDOM.render(<MyComponent />, document.getElementById('root'));
    
  • 使用createRef

    在React 16.3版本后,使用此方法来创建ref。将其赋值给一个变量,通过ref挂载在dom节点或组件上,该ref的current属性,将能拿到dom节点或组件的实例。

    class MyComponent extends React.Component {
          
          
      myRef = React.createRef();
      myRef1 = React.createRef();
      showData () {
          
          
        const {
          
           input1 } = this.myRef.current.value;
        alert(input1.value)
      }
      render() {
          
          
        return (
          <div>
            <input ref={
          
          this.myRef} type="text" placeholder="点击按钮提示数据" />
            <button onClick={
          
          () => this.showData()}>点击提示输入框内容</button>
            <input ref={
          
          this.myRef1} type="text" />
          </div>
        )
      }
    }
    // 渲染组件到页面
    ReactDOM.render(<MyComponent />, document.getElementById('root'));
    

3、总结

不要过度使用ref:当发生事件的元素正好是需要操作的元素本身时,ref可省略,使用event.target代替。

使用ref时,不用担心会导致内存泄露的问题,react会自动帮你管理好,在组件卸载时ref值也会被销毁。

不要在组件的render方法中访问ref引用,render方法只是返回一个虚拟dom,这时组件不一定挂载到dom中或者render返回的虚拟dom不一定会更新到dom中。

五、受控组件和非受控组件

1、受控组件

在HTML表单元素中,输入类DOM,随这用户的输入,通过onChange事件和setState,将数据维护到state中,在需要时,从state中获取数据的组件。

受控组件类似于VUE的双向绑定作用。

class MyComponent extends React.Component {
    
    
  state= {
    
    
    username: '',
    password: ''
  }
  saveUsername = (event) => {
    
    
    this.setState({
    
    username: event.target.value})
  }
  savePassword = (event) => {
    
    
    this.setState({
    
    password: event.target.value})
  }
  handleSubmit = (event)=>{
    
    
		// 通过state获取数据
		const {
    
    username,password} = this.state
		alert(`你输入的用户名是:${
      
      username},你输入的密码是:${
      
      password}`)
	}
  render() {
    
    
    return (
      <form onSubmit={
    
    this.handleSubmit}>
        用户名称:<input onChange={
    
    this.saveUsername} type="text" name="username" />
        密码:<input onChange={
    
    this.savePassword} type="text" name="password" />
      </form>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

2、非受控组件

与受控组件相对的,如果仅仅只是想获取某个表单元素的值,并不关心它是如何改变的,可以通过获取DOM节点的方式或取值,通常是ref,这种不依赖于state。

class MyComponent extends React.Component {
    
    
  handleSubmit = (event)=>{
    
    
		// 通过state获取数据
		const {
    
    username,password} = this
		alert(`你输入的用户名是:${
      
      username},你输入的密码是:${
      
      password}`)
	}
  render() {
    
    
    return (
      <form onSubmit={
    
    this.handleSubmit}>
        用户名称:<input ref={
    
    currentNode => this.username = currentNode} type="text" name="username" />
        密码:<input ref={
    
    currentNode => this.password = currentNode} type="text" name="password" />
      </form>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

六、高阶函数和函数柯里化

能够优化代码,让代码更加简洁

1、含义

高阶函数:一个函数接收的参数是一个函数,或调用返回值仍是一个函数,则称之为高阶函数。

常见的高阶函数:Promise、setTimeout、arr.map等等

函数的柯里化: 通过函数调用继续返回函数的方式,实现多次接受参数最后统一处理的函数编码形式。

// 函数的柯里化写法
function sum(a) {
    
    
  return (b) => {
    
    
    return (c) => {
    
    
      return a + b + c
    }
  }
}

const result = sum(1)(2)(3)

2、示例

class MyComponent extends React.Component {
    
    
  state= {
    
    
    username: '',
    password: ''
  }
  // 这个函数的返回值交给onChange作为回调
  // 这个saveFormData是个高阶函数
  // 同时也是函数的柯里化写法,两个函数接收到两个参数,dataType统一处理
  saveFormData = (dataType) => {
    
    
    return (event) => {
    
    
        this.setState({
    
    [dataType]: event.target.value})
    }
  }
  handleSubmit = (event)=>{
    
    
    event.preventDefault();// 阻止默认事件(阻止表单提交)
    // 通过state获取数据
	const {
    
    username,password} = this.state
	alert(`你输入的用户名是:${
      
      username},你输入的密码是:${
      
      password}`)
  }
  render() {
    
    
    return (
      <form onSubmit={
    
    this.handleSubmit}>
        用户名称:<input onChange={
    
    this.saveFormData('username')} value={
    
    this.state.username} type="text" name="username" />
        密码:<input onChange={
    
    this.saveFormData('password')} this.saveFormData('password') type="text" name="password" />
      </form>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

onChange={this.saveFormData}时,onChange事件的回调函数调用的是saveFormData函数;

onChange={this.saveFormData(‘username’)}时,其回调函数是执行了saveFormData函数所返回的函数;

这就说明,如果其dom上绑定事件所赋予的函数如果有加括号,在组件new实例化后调用render函数的初始化渲染的时候,就会默认去执行该函数,其回调函数就是该函数的返回值,无返回值就是underfined

3、不同柯里化的其他写法(较常用)

class MyComponent extends React.Component {
    
    
  state= {
    
    
    username: '',
    password: ''
  }
  // 这个函数的返回值交给onChange作为回调
  // 这个saveFormData是个高阶函数
  // 同时也是函数的柯里化写法,两个函数接收到两个参数,dataType统一处理
  saveFormData = (dataType, event) => {
    
    
     this.setState({
    
    [dataType]: event.target.value})
  }
  handleSubmit = (event)=>{
    
    
    event.preventDefault();// 阻止默认事件(阻止表单提交)
    // 通过state获取数据
	const {
    
    username,password} = this.state
	alert(`你输入的用户名是:${
      
      username},你输入的密码是:${
      
      password}`)
  }
  render() {
    
    
    return (
      <form onSubmit={
    
    this.handleSubmit}>
        用户名称:<input onChange={
    
    event => this.saveFormData('username', event)} value={
    
    this.state.username} type="text" name="username" />
        密码:<input onChange={
    
    event => this.saveFormData('password', event)} this.saveFormData('password') type="text" name="password" />
      </form>
    )
  }
}
// 渲染组件到页面
ReactDOM.render(<MyComponent />, document.getElementById('root'));

React调用onChange事件中绑定的函数才能接收到参数event,可以直接给onChange一个回调函数就好。让React去帮我们调用onChange事件,接收到event参数后,将dataType和event都传入函数saveFormData(),这样,就不需要使用函数的柯里化,只要直接将数据保存到state中即可。

七、组件的生命周期

1、生命周期(图)

生命周期函数指在某一个时刻组件会自动调用执行的函数

组件从创建到死亡它会经历一些特定的阶段;包含一系列的钩子函数(生命周期的回调函数),会在特定的时刻调用,做特定的工作。

根据广义描述,生命周期分为三个阶段:挂载、渲染、卸载。

生命周期分为以下四大阶段

组件初始化阶段(Initialization)

组件挂载阶段(Mount):组件第一次渲染到DOM树

组件更新阶段(update):组件state、props变化引发的重新渲染

组件卸载阶段(Unmount):组件从Dom树删除

在这里插入图片描述

2、初始化阶段

发生在constructor中的内容,在constructor中进行state、props的初始化,在这个阶段修改state,不会执行更新阶段的生命周期,可以直接对state赋值

3、挂载阶段

当组件实例被创建并插入DOM中时,其生命周期的顺序:

  • constructor: 挂载前会调用其构造函数,通常在这里初始化state对象、给自定义方法绑定this、外部传入参数props本地化。
  • componentWillMount: 预装载函数(较少用),组件经历了构造函数初始化数据后,还未渲染dom前。不能在这边更改state,是无效的。在此函数的操作,都可以提前到构造函数。
  • render: 仅用于渲染的纯函数,只返回要渲染的东西,不应该包含业务逻辑,不能省略的函数,一定要有返回值,返回null或false表示不渲染任何dom元素。返回值取决于state和props,因此不允许任何修改props、state、拉取数据等具有副作用的操作。
  • componentDidMount: 挂载成功函数,组件初次被渲染到dom树之后被调用;因render只是返回JSX对象并没有立即挂载到dom树上,该函数不会再render函数调用完之后立即调用。可以获取到dom节点并操作;可以在此调用ajax请求,返回的setState后组件重新渲染。

4、更新阶段

当组件挂载到DOM树上之后,props/state被修改会导致组件进行更新操作

  • componentWillReceiveProps(nextProps): 主要时提供对props发生改变的监听,如果需要在 props 发生改变后,通过对比nextProps和this.props,来重新setState, 不会二次渲染,而是直接合并 state。
  • shouldComponentUpdate(nextProps, nextState): 性能优化。返回bool值,true表示要更新,false表示不更新,使用得当将大大提高React组件的性能,避免不需要的渲染。因为react父组件的重新渲染会导致其所有子组件的重新渲染,这个时候其实我们是不需要所有子组件都跟着重新渲染的,因此需要在子组件的该生命周期中做判断。不可以 setState,会导致循环调用。
  • componentWillUpdate(nextProps,nextState): 预更新函数,shouldComponentUpdate返回true以后,组件进入重新渲染的流程,进入componentWillUpdate,这里同样可以拿到nextProps和nextState。
  • render:渲染函数
  • componentDidUpdate(prevProps,prevState): 更新完成函数,每次重新渲染后都会进入这个生命周期,这里可以拿到prevProps和prevState,即更新前的props和state

5、卸载阶段

  • componentWillUnmount: 在组件卸载及销毁之前直接调用。用于去除componentDidMount函数带来的副作用,例如清除计时器、删除componentDidMount中创造的非React元素。

6、新生命周期(图)

在这里插入图片描述

对比之前的生命周期可以发现,React 16 中去掉了componentWillMount、componentWillReceiveProps、componentWillUpdate。

官方给出的解释是 react 打算在17版本推出新的 Async Rendering,提出一种可被打断的生命周期,而可以被打断的阶段正是实际 dom 挂载之前的虚拟 dom 构建阶段,也就是要被去掉的三个生命周期。
本身这三个生命周期所表达的含义是没有问题的,但 react 官方认为我们(开发者)也许在这三个函数中编写了有副作用的代码,所以要替换掉这三个生命周期,因为这三个生命周期可能在一次 render 中被反复调用多次。

取代这三个生命周期的是以下两个新生命周期:

  • static getDerivedStateFromProps(nextProps, prevState): 字面意思是:从props中获取派生的state。代替componentWillReceiveProps(). 老版本中的componentWillReceiveProps()方法判断前后两个 props 是否相同,如果不同再将新的 props 更新到相应的 state 上去。这样做一来会破坏 state 数据的单一数据源,导致组件状态变得不可预测,另一方面也会增加组件的重绘次数。

    // before
    componentWillReceiveProps(nextProps) {
          
          
      if (nextProps.isLogin !== this.props.isLogin) {
          
          
        this.setState({
          
           
          isLogin: nextProps.isLogin,   
        });
      }
      if (nextProps.isLogin) {
          
          
        this.handleClose();
      }
    }
    
    // after
    static getDerivedStateFromProps(nextProps, prevState) {
          
          
      if (nextProps.isLogin !== prevState.isLogin) {
          
          
        return {
          
          
          isLogin: nextProps.isLogin,
        };
      }
      return null;
    }
    
    componentDidUpdate(prevProps, prevState) {
          
          
      if (!prevState.isLogin && this.props.isLogin) {
          
          
        this.handleClose();
      }
    }
    
  • getSnapshotBeforeUpdate(prevProps, prevState): 字面意思是:在组件更新之前获取快照。代替componentWillUpdate,在最近一次渲染输出(提交到 DOM 节点)之前调用。

    getSnapshotBeforeUpdate和componentWillUpdate这两者的区别在于:

    1. 在 React 开启异步渲染模式后,在 render 阶段读取到的 DOM 元素状态并不总是和 commit 阶段相同,这就导致在componentDidUpdate 中使用 componentWillUpdate 中读取到的 DOM 元素状态是不安全的,因为这时的值很有可能已经失效了。
    2. getSnapshotBeforeUpdate 会在最终的 render 之前被调用,也就是说再getSnapshotBeforeUpdate 中读取到的 DOM 元素状态是可以保证与 componentDidUpdate 中一致的。此生命周期返回的任何值都将作为参数传递给componentDidUpdate()

八、虚拟DOM与DOM Diffing算法

在这里插入图片描述

react/vue中的key有什么作用?为什么遍历列表时,最好不要用key?

1、虚拟dom中key的作用

  1. 简单的说,key是虚拟dom对象标识,在更新显示时key起着极其重要的作用。
  2. 详细的说,当状态中的key发生改变时,react会根基【新数据】生成【新虚拟dom】,随后react进行【新的虚拟dom】与【旧虚拟dom】的diff比较,比较规则如下:
    • 旧虚拟dom中找到与新虚拟dom相同的key:
      • 若虚拟dom的内容没变,直接用之前的真实dom;
      • 若虚拟dom的内容发生改变,则生成新的真实dom,随后替换掉页面中之前的真实dom
    • 旧虚拟dom中未找到与新虚拟dom相同的key。根据数据创建新的真实dom,随后渲染到页面

2、用index可能会引发的问题

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作,会产生没有必要的真实dom更新。页面效果没问题,但效率低。
  2. 如果结构中还包含输入类dom,会产生错误dom更新。
  3. 注意:如果不存在对数据逆序添加、逆序删除等破坏顺序操作,仅用于渲染到页面展示,使用index作为key是没问题的。

3、开发中如何选择key

  1. 最好使用每条数据的唯一标识作为key,比如id、学号、身份证等唯一值
  2. 如果确定只是简单展示,用index也是可以的

猜你喜欢

转载自blog.csdn.net/weixin_43899065/article/details/126945484