快速上手React(终章)

490 阅读6分钟

0.组件更新机制

React原理---组件更新机制

1.组件性能优化

减轻state

  • 减轻state:只存储跟组件渲染相关的数据(比如:count/ 列表数据 /loading等)
  • 只存储会改变的数据
  • 尽量减少state的体积
  • 注意:不用做渲染的数据不要放在state中
  • 对于需要多个方法中用到的数据,应该放到this中

避免不必要的重新渲染shouldComponentUpdate

  • 组件更新机制:父组件更新会引起子组件也被更新,这种思路很清晰
  • 问题:子组件没有任何变化时也会重新渲染,如果避免不必要的重新渲染优化性能?
  • 解决方式:使用钩子函数 shouldComponentUpdate(nextProps, nextState)
  • 在这个函数中,nextProps和nextState是最新的状态以及属性
  • 作用:这个函数有返回值,如果返回true,代表需要重新渲染,如果返回false,代表不需要重新渲染
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行(shouldComponentUpdate => render)
//index.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
    state = { count: 0 }
    handleClick = () => {
        // 点击时随机数,如果本次的值与上次的值不一样,则触发render
        let count = Math.floor(Math.random() * 3)
        this.setState({
            count
        })
        console.log('点击被触发啦', count);
    }
    shouldComponentUpdate(nextProps, nextState) {
        console.log('nextState', '下一次的值' + nextState.count, '上一次的值' + this.state.count);
        // 如果值未变更,便不触发render,优化性能
        return nextState.count !== this.state.count
    }
    render() {
        console.log('render被触发啦');
        return (
            <div>
                <h3>{this.state.count}</h3>
                <button onClick={this.handleClick}>按钮</button>
            </div>
        )
    }
}
ReactDOM.render(<App />, document.getElementById('root'))

在这里插入图片描述

PureComponent 纯组件

  1. React内部会自动实现一个 shouldComponentUpdate的浅比较效果

  2. PureComponent除了第一点之外 跟React.Component一样

  3. 类组件使用 进行性能优化

在这里插入图片描述

React.memo高阶组件

  1. 是一个高阶组件 接收一个组件 返回一个增强后的组件
  2. 增强后的组件 当props没改变的时候 不重新渲染
  3. 只给函数组件使用 进行性能优化
//index.js
import React, { Component } from 'react'
import ReactDOM from 'react-dom'

class App extends Component {
    state = { count: 0 }
    handleClick = () => {
        // 点击时随机数,如果本次的值与上次的值不一样,则触发render
        let count = Math.floor(Math.random() * 3)
        this.setState({
            count
        })
        console.log('点击被触发啦', count);
    }
    render() {
        return (
            <div>
                <CounterR count={this.state.count} />
                <button onClick={this.handleClick}>按钮</button>
            </div>
        )
    }
}


function Counter(props) {
    console.log('Counter');
    return <h3>随机数案例:{props.count}</h3>
}
//高阶组件 
const CounterR = React.memo(Counter)

ReactDOM.render(<App />, document.getElementById('root'))

在这里插入图片描述

2.浏览器是如何渲染一个html文档

  1. 浏览器解析html文本文件
  2. 解析里面的html和css 生成DOM树和cssOM
  3. DOM树和CSS样式树 生成一棵渲染树
  4. 渲染树计算布局 ==>重排
  5. 浏览器根据渲染树进行渲染 ==> 重绘

1.重排

  1. 哪些操作会导致重排
  • 字体大小
  • 元素大小改变
  • 元素位置改变
  • 元素删除、新增
  1. 重排一定导致重绘

2. 重绘

哪些操作只导致重绘

  1. 颜色改变
  2. 元素显隐 位置大小不变 也不影响其他元素的位置大小
  3. 重绘不一定重排
  4. 重排和重绘 会导致性能问题

3.如何减少重排重绘

  1. 批量替换
  2. 局部更新 按需更新

3. jsx 转化的过程

  1. jsx代码会通过webpack babel 插件 转换成 React.createElement
  2. React.createElement 转换成 js对象 也被称之为 虚拟DOM

在这里插入图片描述

4.虚拟DOM和Diff算法

虚拟DOM

本质上就是一个JS对象,用来描述你希望在屏幕上看到的内容

  1. 虚拟DOM是使用js模拟真实DOM元素 而且js创建在我们事件js执行的过程中由于都处于js执行环境 所以它的开销会比较小。但是如果我们直接在执行js的时候频繁地访问真实DOM元素开销会比较大。
  2. 所以通过js的方式 比较新旧虚拟DOM的差异 。得到差异再转换成真实DOM的差异之后再去修改真实DOM,这样性能更高。
  3. 这样的修改 符合 局部更新、批量替换、按需更新的特点。效率高。

在这里插入图片描述

在这里插入图片描述

总结

  1. 创建js对象开销小,因此使用js对象模拟虚拟DOM
  2. 虚拟DOM是用来模拟真实DOM的 通过新旧虚拟DOM的差异得到真实DOM的差异
  3. 通过新旧真实DOM的差异来修改旧的真实DOM 效率更高。因为它导致的重排重绘最少。
  4. 虚拟DOM的差异不光可以转换成HTML的差异、还可以转换成小程序标签的差异、手机App组件的差异、嵌入式组件差异 。虚拟DOM最大效果可以实现跨平台开发。也就是一次学习、随处编写。

Diff算法

用于加快比较 新旧虚拟DOM差异的算法。 假如:我们有1000个虚拟DOM节点,那么要比较1000个新旧虚拟DOM之间的差异是需要比较 1000 * 1000 = 100万次。

在这里插入图片描述

算法原理

React 分别对 tree diffcomponent diff 以及 element diff 进行了算法优化。

  1. tree diff 同层组件比较
  2. component diff 组件类型比较 类型不一致 直接替换
  3. element diff 数组元素 提供key 避免只是顺序不一致的重新创建

1. tree diff

分析步骤:

  1. 先分析顶层的父节点,如果不一样,则认为 数据全部发生了变化,执行全部替换
  2. 如父节点是一样的,则逐层往下进行比较,直到找到不一样的节点,则执行直接替换

在这里插入图片描述 对于 跨层级操作的dom,React 没有直接把 左侧的A 拷贝到右侧的D中,而是 创建A 创建B 创建C 删除原来的A,B,c,因此,尽量避免跨层级操作dom元素。

在这里插入图片描述

2. component diff

对于组件之间的比较,假如 D和G是两个结构很相似的组件,但是类型不一样,那么React也是直接视为不同的组件。执行直接替换

在这里插入图片描述

3. element diff

对于同一层级的元素,以上 新旧元素 除了顺序外,其他完全一样。在React中,执行算法时,发现 元素类型和KEY完全一样,那么只执行交换顺序即可,避免了反复创建和删除元素。这就是为什么我们在循环数组时要提供一个KEY的原因。

在这里插入图片描述 执行过程

  • 初次渲染时,React会根据初始化的state(model),创建一个虚拟DOM对象(树)
  • 根据虚拟DOM生成真正的DOM,渲染到页面
  • 当数据变化后(setState()),会重新根据新的数据,创建新的虚拟DOM对象(树)
  • 与上一次得到的虚拟DOM对象,使用Diff算法比对(找不同),得到需要更新的内容
  • 最终,React只将变化的内容更新(patch)到DOM中,重新渲染到页面
  • 什么是虚拟dom:用js对象来表示页面上dom元素的的样式体现
  • 虚拟dom的作用:高效更新页面,还有就是让react脱离了浏览器的概念
  • 怎么来高效更新页面的:就是在第一次渲染页面的时候生成一份初始的虚拟dom树,然后当数据改变之后,再生成一份虚拟dom树,然后根据新旧dom树进行对比,对比完成之后,把有区别的进行更新
  • diff算法的作用就是:新旧虚拟dom树在对比的时候就是通过diff算法来对比的
  • diff算法又是怎么去对比的:tree diff、component diff、element diff

在这里插入图片描述