likes
comments
collection
share

React 日常使用/优化机制

作者站长头像
站长
· 阅读数 17

React 渲染/更新机制

  • 渲染流程

JSX->虚拟DOM -> 真实DOM

  • 更新机制

React 日常使用/优化机制

  • 更新流程
  1. 同层节点之间互相比较 不会跨节点比较
  2. 不同类型节点 产生不同的树结构(如果同层父节点有差异 会直接节点替换)
  3. 定义key 来指定哪些节点在不同的渲染保持稳定
  • key的优化
  1. 在最后位置插入数据 -> 有无key意义不大
  2. 在前面插入数据 -> 如果没有key的话 所有的li节点都会改变
  3. 在拥有key 做修改时候

React 使用 key 来匹配原有树上的子元素以及最新树上的子元素: 在下面这种场景下,key为111和222的元素仅仅进行位移,不需要进行任何的修改; 将key为333的元素插入到最前面的位置即可;

  • key 的注意事项
  1. key是唯一的
  2. key不要使用随机数(随机数在下一次render时候 会重新生成)
  3. 使用index作为key 对性能没有优化

SCU性能优化

  • render函数

state中变量有被修改 这就会导致render重新执行。对于没有依赖于修改的变量来说 它们不需要被重新渲染&diff比较。这就回造成性能较低,可以进行如下优化

  1. shouldComponentUpdate 生命周期(简称SCU)
  2. 这个方法有两个参数 newProps(最新的props属性) newState(最新的state属性) 根据返回的boolean 来判断是否触发render函数

  // shouldComponentUpdate(nextProps, newState) {
  //   因为此时还没有更新state的值 所以this.state还是未修改的
  //   if (this.state.message !== newState.message || this.state.counter !== newState.counter) {
  //     return true
  //   }
  //   return false
  // }
  1. 将class继承与PureComponent 其内部是对state和props进行一个浅层次的对比(只比较第一层)
  2. 在函数组件中 没有继承和生命周期的。可以使用memo函数给他包裹起来
import { memo } from "react"
const Profile = memo(function(props) {
  return <h2>Profile: {props.message}</h2>
})

  1. 不可变数据力量 在使用pureComponent 时候 不要去修改它原来的state 需要重新赋值。因为对象是引用类型 如果你直接修改其本身值 在内部它进行对比 就会因为new & old 是在一个内存空间内。然后不进行修改&&渲染,如果给他重新赋值给一个新的变量 在内存的角度来看 这就会创建一个新的内存空间 (字符串不需要 因为你直接改变字符串 就相当于改变了内存空间)
    books: [
        { name: "你不知道JS", price: 99, count: 1 },
        { name: "JS高级程序设计", price: 88, count: 1 },
        { name: "React高级设计", price: 78, count: 2 },
        { name: "Vue高级设计", price: 95, count: 3 },
      ],
      
  addNewBook() {
    const newBook = { name: "Angular高级设计", price: 88, count: 1 }

    // 1.直接修改原有的state, 重新设置一遍
    // 在PureComponent是不能引入重新渲染(re-render)
    this.state.books.push(newBook)
    this.setState({ books: this.state.books })

    // 2.赋值一份books, 在新的books中修改, 设置新的books
    const books = [...this.state.books]
    books.push(newBook)

    this.setState({ books: books })
  }

  addBookCount(index) {
    // this.state.books[index].count++
    const books = [...this.state.books]
    books[index].count++
    this.setState({ books: books })
  }

在它内部进行浅层比较 其实就是进行了shallowEqual() !shallowEqual(oldProps, newProps) || !shallowEqual(oldState, newState)

  function shallowEqual(objA: mixed, objB: mixed): boolean {
    if (Object.is(objA, objB)) {
      return true;
    }
    if (
      typeof objA !== 'object' ||
      objA === null ||
      typeof objB !== 'object' ||
      objB === null
    ) {
      return false;
    }
    const keysA = Object.keys(objA);
    const keysB = Object.keys(objB);
    // 如果两个对象长度不一样的话 直接渲染
    if (keysA.length !== keysB.length) {
      return false;
    }
    for (let i = 0; i < keysA.length; i++) {
      if (
      // 比较a的key 是否在objB中
        !hasOwnProperty.call(objB, keysA[i]) ||
        // A对象和B对象是否是一个内存空间
        !Object.is(objA[keysA[i]], objB[keysA[i]])
      ) {
        return false;
      }
    }
    return true;
  }

  function shallowCompare(instance, nextProps, nextState) {
    return (
      !shallowEqual(instance.props, nextProps) ||
      !shallowEqual(instance.state, nextState)
    );
  }

通过ref 获取到dom属性

  • 直接给元素绑定ref (不建议使用 )
  • 通过createRef 给ref绑定值
  • ref的参数 通过回调函数绑定ref
  • 给组件class类组件 添加ref 可以获取到类组件的组件实例
import React, { PureComponent, createRef } from 'react'

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {

    }

    this.titleRef = createRef()
    this.titleEl = null
  }

  getNativeDOM() {
    // 1.方式一: 在React元素上绑定一个ref字符串
    // console.log(this.refs.why)

    // 2.方式二: 提前创建好ref对象, createRef(), 将创建出来的对象绑定到元素
    // console.log(this.titleRef.current)

    // 3.方式三: 传入一个回调函数, 在对应的元素被渲染之后, 回调函数被执行, 并且将元素传入
    console.log(this.titleEl)
  }

  render() {
    return (
      <div>
        <h2 ref="why">Hello World</h2>
        <h2 ref={this.titleRef}>你好啊,李银河</h2>
        <h2 ref={el => this.titleEl = el}>你好啊, 师姐</h2>
        <button onClick={e => this.getNativeDOM()}>获取DOM</button>
      </div>
    )
  }
}
  • 函数组件没有组件实例 如果想获取到函数组件内部的元素 需要使用forwardRef函数包裹
import React, { PureComponent, createRef, forwardRef } from 'react'


const HelloWorld = forwardRef(function(props, ref) {
  return (
    <div>
      <h1 ref={ref}>Hello World</h1>
      <p>哈哈哈</p>
    </div>
  )
})


export class App extends PureComponent {
  constructor() {
    super()

    this.hwRef = createRef()
  }

  getComponent() {
    console.log(this.hwRef.current)
  }

  render() {
    return (
      <div>
        <HelloWorld ref={this.hwRef}/>
        <button onClick={e => this.getComponent()}>获取组件实例</button>
      </div>
    )
  }
}

export default App

受控组件

  • 在原生组件中(input/select) react中没有vue的双向绑定 所以如果想要获取到输入的值 就得通过onChange事件。如果想给他一个初始值 就需要绑定value属性。如果给了value 那它就是一个受控组件 就需要onChange 来修改value的
import React, { PureComponent } from 'react'

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      username: "coderwhy"
    }
  }

  inputChange(event) {
    console.log("inputChange:", event.target.value)
    this.setState({ username: event.target.value })
  }

  render() {
    const { username } = this.state

    return (
      <div>
        {/* 受控组件 */}
        <input type="checkbox" value={username} onChange={e => this.inputChange(e)}/>

        {/* 非受控组件 */}
        <input type="text" />
        <h2>username: {username}</h2>
      </div>
    )
  }
}

export default App