qiaofugui
qiaofugui
发布于 2024-05-21 / 0 阅读
0
0

React-原理

setState() 说明

更新数据

  • setState()异步更新数据的
  • 可以多次调用 setState(),之后触发一次重新渲染(render)
  • 使用下面这种语法时,后面的 setState() 不要依赖于前面的 setState()
this.state = {
    count: 1
}
this.setState({
    count: this.state.count + 1
})
console.log(this.state.count) // 1
this.setState({
    count: this.state.count + 1 // 1 + 1
})
console.log(this.state.count) // 1

推荐语法 setState((state, props) => { })

  • 使用 setState((state, props) => { }) 语法
  • 参数 state 表示最新的 satte
  • 参数 props 表示最新的 props
// 这种语法也是异步更新 state 的
this.setState((state, props) => {
    return {
        count: state.count + 1
    }
})
console.log(this.state.count) // 1
this.setState((state, props) => {
    console.log('第二次调用', state.count) // 2
    return {
        count: state.count + 1
    }
})
console.log(this.state.count) // 1

第二个参数

  • 场景:在状态更新(页面完成渲染)后立即执行某个操作
  • 语法:setState(updater[, callback])
this.setState((state, props) => {
    return {
        count: state.count + 1
    }
}, () => {
    // 状态更新后并且重新渲染后,立即执行
    console.log('状态更新完成', this.state.count) // 再打印 2
    document.title = '更新 state 后的标题' + this.state.count
})
console.log(this.state.count) // 先打印 1

setState 处在同步的逻辑中,异步更新状态,异步更新真实 dom

setState 处在异步的逻辑中,同步更新状态,同步更新真实 dom

JSX 语法的转化过程

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

组件更新机制

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

组件性能优化

减轻 state

  • state 是组件的状态,只存储根组件渲染相关的数据(比如:count / 列表数据 / loading 等)
  • 不用做渲染的数据不要放在 state 中,比如定时器id等
  • 对于这种需要在多个方法中用到的数据,应该放在 this 中
class Hello extends Component {
    componentDidMount() {
        // timerId 存储到 this 中,而不是 state 中
        this.timerId = setInterval( () => { }, 2000 )
    }
    componentWillUnmount() {
        clearInterval(this.timerId)
    }
    render() { ... }
}

避免不必要的重新渲染

  • 父组件更新会引起子组件也被更新,这种思路很清晰
  • 问题:子组件没有任何变化时也会重新渲染
  • 解决方式:使用钩子函数 shouldComponentUpdate(nextProps, nextState)
  • 作用:通过返回值决定该组件是否重新渲染,返回 true 表示重新渲染,false 反之
  • 触发时机:更新阶段的钩子函数,组件重新渲染前执行( shouldComponentUpdate -> render )
class Hello extends Component {
    shouldComponentUpdate(nextProps, nextState) {
        // 根据条件,决定是否重新渲染组件
        console.log(this.state) // 更新前的状态
         console.log(nextProps) // 最新的状态
        return false
    }
    render() { ... }
}

纯组件

  • React.PureComponentReact.Component 功能相似
  • 区别:PureComponent 内部自动实现了 shouldComponentUpdate 钩子,不需要手动比较
  • 原理:纯组件内部通过分别对比前后两次 props 和 state 的值,来决定是否重新渲染组件
class Hello extends React.PureComponent {
    render() {
        return (
            <div>纯组件</dic>
        )
    }
}
  • 纯组件内部的对比是 shallow compare(浅层对比)
  • 对于值类型来说:比较两个值是否相同(直接赋值即可)
let number = 0
let newNumber = number
newNumber = 2
console.log(number === newNumber) // false

state = {
    number: 0
}
this.setState({
    number: Math.floor(Math.random() * 3)
})
// PureComponeent 内部对比:
最新的 state.number === 上一次的 satte.number // false 重新渲染组件
  • 对于引用类型来说:只比较对象的引用(地址)是否相同
  • state 或 props 中属性值为引用类型时,应该创建新数据,不要直接修改原数据
const obj = {
    number: 0
}
const newObj = obj
newObj.number = 2
console.log(newObj === obj) // true

state = {
    obj: {
        number: 0
    }
}
// 错误做法
this.state.obj.number = 2
this.setState({
    obj: state.obj
})
// PureComponent 内部比较
最新的 state.obj === 上一次的 satte.obj // true 不重新渲染组件
// 正确 创建新数据
const newObj = { ...state.obj, number: 2 }
this.setState({ obj: newObj })

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

虚拟 DOM 和 Diffing 算法

  • 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中的key有什么作用?(key的内部原理是什么?)

为什么遍历列表时,key最好不要用index?

一. 虚拟DOM中key的作用:

  1. 简单的说: key是虚拟DOM对象的标识, 在更新显示时key起着极其重要的作用。
  2. 详细的说: 当状态中的数据发生变化时,react会根据【新数据】生成【新的虚拟DOM】,随后React进行【新虚拟DOM】与【旧虚拟DOM】的diff比较,比较规则如下:
    • a. 旧虚拟DOM中找到了与新虚拟DOM相同的key:
      1. 若虚拟DOM中内容没变, 直接使用之前的真实DOM
      2. 若虚拟DOM中内容变了, 则生成新的真实DOM,随后替换掉页面中之前的真实DOM
    • b. 旧虚拟DOM中未找到与新虚拟DOM相同的key根据数据创建新的真实DOM,随后渲染到到页面

二. 用index作为key可能会引发的问题:

  1. 若对数据进行:逆序添加、逆序删除等破坏顺序操作:会产生没有必要的真实DOM更新 ==> 界面效果没问题, 但效率低。
  2. 如果结构中还包含输入类的DOM:会产生错误DOM更新 ==> 界面有问题。
  3. 注意!如果不存在对数据的逆序添加、逆序删除等破坏顺序操作,仅用于渲染列表用于展示,使用index作为key是没有问题的。

三. 开发中如何选择key?:

  1. 最好使用每条数据的唯一标识作为key, 比如id、手机号、身份证号、学号等唯一值。
  2. 如果确定只是简单的展示数据,用index也是可以的

评论