博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
VueJS源码学习——MutationObserver实现nextTick
阅读量:5732 次
发布时间:2019-06-18

本文共 3224 字,大约阅读时间需要 10 分钟。

Vue 倡导开发者尽量不直接操作 DOM,但有的时候由于各种需求让开发者不得不这样做,于是 nextTick 的实现就是让开发者在修改数据后,能够在数据更新到 DOM 后才执行对应的函数,从而获取最新的 DON 数据。

那么如何实现 nextTick呢,我们首先可以想到的是利用 setTimeout 的异步回调来实现,不过由于各个浏览器的不同,setTimeout 的延迟很高,因此在 nextTick 中只作为最后的备胎,首选的方案则是 MutationObserver(在后面的内容中 MO 代表 MutationObserver)

nextTick 的源码实现

export const nextTick = (function () {  var callbacks = []  var pending = false  var timerFunc  function nextTickHandler () {    pending = false    var copies = callbacks.slice(0)    callbacks = []    for (var i = 0; i < copies.length; i++) {      copies[i]()    }  }  /* istanbul ignore if */  if (typeof MutationObserver !== 'undefined') { // 首选 MutationObserver     var counter = 1    var observer = new MutationObserver(nextTickHandler) // 声明 MO 和回调函数    var textNode = document.createTextNode(counter)    observer.observe(textNode, { // 监听 textNode 这个文本节点      characterData: true // 一旦文本改变则触发回调函数 nextTickHandler    })    timerFunc = function () {      counter = (counter + 1) % 2 // 每次执行 timeFunc 都会让文本在 1 和 0 间切换      textNode.data = counter    }  } else {    timerFunc = setTimeout // 如果不支持 MutationObserver, 退选 setTimeout  }  return function (cb, ctx) {    var func = ctx      ? function () { cb.call(ctx) }      : cb    callbacks.push(func)    if (pending) return    pending = true    timerFunc(nextTickHandler, 0)  }})()

MutationObserver 的功能和作用

MO 给开发者提供了一种能在某个范围内的DOM数发生变化时作出适当反应的能力 ——

用人话说是开发者能通过它创建一个观察者对象,这个对象会监听某个DOM元素,并在它的DOM树发生变化时执行我们提供的回调函数。

具体参考这个

比较特别的是实例化的时候需要先传入回调函数:

var observer = new MutationObserver(function(mutations) {  mutations.forEach(function(mutation) {    console.log(mutation.type);  })})

然后才配置观察选项,包括观察节点和观察的属性:

// 选择目标节点var target = document.querySelector('#some-id'); // 配置观察选项:var config = { attributes: true, childList: true, characterData: true } // 传入目标节点和观察选项observer.observe(target, config); // 随后,你还可以停止观察observer.disconnect();

对于老版本的谷歌和火狐,则需要使用带前缀的 MO:

var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver

MutationObserver 和 microtask

那么为什么优选使用 MutationObserver呢?

一开始以为是 MO 就是用来监听 DOM 变化的,那么使用 textnode 模拟 DOM 变化再利用 MO 来监听触发从而实现 nextTick 不就很适合,直到了解看到了知乎上的才知道是因为 MO 会比 setTimeout 早执行的缘故,

这里需要了解JS的运行运行机制(重新刷新了我的三观), JS 的事件运行机制执行的时候会区分 taskmicrotask, 引擎在每个 task 执行完毕,并在从队列里取下一个task来执行之前, 执行完所有的 microtask 队列中的 microtask

(task 和 microtask 摘自

setTimeout 回调会被分配到一个新的task中等待执行,而 Promise 的 resolver、MO 的 回调都会被分配到 microtask 的队列中,所以会比 setTimout 先执行

除了比 setTimout 快之外,还有 渲染性能 的问题,根据, 每个 task 运行完以后, UI 都会重新渲染,那么在 microtask 中就完成数据更新, 当前 task 结束就可以得到最新的 UI, 反之如果新建一个 task 来做数据更新,那么渲染就会进行两次。

所以性价比如此高的 MO 自然成为了首选

关于 microtask,具体可以阅读 Jake 写的

nextTick 的版本迭代

上面关于 nextTick 的源码实现属于 vue 最早的版本 v1.0.9,在深挖 mutationObserver 的时候发现 nextTick 在vue的版本迭代中也在不断的进化,同事也发生过退化,非常有趣:

先说说退化的事件,尤大(vue的作者)曾经使用 window.postMessage 来替代 MO 实现 nextTick,结果开发者使用后发现了问题,可以看看这两个 JSFiddle: 和 , 两个例子用了不同版本来实现元素的绝对定位,第一个使用的是 2.0.0-rc6,这个版本采用的是 MO,而后来因为 IOS 9.3 的 WebView 里 MO 有 bug,尤大便换成 window.postMessage来实现,即第二个实例版本为 2.0.0-rc7, 但是由于 postMessage 会将回调放到 macrotask 其实也就是 task 里面,导致可能执行了多次 UI 的task都没有执行 window.postMessage 的 task,也就延迟了更新DOM操作的时间。尤大在后续版本撤回了这一次修改,具体的讨论可以看

关于进化,在后续的版本里,由于 es6 的新语法,nextTick 开始使用 Promise.then 和 MO 来做首选和次选,在前面的讨论中已经提到,Promise.then 也属于 microtask。

资源

转载地址:http://kfmwx.baihongyu.com/

你可能感兴趣的文章
Linux下MongoDB安装与配置
查看>>
DSL配置(PPPOA)
查看>>
WEBRTC执行流程
查看>>
Spring Boot 入门系列
查看>>
Spring Cloud版——电影售票系统<六>使用 Spring Cloud Config 统一管理微服务配置
查看>>
Java not support java EE1.3
查看>>
iptables规则备份及恢复、firewalld九个zone,service的操作
查看>>
www.conf配置文件的参数详解
查看>>
如何实现邀请好友帮抢票功能?
查看>>
深圳联通特邀湖北籍企业参观公司总部大楼举行
查看>>
告警系统主脚本、告警系统配置文件、告警系统监控项目
查看>>
Python 和 PyCharm 在 windows10 环境的安装和设置
查看>>
C语言入门基础之数组——数学和编程的完美结合(图)
查看>>
《远见》的读后感作文1000字范文
查看>>
重置密码、单用户模式、救援模式
查看>>
LAMP环境搭建1-mysql5.5
查看>>
第三课 Linux目录及文件管理、用户组管理及bash重定向
查看>>
shell 脚本攻略--小试牛刀
查看>>
spring boot view override
查看>>
bzoj 2282: [Sdoi2011]消防
查看>>