• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

React--性能优化

武飞扬头像
一如彷徨
帮助1

React更新机制

  • React 更新流程如下:

学新通

  • Reactpropsstate 发生改变时,会调用 Reactrender 方法,会创建不同的 DOM

React 需要基于这两棵不同的树之间的差别来判断如何有效的更新 UI

  • 如果进行全量比较更新(旧 DOM 树的每个节点都和新 DOM 树每个节点比较) ,即使是最先进的算法,其复杂度也需要 O(n²),其中 n 是树中元素的数量
  • 这种全量比较更新的算法会使得 React 更新性能变得低效

React 对这个算法的复杂度优化为 O(n),优化手段如下:

  • 同层节点之间相互比较,不会跨节点比较
  • 不同类型的节点,产生不同的树结构
  • 通过 key 来指定哪些节点在不同的渲染下保持稳定.

key的优化

  • 在通过循环遍历生产 DOM 结构时,如果没有绑定 key 属性,浏览器会发出警告

学新通

  • 需要给每个遍历生成的 DOM 节点绑定一个唯一的 key 属性

key 的作用:

  • keyReact 用于追踪哪些列表中元素被修改、被添加或者被移除的辅助标识
  • ReactDiff 算法对比更新时,会借助元素的 key 来判断该元素是新创建的还是被移动而来的,进而减少不必要重渲染
  • React 还借助 key 来判断元素与本地状态的关联关系

key 对新增元素的意义:

  • DOM 树最后新增元素: 有无 key 属性的意义不大
  • DOM 树前面或者中间新增元素:

    • 没有绑定 key 在新增元素的位置到最后的 DOM 节点都会进行修改
    • 绑定 key 通过 key 找到对应元素进行移位复用,不需要修改任何 DOM 元素,只需将新增元素插入到对应的位置即可

key 的注意事项

  • key 应该是唯一的
  • key 不要使用随机数(随机数在下一次 render 时,会重新生成)
  • 若使用索引作为 key,对性能则没有优化

render优化

  • 当父组件调用 setState 更改数据时,组件本身会重新调用 render 函数,但是子组件的 render 函数也都会被重新调用
 class App extends Component {
   constructor() {
     super()
     this.state = {
       text: 'Hello,React',
     }
   }
   changeText() {
    this.setState({text: '你好啊,React'}) 
   }
   render() {
     console.log('App render');
     const { text } = this.state
     return (
       <div>
         <h2>APP-{text}</h2>
         <button onClick={e => this.changeText()}>修改文本</button>
         <Home/>
         <Recommend/>
       </div>
     )
   }
 }

学新通

  • 如果只修改了组件本身的数据,其自身和所有后代组件的 render 函数都会被重新调用,进行 diff 算法,这样会造成性能很低
  • 调用 render 应该有一个前提,那就是依赖的数据(state、props)发生改变时,再调用 render 方法
  • React 提供了一个生命周期函数 shouldComponentUpdate,可用于控制是否调用 render 重渲染

该生命周期函数提供了两个参数和必须要一个返回值

  • 第一个参数newProps 修改之后的 props 属性
  • 第二个参数newState 修改之后的 state 属性
  • 返回值: 布尔类型,用于决定是否调用 render,默认为 true,即只要调用 setState 都会重新 render
 shouldComponentUpdate(newProps, newState) {
   if(this.state.text !== newState.text) return true
   return false
 }

PureComponent

  • 在数据非常庞大的时候,如果所有组件都要手动实现 shouldComponentUpdate,那么工作量则一言难尽

使用 shouldComponentUpdate 的目的: 根据 propsstate 中的数据是否发生了改变,来决定是否需要重新执行 render 函数

  • React 提供了一个叫 PureComponent 的类,其内部已经实现了这种机制,让组件继承 PureComponent
 import { PureComponent } from 'react'
 
 class App extends PureComponent { }
  • 那么函数式组件中不能继承 PureComponent,需要使用高阶函数 memo 对其进行包裹,实现这种机制
 import { memo } from 'react'
 const App = memo(function(props) {})

数据的不可变性

  • 其实 PureComponent 内部只做了浅层比较,即如果在 propsstate 中存在引用类型的数据,那么修改其深层数据时,PureComponent 是无法判断的
 class App extends PureComponent {
   constructor() {
     super()
     this.state = {
       books: [
         { name:'你不知道的JavaScript', price: 99, count: 1 },
       ]
     }
   }
   
   addNewBook() {
     const newBook = { name:'React高级设计', price: 118, count: 1 };
     // 改变了state中的books
     this.state.books.push(newBook)
     // 调用setState
     this.setState({
       books: this.state.books
     })
   }
   
   render() {
     const { books } = this.state
     return (
       <div>
         <button onClick={e => this.addNewBook()}>添加新书籍</button>
       </div>
     )
   }
 }
  • 当组件继承自 PureComponent 时,上面的做法并不会让界面更新,官方文档中也建议不使用这种做法

学新通

  • 官方文档的意思是,在修改引用类型的深层数据时,PureComponent 内部比较的是其引用指针,并且避免这种修改数据的方式
  • 官方文档: zh-hans.legacy.reactjs.org/docs/optimi…

学新通

  • 所以最好改用以下写法,让statebooks 的获得一个新的引用
 addNewBook() {
   const newBook = { name:'React高级设计', price: 118, count: 1 };
   this.setState({
     books: [...this.state.books, newBook]
   })
 }

shallowEqual

  • checkShouldComponentUpdate 方法中,如果是PureComponent组件,则使用 shallowEqual 函数对 stateprops 进行浅层比较

学新通

shallowEqual函数源码:

 function shallowEqual(objA: mixed, objB: mixed): boolean {
   // shallowEqual函数首先会对新旧state或props进行比较,判断是否为同一个对象
   if (is(objA, objB)) {
     return true; // 是则不用执行render
   }
   
   // 判断新旧state或props是否为对象或者null
   if (typeof objA !== 'object' || objA === null ||
       typeof objB !== 'object' || objB === null) {
     return false; // 不成立则执行render 
   }
 
   // 然后取出新旧 `state` 或 `props` 中所有属性 ,首先判断属性的数量是否相等
   const keysA = Object.keys(objA);
   const keysB = Object.keys(objB);
   if (keysA.length !== keysB.length) {
     return false; // // 不相等则直接重新render
   }
 
   // 最后会对新旧state 或 props 中每个属性进行判断
   for (let i = 0; i < keysA.length; i  ) {
     if (
       // 新state(或props)的属性是否在旧的中
       !hasOwnProperty.call(objB, keysA[i]) ||
       // 如果在则判断两个属性是否为相等(引用类型会判断其引用指针)
       !is(objA[keysA[i]], objB[keysA[i]])
     ) {
       return false; // 不成立则执行render
     }
   }
 
   // 最后如果上面都不成立,则不执行render
   return true;
 }

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhgajffa
系列文章
更多 icon
同类精品
更多 icon
继续加载