React 原理

文章目录


React 原理

1. setState() 的说明

1.1 更新数据

  • setState() 是异步更新数据的(同学们是怎么理解异步的?谁能说一下你理解异步的模型)

  • 可以多次调用 setState() ,只会触发一次重新渲染

    this.state = { count: 1 }
    this.setState({
        count: this.state.count + 1
    })
    console.log(this.state.count) // 1
    

1.2 推荐语法

  • 推荐:使用 setState((state, props) => {}) 语法

  • 参数 state :表示最新的 state

  • 参数 props :表示最新的 props

    this.setState((state, props) => {  
        return {
            count: state.count + 1
        }
    })
    console.log(this.state.count) // 1
    

1.3 第二个参数

  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作

  • 语法:setState(updater, [callback])

    this.setState(
        (state, props) => {},
        () => {console.log('这个回调函数会在状态更新后立即执行')}
    )
    
    this.setState(
        (state, props) => {},
        () => {
            document.title = '更新state后的标题:' + this.state.count
        }
    )
    

2. JSX 语法的转化过程

  • JSX 仅仅是 createElement() 方法的语法糖(简化语法)
  • JSX 语法被 @babel/preset-react 插件编译为 createElement() 方法
  • React 元素:是一个对象,用来描述你希望在屏幕上看到的内容

在这里插入图片描述

3. 组件更新机制

  • setState() 的两个作用: 1. 修改 state 2. 更新组件(UI)
  • 过程:父组件重新渲染时,也会重新渲染子组件。但只会渲染当前组件子树(当前组件及其所有子组件)

在这里插入图片描述

4. 组件性能优化

4.1 减轻 state

  • 减轻 state:只存储跟组件渲染相关的数据(比如:count / 列表数据 / loading状态 等)

  • 注意:不用做渲染的数据不要放在state 中,比如定时器id等

  • 对于这种需要在多个方法中用到的数据,应该放在 this 中(组件实例上)

    class Hello extends Component {
        componentDidMount() {
            // timerId存储到this中,而不是state中
            this.timerId = setInterval(() => {}, 2000)
        }
        componentWillUnmount() {
            clearInterval(this.timerId)
        }
        render() { … }
    }
    

4.2 避免不必要的重新渲染

  • 组件更新机制:父组件更新会引起子组件也被更新

  • 问题:子组件没有任何变化时也会重新渲染

  • 如何避免不必要的重新渲染呢?

  • 解决方式:使用钩子函数shouldComponentUpdate(nextProps, nextState)

  • 作用:通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 表示不重新渲染

  • 触发时机:组件更新阶段,组件重新渲染(render)前执行(shouldComponentUpdate  render)

    class App extends Component {
        shouldComponentUpdate(nextProps, nextState) {
            // 根据条件,决定是否重新渲染组件
            return false
        }
        render() {…}
    }
    

4.3 纯组件

  • 纯组件:PureComponent 与 React.Component 功能相似

  • 区别:PureComponent 内部自动实现了shouldComponentUpdate 钩子,不需要手动比较

  • 原理:纯组件内部通过分别对比 前后两次 props 和 state 的值,来决定是否重新渲染组件

    class Hello extends React.PureComponent {  
        render() {
            return (
                <div>纯组件</div>
            )
        }
    }
    
  • 说明:纯组件内部的对比是 shallow compare(浅层对比)

  • 对于值类型来说:比较两个值是否相同(直接赋值即可)

    let number = 0
    let newNumber = number
    newNumber = 2
    console.log(number === newNumber) // false
    
    state = { number: 0 }
    setState({
        number: Math.floor(Math.random() * 3)
    })
    // PureComponent内部对比:
    最新的state.number === 上一次的state.number // false,重新渲染组件
    
  • 对于引用类型来说:只比较对象的引用(地址)是否相同

    const obj = { number: 0 }  
    const newObj = obj  
    newObj.number = 2
    console.log(newObj === obj) // true
    
    state = { obj: { number: 0 } }
    // 错误做法
    state.obj.number = 2  
    setState({ obj: state.obj })
    // PureComponent内部比较:
    最新的state.obj === 上一次的state.obj // true,不重新渲染组件
    

    注意:state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据!

    // 正确!创建新数据
    const newObj = {...state.obj, number: 2}  
    setState({ obj: newObj })
    
    // 正确!创建新数据
    // 不要用数组的push / unshift 等直接修改当前数组的的方法
    // 而应该用 concat 或 slice 等这些返回新数组的方法
    this.setState({
        list: [...this.state.list, {新数据}]
    })
    

5. 虚拟 DOM 和 Diff 算法

  • React 更新视图的思想是:只要state 变化就重新渲染视图
  • 特点:思路非常清晰
  • 问题:组件中只有一个 DOM 元素需要更新时,也得把整个组件的内容重新渲染到页面中?
  • 理想状态:部分更新,只更新变化的地方。
  • 问题:React 是如何做到部分更新的? 虚拟 DOM 配合 Diff 算法
  • 虚拟 DOM:本质上就是一个 JS 对象,用来描述你希望在屏幕上看到的内容(UI)

在这里插入图片描述

执行过程

  1. 初次渲染时,React 会根据初始state(Model),创建一个虚拟 DOM 对象(树)。
  2. 根据虚拟 DOM 生成真正的 DOM,渲染到页面中。
  3. 当数据变化后(setState()),重新根据新的数据,创建新的虚拟DOM对象(树)。
  4. 与上一次得到的虚拟 DOM 对象,使用 Diff 算法 对比(找不同),得到需要更新的内容。
  5. 最终,React 只将变化的内容更新(patch)到 DOM 中,重新渲染到页面。

相关文章

一、前言 在组件方面react和Vue一样的,核心思想玩的就是组件...
前言: 前段时间学习完react后,刚好就接到公司一个react项目...
前言: 最近收到组长通知我们项目组后面新开的项目准备统一技...
react 中的高阶组件主要是对于 hooks 之前的类组件来说的,如...
我们上一节了解了组件的更新机制,但是只是停留在表层上,例...
我们上一节了解了 react 的虚拟 dom 的格式,如何把虚拟 dom...