不懂就要问,浏览器到底是什么时候更新dom?

求大佬拯救我
相信很多人看过这段话:
浏览器内核是多线程的,其中一个常驻线程叫javascript引擎线程,负责执行js代码,还有一个常驻线程叫GUI渲染线程,负责页面渲染,dom重画等操作。javascript引擎是基于事件驱动单线程执行的,js线程一直在等待着任务列表中的任务到来,而js线程与gui渲染线程是互斥的,当js线程执行时,渲染线程呈挂起状态,只有当js线程空闲时渲染线程才会执行。

下面有我的三段测试代码:

document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML) 
var time = new Date().valueOf()
while(new Date().valueOf() - time < 2000) {}
// console是最新的随机数,页面2秒后更新
document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML)
Promise.resolve().then(()=>{
    var time = new Date().valueOf()
    while(new Date().valueOf() - time < 2000) {}
})
// console是最新的随机数,页面2秒后更新
document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML)
setTimeout(()=>{
    var time = new Date().valueOf()
    while(new Date().valueOf() - time < 2000) {}       
}, 1)
// console是最新的随机数,页面2秒后更新
document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML)
setTimeout(()=>{
    var time = new Date().valueOf()
    while(new Date().valueOf() - time < 2000) {}       
}, 100)
// console是最新的随机数,页面立刻更新

我的问题是:
dom什么时候更新?
同步代码和微任务执行完更新?好像不是。
js线程空闲的时候更新?好像是,那么什么时候算是js线程空闲?

阅读 5k
3 个回答

简单的讲一下吧,说深了可能是一个大长篇。

其实这个很好理解,推荐先学习一下 requestAnimationFrame 这个API

其根本原因在于浏览器屏幕有一个相对稳定的刷新频率,理想情况是每秒60次。也就是 1000/60,大概16.66ms屏幕会刷新一次

根据浏览器的刷新屏幕,再对照上述代码 和 js线程 Gui线程理论 是不是马上就理解了呢

你的前三种情况,无论是同步还是微任务还是宏任务触发,因为都卡在了浏览器 绘制频率的时间内,后续都被js线程占有,停下了Gui绘制,所以结果是一样。 最后一种情况定时器的宏任务执行时间 足够浏览器屏幕更新大概五六次了,所以屏幕UI早就更新完毕了,后续才执行的 setTimeout宏任务

eventloop.png

DOM结构的修改是同步完成的,dom的实际效果修改或者说渲染是异步的,个人认为是同步js代码执行完毕和异步队列执行之前的中间段进行更新

document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML) 
var time = new Date().valueOf()
while(new Date().valueOf() - time < 2000) {}

这个的测试结果,调用栈空了在执行dom update,在下图的5500ms分界线。
image.png
这时候你会有个疑问说,为什么console.log先输出了。首先document.querySelector('body').innerHTML = Math.random()
console.log(document.querySelector('body').innerHTML) 你调用这个的时候,dom确实是已经改变了(我觉得可以简单理解为,dom对象数据已经改变,所以可以读出来),但是dom的改变不是说dom已经更新在界面了,这是两个概念,dom更新到页面上是要经过ui render之后;这个是render是在5500ms处,就是说等你的调用栈空了在去render的。因为js线程与gui渲染线程是互斥的
其他的你可以在chrome performance自己慢慢验证,

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏