首先明确一下 dom 更新的丝抽概念。
浏览器中有一个 js 引擎线程执行我们的茧响 js 代码,同时还有一个 GUI 渲染线程来进行绘图,应式并且两个线程是系统互斥的,只能交替着进行。丝抽
而dom 更新是茧响在 js 线程中进行的,因此 dom 更新了并不代表我们就一定可以看到,应式只有当渲染线程把更新的系统 dom 绘制完毕我们才会看到。
简单理解就是丝抽下边的样子:

举一个极端的例子,如果我们在 js 线程里修改了 dom ,茧响但某种原因使得 js 线程一直在执行,应式没有轮到渲染线程,系统那么我们就永远看不到更新后 dom 了。丝抽
html 引入 bundle.js 。茧响
bundle.js 首先修改 dom ,应式然后执行一个死循环。
document.getElementById("root").innerText = "hello";
while (true) {}此时页面就永远是空白了。但事实上我们的 dom 已经更新了,只是没有轮到渲染线程展示出来。
只更新最后一次结果在 js 线程中如果修改同一个 dom 元素,无论修改多少次,亿华云最终轮到渲染线程的时候,渲染线程当前读到的 dom 是啥就会是啥。
document.getElementById("root").innerText = "hello";
document.getElementById("root").innerText = "hello2";
document.getElementById("root").innerText = "hello3";
document.getElementById("root").innerText = "liang";上边 dom 变化了多次,但屏幕上只会看到 liang。
宏任务微任务任务队列这里简单说一下,不细讲了。
宏任务生成方式:script 标签, setTimeout, setInterval 等微任务生成方式:Promise, MutationObserver 等。js 线程中,通过 <script> 执行代码,也就是开始执行第一个宏任务,执行过程中新生成的宏任务丢到任务队列,新生成的微任务丢到微任务队列。
当前宏任务执行结束后,开始执行微任务队列,直到微任务队列执行完毕。
js 线程退出来,开始执行渲染线程。
渲染线程执行完毕后,然后又回到 js 线程,去任务队列中取一个宏任务,重复上边的过程。

让 dom 更新多次document.getElementById("root").innerText = "hello";
document.getElementById("root").innerText = "hello2";
document.getElementById("root").innerText = "hello3";
document.getElementById("root").innerText = "liang";这个例子中渲染的时候只会执行第一次 dom ,但如果我们通过 setTimeout 产生一个宏任务,这样就会看到会先后渲染了。香港云服务器
document.getElementById("root").innerText = "hello";
setTimeout(() => {
document.getElementById("root").innerText = "hello2";
setTimeout(() => {
document.getElementById("root").innerText = "hello3";
setTimeout(() => {
document.getElementById("root").innerText = "liang";
}, 1000);
}, 1000);
}, 1000);
回到我们的响应式系统中。
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello",
};
observe(data);
const updateComponent = () => {
document.getElementById("root").innerText = data.text;
};
new Watcher(updateComponent);
data.text = "liang";data.text="liang" 触发 Wathcer 更新的时候,并不会立即更新,而是放到 Wathcer 队列中,在 setTimeout 中执行,代码如下。
export function queueWatcher(watcher) {
const id = watcher.id;
if (has[id] == null) {
has[id] = true;
queue.push(watcher);
// queue the flush
if (!waiting) {
waiting = true;
setTimeout(flushSchedulerQueue, 0);
}
}
}再结合这张图:

第 1 次宏任务将 dom 更新为 hello ,然后执行第一次的渲染任务。
第 2 次宏任务是将第 1 次宏任务中的 setTimeout 取出进行执行,然后将 dom 更新为 liang ,执行渲染任务。
所以页面应该先是 hello 后是 liang 。
但运行上边的程序发现并不是这样,页面只看到了 liang ,没有看到 hello 。
没有研究过 Chrome 的代码,这里不负责任的猜想一下,有问题欢迎讨论。
渲染线程不是像上边图中一样每次都接到 js 进程后边,相反渲染线程可以看做在间隔执行,IT技术网比如每 10ms 执行一次,如果渲染线程准备执行的时候 js 线程还在执行就等待。
但如果第一次宏任务、微任务执行完毕后,时间小于了 10ms ,此时渲染线程还没有准备执行,所以 js 线程就直接去执行第二次宏任务了。
因此,我们可以强行增加第一次宏任务执行的时间,确保 js 线程执行完以后会去执行渲染线程。
import { observe } from "./reactive";
import Watcher from "./watcher";
const data = {
text: "hello",
};
observe(data);
const updateComponent = () => {
/****强行增加耗时
X.Org server 1.5Ubuntu 8.10中装载了最新版本的X.Org,带来了对热插拨设备的更好支持。基本上,它能使得大多数用户不用”/etc/X11/xorg.conf”文件就能正常运行。Linux kernel 2.6.26Alpha 4包含了基于2.6.26.2的内核版本。32位下载地址:http://cdimage.ubuntu.com/releases/intrepid/alpha-4/intrepid-desktop-i386.iso64位下载地址:http://cdimage.ubuntu.com/releases/intrepid/alpha-4/intrepid-desktop-amd64.iso
什么技能产品经理不会提,但技术人必须懂?
创建软件架构时应该关注什么?
Golang应付百万级请求/分钟