为什么要学习源码呢?每个人都有不同的看法,这就好比开车一样,如果你只会开车不会修理的话,那么车开到一半怎么办?所以想要使用好React,或多或少都得了解一些React的源码。毕竟靠人不如靠自己!自己动手丰衣足食!!
本片文章内容比较简单,为什么呢?如果我把源码直接拿出来一步一步的分析,很多人会一头雾水,搞不懂在干什么,所以干脆站的高一点,主要讲一下两个框架在更新状态时的原理对比。只有先懂得了状态更新的原理,看源码才不会很吃力。
React基本组成
这里我们必须明白React的组成,React是由 调度器, 协调器, 渲染器 这三大部分组成。
-
调度器: 调度器可以理解成控制室,每次更新根据优先级来调度更新,高优先级的更新可以打断低优先级的更新。
-
协调器: 协调器就是我们经常提到的Diff算法的部分,这个部分主要的作用就是 对比currentFiber和JSX对象的差异生成 workInProgressFiber。 然后由渲染器渲染。
-
渲染器: 渲染器就很好理解了,主要的作用就是将生成好的 workInProgressFiber 渲染到页面。
基本的运行逻辑为: 状态的更新经由 控制器根据优先级来调度,高优先级的更新可以打断低优先级的更新,然后经由协调器进行Diff生成workInProgressFiber,再由渲染器进行渲染。
React状态更新
经过了基本组成和运行逻辑的介绍,现在我们进入正题,看看状态跟新具体做了什么事情!
基本代码:
import React from "react";
const App = () => {
const [num, setNum] = React.useState(1);
const handleClickBut = () => {
setNum((num) => num + 1);
};
React.useEffect(() => {}, [num]);
return (
<div>
{num}
<button onClick={handleClickBut}>增加</button>
</div>
);
};
这个例子很简单,用户点击增加按钮,调用setNum触发状态更新。
状态更新流程
主要流程如下图所示:
关键步骤分析
-
dispatchAction ---> basicStateReducer 阶段
在这个阶段当中,会创建一个新的undate对象和之前的queue.pending形成一个环状链表。然后在根据传入的action计算得出最新的memoizedState。
-
scheduleUpdateOnFiber ---> performSyncWorkOnRoot || performConcurrentWorkOnRoot 阶段
在这个阶段中,React主要进行优先级的调度,根据创建应用的模式不同调用不同模式的 Diff 方法(performSyncWorkOnRoot || performConcurrentWorkOnRoot) Diff逻辑一样,只是在Diff之前会做不同的处理。不要错误理解
-
renderRootComcurrent || renderRootSync ---> commitRoot 阶段
在这个阶段中,React主要进行Diff, 对比currentFiber和JSX对象的差异生成 workInProgressFiber。 然后由渲染器渲染。
-
commitRoot ---> commitLayoutEffects 阶段
在这个阶段, Reac主要进行渲染DOM,并且调用生命周期。
Vue基本组成
对于Vue的基本原理我想我就不用说了吧!官网已经说得很清楚了!!
其实总结下来也就是: 当状态改变Object.defineProperty中的set触发,通知相关的依赖,由Watcher触发对应的组件进行更新,以达到视图更新的目的。
Vue状态更新
基本代码:
<template>
<div>
{{ num }}
<button @click="num++">增加</button>
</div>
</template>
<script>
export default {
data() {
return {
num: 0,
};
},
};
</script>
点击按钮触发num加1Z,状态改变触发更新流程。
状态更新流程
主要的流程如图所示:
关键步骤分析
-
proxySetter ---> dep.notify() 阶段
在这个阶段,Vue主要进行了 状态的改变和根据依赖通知更新。
-
subs[i].update() ---> watcher.run() 阶段
在这个阶段,Vue主要进行了由Watcher监听到了改变,然后更新视图组件的过程。
-
updateComponent ---> updated
在这个阶段,Vue主要进行了Diff,并且将最终的DOM渲染到视图中。并且调用更新时的生命周期函数。