数据驱动视图是什么?(单向数据流)
传统的jquery
开发是通过操作dom
来实现页面的渲染和交互,而React
采用的是数据驱动视图的方式:view
是基于数据来渲染的,数据一旦变化,view
就会自动更新。因此我们在开发时,只需要关注数据即可,不用直接操作dom。
注意:React
不是响应式设计,因为它需要通过this.setState
、useState
等方式手动触发数据变化。
一、数据驱动视图原理
class Calculator extends React.Component {
constructor(props) {
super(props);
this.state = {val: 0};
}
render() {
return(
<div className="container">
<button onClick={ () => {
this.setState({val: 1})
}}>Submit!</button>
</div>
)
}
}
React
通过setState
实现数据驱动视图,通过setState
来引发一次组件的更新过程从而实现页面的重新渲染(除非shouldComponentUpdate返回false)。假设我们点击button
触发了onClick事件
,然后它对应的监听函数调用了this.setState()
来改变数据,之后的操作过程为:
1. setState:dirtyComponent => 批量更新
pending
:当前所有等待更新的state
队列。isBatchingUpdates
:React
中用于标识当前是否处理批量更新状态,默认false
。dirtyComponent
:当前所有待更新state
的组件队列。- 将
pending
队列中的state
进行合并,得到最终要更新的state,并将pending
队列置为空。(该方法的执行时机不确定,应该在事务结束后?)
处理过程:
setState()
首先将接收的第一个参数state
存储在pending队列
中;(state)- 判断当前
React
是否处于批量更新状态,是的话就将需要更新state的组件添加到dirtyComponents
中;(组件) - 不是的话,它会遍历
dirtyComponents
的所有组件,调用updateComponent
方法更新每个dirty
组件(开启批量更新事务),每个组件有:
2. 执行更新阶段的生命周期 => dirty组件的更新
调用setState
会默认调用组件更新阶段的5个生命周期,依次是:
- 执行生命周期
getDerivedStateFromProps
- 执行生命周期
shouldComponentUpdate
,根据返回值判断是否要继续更新。 - 执行生命周期
render
:执行真正的更新。 - 执行生命周期
getSnapshotBeforeUpdate
- 执行生命周期
componentDidUpdate
3. render:重新构建虚拟dom树,执行 diff 并更新到真实dom => view更新
我们知道在React
的生命周期里,无论是挂载还是更新阶段,在render
之前的生命周期函数都不会更新this.state
和props
,直到render
执行完成后,数据才会更新。(只有shouldComponentUpdate
返回false
时例外,此时会中断更新过程,但依然会更新this.state
)
3.1 虚拟dom树:dirty组件的组件树
jsx
通过babel
转换成React.createElement
;createElement()
函数返回了一个了ReactElement
函数;ReactElement
函数返回的对象就是虚拟dom(Fiber 链表结构);Fiber
节点通过return、child、sibling
属性连接成Fiber Tree
。
当state或props改变时,会再次调用render生成一个虚拟dom树。
3.2 diff:批量更新,使虚拟dom和真实dom保持同步
比较新旧两个虚拟dom树,生成一个补丁,最后批量把补丁更新到真实dom上。
它会把收集到的多个补丁集暂存到队列中,最终实现集中的dom批量更新。
协调Reconciliation
过程是将虚拟dom和真实dom保持同步,它包含diff
算法,但它加了一些启发规则。