你理解错误的Vue nextTick

4,961 阅读4分钟

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

根据上图

  1. 我们看到nextTick的几种写法:
    1. this.$nextTick(cb)
    2. this.$nextTick().then(cb)
  1. 所有的cb都会被放入到callbacks数组里,等待一次性调用

上图中我们看到了主要是由timerFunc这个函数来进行调用回调, 那么我们下面来着重介绍这个函数,首先看下源码

我们可以看到timerFunc在不同情况下不同的赋值情况

  1. 首先会判断浏览器是否支持promise属性, 如果支持, timerFunc就会被赋值成Promise, 这里有个小小的问题,那就是在ios下,虽然是具备Pormise对象,并且会将它推入到microTask队列里,但是队列却不会更新,这个时候需要添加一个macroTask来强制刷新microTask队列

  2. MutationObserver, 相信很多人并不清楚这个Api, 这是一个能够监听DOM变化的API,并且属于microTask, 优先级低于Promise 在创建一个新的文本节点后,手动更改其文本节点来触发microTask,

    这里会有个小小的问题:

    该文本节点渲染成功后,一定能代表其他的DOM渲染成功了吗?

    这是个备选方案, 主要还是因为它是一个微任务,所以才使用它,并不是因为它监听了DOM

  3. 微任务都失败后, 退而求其次,选择setImmediate, 这是一个只有高版本IE和Edge浏览器才可能拥有的API, 其主要是用于计算大量数据的时候使用

  4. 最后就是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

3Q!