简单的了解下 Vue nextTick 获取更新后 DOM 的原理
Vue nexTick
使用过Vue的小伙伴们都知道,Vue里的nextTick可以获取到更新后的DOM, 今天我就来讲解下nextTick里面究竟做了什么?
开始讲解前,我们需要知道了解一个概念,那就是Event Loop
Event Loop
Event Loop翻译过来就是事件循环, 一个Event Loop会包括一个或多个task队列,持续线程会从队列中取出最早进入队列的任务进行执行,被取出的任务就叫做macroTask(宏任务), 每个macroTask都有一个任务源, 每个macroTask处理完之后就从队列中取出下一个时间最早进入的macroTask再重执行
任务源:
```
1. script
2. 事件
3. Dom交互
4. I/O
5. UI Render
6. setTimeout
7. setInterval
8. requestAnimationFrame
.....
```
也就是说碰到以上几种情况就会产生一个macroTask并且推入到队列中
miscroTask(微任务)
执行完每个macroTask之后,主线程会去检查该macroTask
下的microTask是否为空,如果不为空,则按照时间顺序从早到晚取出,如果途中有遇到新的microTask, 那么会继续将该microTask推入到microTask队列里
UI Render(重点)
伴随着miscroTask队列的清空,主线程就会执行UI Render, 也就是渲染界面,但是浏览器它并不会每次在UI Render任务下一定会渲染界面,视情况而言,现在主流浏览器一般都是按照60HZ 也就是16.7ms刷新频率进行渲染(不是精确估量),一个macroTask通常是小于16.7ms, 所以浏览器每次会根据情况进行渲染
总结下一个循环
1. 从macroTask队列里取出最早添加进去的
2. 开始执行task, 途中如果遇到新的macroTask,就会将其添加到macroTask队列的最后面
3. 执行完macroTask之后,event loop会去寻找microTask队列
4. 同样的道理,如果途中遇到新的microTask,将其放入该macroTask下的microTask队列最后面
5. 执行完microTask,会执行UI Render macroTask
6. 浏览器会根据现有情况决定是否更新DOM,通常是按照60HZ的频率去更新
7. 至此,一个event loop结束了
nextTick
我们开始分析nextTick根据上图
我们看到nextTick的几种写法:
1. this.$nextTick(cb)
2. this.$nextTick().then(cb)
所有的cb都会被放入到callbacks数组里,等待一次性调用
上图中我们看到了主要是由timerFunc这个函数来进行调用回调, 那么我们下面来着重介绍这个函数,首先看下源码
我们可以看到timerFunc在不同情况下不同的赋值情况
首先会判断浏览器是否支持promise属性, 如果支持, timerFunc就会被赋值成Promise, 这里有个小小的问题,那就是在ios下,虽然是具备Pormise对象,并且会将它推入到microTask队列里,但是队列却不会更新,这个时候需要添加一个macroTask来强制刷新microTask队列
MutationObserver, 相信很多人并不清楚这个Api, 这是一个能够监听DOM变化的API,并且属于microTask, 优先级低于Promise 在创建一个新的文本节点后,手动更改其文本节点来触发microTask,
这里会有个小小的问题:
该文本节点渲染成功后,一定能代表其他的DOM渲染成功了吗?
这是个备选方案, 主要还是因为它是一个微任务,所以才使用它,并不是因为它监听了DOM
微任务都失败后, 退而求其次,选择setImmediate, 这是一个只有高版本IE和Edge浏览器才可能拥有的API, 其主要是用于计算大量数据的时候使用
最后就是setTimeout
看到这里,你会不会有疑惑?
上面的代码并没有说明nextTick是在监听DOM更新后才执行的?What????当时脑袋就duang了一下
那么接下来说的就是重中之重
DOM Tree的更新是实时的,DOM Tree的更新是实时的,DOM Tree的更新是实时的, 重要的事说3遍, 这意味着你无需去监听DOM 更新, 你对DOM的操作是能够实时得到反馈的,上一行代码操作了DOM,下一行就能获取到
那么有人就会产生疑惑了, nextTick究竟是干嘛的?
nextTick的作用是将收集Watcher从队列中一个个取出,并且更改数据,来一次性渲染DOM, 我们知道操作DOM的代价是昂贵的, 浏览器打开一个网页后会开启一个进程,进程是由线程组成的, 那么在打开的同时,
1. GUI渲染线程
2. js引擎线程(主线程)
3. EventLoop轮训处理线程
4. 其他线程,例如网络
跨线程操作代价是昂贵的,所以做到一次性渲染Dom,可以有效的优化性能!!
总结
nextTick并不是用来监听DOM变更,因为DOM变更是能够实时获取到的,它的作用是一次性更改数据,并且渲染DOM。