当前位置: 首页 > news >正文

16.React性能优化SCU

2. React性能优化SCU

PureComponent
memo
数据不可变力量

books
this.state.books.splice(2,1)
this.setState({books:this.state.books})

  • 2.1. React更新机制
      1. 在前面已经了解过React的渲染流程:
      • 编写jsx -> babel转换成React.createElement -> ReactElement -> 虚拟DOM(形成树结构) -> 真实DOM
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      1. 那么React的更新流程呢?
      • 外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
  • 2.2. React的更新流程
      1. React在props或state发生改变时,会调用React的render方法,会创建一颗不同的树。
      1. React需要基于这两颗不同的树之间的差别来判断如何有效的更新UI:
      • 如何一棵树参考另外一棵树进行完全比较更新,那么即使是最先进的算法,该算法复杂程度为O(n^3),其中n是树中元素的数量;
      • [外链图片转存中…(img-E7dHwjHv-1761926407037)]https://grfia.dlsi.ua.es/ml/algorithms/references/editsurvey_bille.pdf
      • 如果在React中使用了该算法,那么展示1000个元素所需要执行的计算量将在十亿的量级范围;
      • 这个开销太过昂贵了,React的更新性能会变得非常低效;
      1. 于是,React对这个算法进行了优化,将其 优化成O(n), 如何优化的呢?
      • 3.1. 同层节点之间相互比较,不会跨节点比较
      • 3.2. 不同类型的节点,产生不同的树结构
      • 3.3. 开发中,可以通过key来制定哪些节点在不同的渲染下保持稳定;
        • 不同节点直接生成新的DOM结构图:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 同层节点进行对比图:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 子元素有对应的key的话,会尽量来对比复用某些节点,之后插入一些新插入的节点;绑定key值插入元素对比图:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      1. 在前面遍历列表时,总是会提示一个警告,让我们加入一个key属性
      • 4.1. 方式一:在最后位置插入数据
        • 这种勤快,有无key意义并不大
      • 4.2. 方式二:在前面插入数据
        • 这种做法,在没有key的情况下,所有的li都需要进行修改;
      • 4.3. 当子元素(这里的li)拥有key时,React使用key来匹配原有树上的子元素以及最新树上的子元素;
        • 在下面这种场景下,key为c和d的元素仅仅进行位移,不需要进行任何的修改;
        • 将key为e的元素插入到前面的位置即可
        • 如下图:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 4.4. key的注意事项:
        • key应该是唯一的;
        • key不要使用随机数(随机数在下一次render时,会重新生成一个数字)
        • 使用index作为key, 对性能是没有优化的
  • 2.3. render函数被调用
      1. 我们使用之前的一个嵌套案例
      • 在App中,增加一个计数器的代码
      • 当点击+1时,会重新调用App的render函数
      • 而当App的render函数被调用时,所有的子组件的render函数都会被重新调用;
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      1. 那么,我们可以思考一下,在以后的开发中,我们只要是修改了App中的数据,
        所有组件都需要重新render,进行diff算法,性能必然是很低的
      • 事实上,很多的组件是没有必须要重新render;
      • 它们调用render应该有一个前提,就是依赖的数据(state、props)发生改变时,在调用自己的render方法
      1. 如何来控制render方法是否被调用呢?
      • 通过shouldComponentUpdate方法即可;

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 2.4. shouldComponentUpdate
      1. React给我们提供了一个生命周期方法shouldComponentUpdate(很多时候,我们简称为SCU),这个方法接受参数,并且需要有返回值:
      1. 该方法有两个参数:
      • 参数一: nextProps修改之后,最新的props属性
      • 参数二: nextState修改之后,最新的state属性
      1. 该方法返回值是一个boolean类型:
      • 返回值为true, 那么久需要调用render方法;
      • 返回值为false,那么就不需要调用render方法;
      • 默认返回的是true, 也就是只要state发生改变,就会调用render方法;
      1. 比如我们在App中增加一个message属性:
      • jsx中并没有依赖这个message, 那么它的改变不应该引起重新渲染;
      • 但是因为render监听到state的改变,就会重新render,所以最后render方法还是被重新调用了
  • 2.5. PureComponent
      1. 如果所有的类,我们都需要手动来实现shouldComponentUpdate,那么会给我们开发者增加非常多的工作量。
      • 我们来设想一下shouldComponentUpdate方法中的各种判断的目的是什么?
      • props或者state中的数据是否发生了改变,来决定shouldComponentUpdate返回的true或者false;
      1. 事实上React已经考虑到了这一点,所以React已经默认帮我们实现好了,如何实现嗯?
      • 将class继承自PureComponent
      1. 如果想要通过React机制进行性能优化导入PureComponent, 继承到Class组件上
        import React, { PureComponent } from 'react'export class App extends PureComponent {constructor () {super()this.state = {message: 'Hello World',counter: 0}}render() {// 初始化的时候,App组件和子组件的render函数都会执行console.log('App render')const { message, counter } = this.statereturn (<div><h2>App Component: {message} - {counter}</h2><button onClick={e => this.changeText()}>修改文本</button><button onClick={e => this.changeCounter()}>counter+1</button>{/* <Home /><Recommend /> */}<Home message={message}/><Recommend counter={counter}/><Profile message={message} /></div>)}}
      
      1. PureComponent内部的操作就是自动的对state和props进行一个相关判断,是返回一个false还是一个true
      1. PureComponent本质是进行一个浅层的比较,本身内部的源码是做了一个浅层的比较,只比较第一层是不是同一个对象
      1. 示例代码如下:
      • App.jsx
        import React, { Component, PureComponent } from 'react'import Home from './Home'import Recommend from './Recommend' import Profile from './Profile'// 如果想要通过React机制进行性能优化导入PureComponent, 继承到Class组件上// PureComponent内部的操作就是自动的对state和props进行一个相关判断,是返回一个false还是一个true// PureComponent本质是进行一个浅层的比较,本身内部的源码是做了一个浅层的比较,只比较第一层是不是同一个对象export class App extends PureComponent {constructor () {super()this.state = {message: 'Hello World',counter: 0}}// shouldComponentUpdate (nextProps, nextState) {//   // App中可以性能优化的点//   if(this.state.message !== nextState.message || this.state.counter !== nextState.counter) {//     return true//   }//   return false// }changeText () {// 当修改message的值,App的render函数会执行,Home和Recommend的render函数会不会执行呢?// Home和Recommend的render函数也会执行// this.setState({//   message: '你好啊, 李银河'// })// 如果设置的是和原来一样的值,依然是会触发render函数执行// 这样都执行性能不高,不高在两个地方// - App的render函数重新执行了  App -> render()// - 子组件: Home和Recommend的render函数重新执行了 Home/Recommend -> render()// 对于以上这种情况是没有必要执行render函数的 可以使用shouldComponentUpdate方法来优化性能this.setState({ message: 'Hello World'})}changeCounter () {this.setState({counter: this.state.counter + 1})}render() {// 初始化的时候,App组件和子组件的render函数都会执行console.log('App render')const { message, counter } = this.statereturn (<div><h2>App Component: {message} - {counter}</h2><button onClick={e => this.changeText()}>修改文本</button><button onClick={e => this.changeCounter()}>counter+1</button>{/* <Home /><Recommend /> */}<Home message={message}/><Recommend counter={counter}/><Profile message={message} /></div>)}}export default App
      
      • Home.jsx
        import React, { PureComponent } from 'react'export class Home extends PureComponent {constructor(props) {super(props)this.state = {}}// // 这个组件要不要更新// shouldComponentUpdate(nextProps, nextState) {//   // 自己对比state是否发生改变: this.state和nextState//   // shouldComponentUpdate的原理就是取原来的值和更新后的值进行对比,如果不一样更新,一样不更新render//   if(this.props.message !== nextProps.message) {//     return true//   }//   return false// }render() {console.log('Home render')return (<div><h2>Home Component: {this.props.message}</h2></div>)}}export default Home
      
      • Recommend.jsx
      import React, { PureComponent } from 'react'export class Recommend extends PureComponent {// shouldComponentUpdate(nextProps, nextState) {//   if(this.props.counter !== nextProps.counter) {//     return true//   }//   return false// }render() {console.log('Recommend render')return (<div><h2>Recommend Component: {this.props.counter}</h2></div>)}
      }export default Recommend
      
  • 2.6. 高阶组件memo
      1. 目前是针对类组件可以使用PureComponent,那么函数式组件呢?
      • 事实上函数组件我们在props没有更改时,也是不希望其重新渲染DOM树结构;
      1. 需要使用一个高阶组件memo
      • 2.1. 将之前的封装好的函数式组件通过memo函数进行一层包裹;
      • 2.2. 最终的效果是,当数据发生改变时,函数式组件会重新执行,否则不会重新执行;
      1. 使用memo高阶函数对函数式组件进行性能优化
      1. 示例代码如下:
        import { memo } from "react";// memo高阶函数const Profile = memo(function (props) {console.log('Profile render')return (<div><h2>Profile Component: {props.message}</h2></div>)})export default Profile;
  • 2.7. 数据不可变的力量
      1. 不可变的力量:不要直接去修改state里面的数据,而是把整个数据都修改掉,整个东西指向的内存全部修改掉,引用类型必须这样操作,值类型当设置一个新的值,本身这个东西就是全部修改掉了
      1. 直接修改原有的state,重新设置一遍,在PurComponent是不能引起重新渲染(re-render)
      • 关键代码如下:
      addNewBook () {this.state.books.push({name: 'Vue高级程序设计',price: 95,count: 1})this.setState({books: this.state.books})
      }
      
      1. 复制一份books,在新的books中修改,设置新的books, 本质上指向的是同一个对象, 新数组的目的是保证一定可以执行render函数
      • 内存地址如下图:
        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
      • 关键代码如下:
        addNewBook () {const newBooks =[...this.state.books]newBooks.push({name: 'Vue高级程序设计',price: 95,count: 1})this.setState({books: newBooks})
        }
        
    • 4.完整代码如下:

        import React, { PureComponent } from 'react'export class App extends PureComponent {constructor() {super()this.state = {// React中要求state里面的数据都是不可变的// 如果想要改变,把整个对象改掉books: [{ name: '你不知道js', price: 99, count:1 },{ name: 'JS高级程序设计', price: 88, count:1 },{ name: 'React高级程序设计', price: 78, count:2 },{ name: 'Vue高级程序设计', price: 95, count:1 },]}}addBookCount(index) {// 不要这样的写法// this.state.books[index].count++// 上面的修改和下面浅层复制一份修改,本质上指向的是同一个对象// 既然一样为什么要设置个新数组? 因为可以保证设置过去可以是一个新数组,新数组的目的是保证一定可以执行render函数const newBooks = [...this.state.books]newBooks[index].count++this.setState({books: newBooks})}//  shouldComponentUpdate (nextProps, nextState) {//   // 在PurComponent中,要不要修改,底层在实现shouldComponentUpdate就是把新旧props和state进行浅层比较, 发生改变返回true,否则返回false//   shallowEqual(nextProps, this.props)//   shallowEqual(nextState, this.state)//  }addNewBook () {// 不可变的力量// 1. 直接修改原有的state,重新设置一遍// 在PurComponent是不能引起重新渲染(re-render)// this.state.books.push({//   name: 'Vue高级程序设计',//   price: 95,//   count: 1// })// 会和原来的books进行比较,发现和原来的books是同一个对象,所以不会更新render// 只要放到state里面的数据不要直接修改它,它是不可变的// 如果要修改要重新设置新的对象好进行浅层比较,让React知道数据发生变化了// 这里的books是不可变的// this.setState({//   books: this.state.books// })// 2. 复制一份books,在新的books中修改,设置新的booksconst newBooks =[...this.state.books]newBooks.push({name: 'Vue高级程序设计',price: 95,count: 1})this.setState({books: newBooks})}render() {const { books } = this.statereturn (<div><h2>书籍列表</h2><ul>{books.map((item, index) => {return (<li key={index}><span>name: {item.name} - price: {item.price} - count: {item.count} </span><button onClick={() => this.addBookCount(index)}>+1</button></li>)})}</ul><button onClick={e => this.addNewBook()}>添加新书籍</button></div>)}}export default App
      
      1. 源码部分:
        1. 源码部分判断一个组件是否要更新如下图:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        1. 源码里浅层对比逻辑:
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        1. 底层做浅层比较的三个地方:
        • 2.1. 第一个做了一个标识:isPureReactComponent
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 2.2. 第二个判断是否是纯函数,那么进行一个浅层比较
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
        • 2.3. 第三个在shallowEqual中进行比较
          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
http://www.dtcms.com/a/552720.html

相关文章:

  • Linux系统C++开发环境搭建工具(三)—— brpc使用指南
  • 《静态库与动态库:从编译原理到实战调用,一篇文章讲透》
  • 标签绑定货物信息误删处理
  • 划时代的AI Agent qwen的回答和思考
  • Rust中泛型函数实现不同类型数据的比较
  • 19. React的高阶组件
  • 中小企业建站服务外贸建站模板免费
  • 网站域名多少钱一年wordpress 发布 工具
  • 个人备案网站可以做淘宝客吗codex.wordpress.org
  • 做网络推广自己建网站建设局局长权力大吗
  • 外贸网站建设需求无锡做网站设计
  • 泰安网站建设策划方案wordpress 评论模版
  • 论坛网站用的虚拟主机做网站需要先学什么
  • 佛山网站推广seo定制网站开发流程图
  • 慈溪市住房和城乡建设局网站营销型网站建设需要懂什么软件
  • 参考网是正规网站吗平面设计大师
  • 网站策划书wordpress群
  • 查看网站开发平台苏州最新情况最新消息今天
  • 攀枝花网站建设兼职wap网站乱码
  • 开封景区网站建设项目方案婚纱摄影网站建站
  • 购物网站 购物车界面如何做访问网站提示输入用户名密码
  • 丹徒网站建设价格深圳网站建设软件定制公司
  • 有域名一定要买空间做网站安阳做网站的公司
  • 网站程序源码手机能制作网站吗
  • 简述电子政务系统网站建设的基本过程网站建设维护协议书
  • 苏州住房和城乡建设厅网站wordpress 定时显示
  • 机械公司网站模板爱网站
  • 全国精品课程建设网站cms源码下载
  • 邢台网站推广公司百度账户登录
  • 建网站如何上传门户网站建设方法