React-原理

17次阅读
没有评论

共计 3713 个字符,预计需要花费 10 分钟才能阅读完成。

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 元素是一个对象,用来描述你希望再屏幕上看到的内容
    React-原理

组件更新机制

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

组件性能优化

减轻 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)
    React-原理

执行过程

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

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也是可以的

正文完
 0
qiaofugui.cn
版权声明:本站原创文章,由 qiaofugui.cn 于2024-05-21发表,共计3713字。
转载说明:除特殊说明外本站文章皆由CC-4.0协议发布,转载请注明出处。
评论(没有评论)
验证码