vue之nextTick全面解析


Posted in Javascript onMay 17, 2017

简介

vue是非常流行的框架,他结合了angular和react的优点,从而形成了一个轻量级的易上手的具有双向数据绑定特性的mvvm框架。本人比较喜欢用之。在我们用vue时,我们经常用到一个方法是this.$nextTick,相信你也用过。我常用的场景是在进行获取数据后,需要对新视图进行下一步操作或者其他操作时,发现获取不到dom。因为赋值操作只完成了数据模型的改变并没有完成视图更新。在这个时候我们需要用到本章介绍的函数。

为什么要用nextTick

请看如下一段代码

new Vue({
 el: '#app',
 data: {
  list: []
 },
 mounted: function () {
  this.get()
 },
 methods: {
  get: function () {
   this.$http.get('/api/article').then(function (res) {
    this.list = res.data.data.list
    // ref list 引用了ul元素,我想把第一个li颜色变为红色
    this.$refs.list.getElementsByTagName('li')[0].style.color = 'red'
   })
  },
 }
})

我在获取到数据后赋值给数据模型中list属性,然后我想引用ul元素找到第一个li把它的颜色变为红色,但是事实上,这个要报错了,我们知道,在执行这句话时,ul下面并没有li,也就是说刚刚进行的赋值操作,当前并没有引起视图层的更新。因此,在这样的情况下,vue给我们提供了$nextTick方法,如果我们想对未来更新后的视图进行操作,我们只需要把要执行的函数传递给this.$nextTick方法,vue就会给我们做这个工作。

源码解读

这个函数很简单,vue2.2.6版本 450行开始。

首先,这个函数是采用了一个单利模式还是什么创建的一个闭包函数

var callbacks = [];  // 缓存函数的数组
var pending = false; // 是否正在执行
var timerFunc; // 保存着要执行的函数

首先定义了一些变量供之后使用,下面是一个函数

function nextTickHandler () {
 pending = false;
 // 拷贝出函数数组副本
 var copies = callbacks.slice(0);
 // 把函数数组清空
 callbacks.length = 0;
 // 依次执行函数
 for (var i = 0; i < copies.length; i++) {
  copies[i]();
 }
}

这个函数就是$nextTick内实际调用的函数。

接下来,是vue分了三种情况来延迟调用以上这个函数,因为$nextTick目的就是把传进来的函数延迟到dom更新后再使用,所以这里依次优雅降序的使用js的方法来做到这一点。

1. promise.then延迟调用

if (typeof Promise !== 'undefined' && isNative(Promise)) {
 var p = Promise.resolve();
 var logError = function (err) { console.error(err); };
 timerFunc = function () {
  p.then(nextTickHandler).catch(logError);
  if (isIOS) { setTimeout(noop); }
 };
}

如果浏览器支持Promise,那么就用Promise.then的方式来延迟函数调用,Promise.then方法可以将函数延迟到当前函数调用栈最末端,也就是函数调用栈最后调用该函数。从而做到延迟。

2. MutationObserver 监听变化

else if (typeof MutationObserver !== 'undefined' && (
 isNative(MutationObserver) ||
 MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {

 var counter = 1;
 var observer = new MutationObserver(nextTickHandler);
 var textNode = document.createTextNode(String(counter));
 observer.observe(textNode, {
  characterData: true
 });
 timerFunc = function () {
  counter = (counter + 1) % 2;
  textNode.data = String(counter);
 };
}

MutationObserver是h5新加的一个功能,其功能是监听dom节点的变动,在所有dom变动完成后,执行回调函数。

具体有一下几点变动的监听

  1. childList:子元素的变动
  2. attributes:属性的变动
  3. characterData:节点内容或节点文本的变动
  4. subtree:所有下属节点(包括子节点和子节点的子节点)的变动

可以看出,以上代码是创建了一个文本节点,来改变文本节点的内容来触发的变动,因为我们在数据模型更新后,将会引起dom节点重新渲染,所以,我们加了这样一个变动监听,用一个文本节点的变动触发监听,等所有dom渲染完后,执行函数,达到我们延迟的效果。

3.setTimeout延迟器

else {
  timerFunc = function () {
   setTimeout(nextTickHandler, 0);
  };
 }

利用setTimeout的延迟原理,setTimeout(func, 0)会将func函数延迟到下一次函数调用栈的开始,也就是当前函数执行完毕后再执行该函数,因此完成了延迟功能。

闭包函数

return function queueNextTick (cb, ctx) {
  var _resolve;
  callbacks.push(function () {
   if (cb) { cb.call(ctx); }
   if (_resolve) { _resolve(ctx); }
  });
  // 如果没有函数队列在执行才执行
  if (!pending) {
   pending = true;
   timerFunc();
  }
  // promise化
  if (!cb && typeof Promise !== 'undefined') {
   console.log('进来了')
   return new Promise(function (resolve) {
    _resolve = resolve;
   })
  }
 }

这个return的函数就是我们实际使用的闭包函数,每一次添加函数,都会想callbacks这个函数数组入栈。然后监听当前是否正在执行,如果没有,执行函数。这个很好理解。下面一个if是promise化。

this.$nextTick(function () {

})
// promise化
this.$nextTick().then(function () {

}.bind(this))

以上代码中第二种写法我们不常见把,直接调用$nextTick函数然后用promise格式去书写代码,不过这个then里面需要手动绑定this,vue内部没有给做处理。

结尾

这就是一个this.$nextTick的实现,其中利用了优雅降序的巧妙手法,使代码尽可能优化。而且还提供了promise的写法,虽然我们不经常用,但是有总比没有好。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
javascript Zifa FormValid 0.1表单验证 代码打包下载
Jun 08 Javascript
JS实现图片横向滚动效果示例代码
Sep 04 Javascript
JSF中confirm弹出框的用法示例介绍
Jan 07 Javascript
javascript框架设计读书笔记之种子模块
Dec 02 Javascript
jQuery+HTML5美女瀑布流布局实现方法
Sep 21 Javascript
jQuery如何封装输入框插件
Aug 19 Javascript
socket.io学习教程之基础介绍(一)
Apr 29 Javascript
js脚本编写简单刷票投票系统
Jun 27 Javascript
浅谈VUE监听窗口变化事件的问题
Feb 24 Javascript
原生js实现公告滚动效果
Jan 10 Javascript
深入了解JavaScript词法作用域
Jul 29 Javascript
JS如何判断对象是否包含某个属性
Aug 29 Javascript
Vue.js学习教程之列表渲染详解
May 17 #Javascript
JavaScript运动框架 解决速度正负取整问题(一)
May 17 #Javascript
深入理解Commonjs规范及Node模块实现
May 17 #Javascript
JavaScript限制在客户区可见范围的拖拽(解决scrollLeft和scrollTop的问题)(2)
May 17 #Javascript
vue学习笔记之vue1.0和vue2.0的区别介绍
May 17 #Javascript
Angular.JS中的this指向详解
May 17 #Javascript
websocket+node.js实现实时聊天系统问题咨询
May 17 #Javascript
You might like
WML,Apache,和 PHP 的介绍
2006/10/09 PHP
PHP读取RSS(Feed)简单实例
2014/06/12 PHP
PHP智能识别收货地址信息实例
2019/01/05 PHP
PHP 范围解析操作符(::)用法分析【访问静态成员和类常量】
2020/04/14 PHP
js form action动态修改方法
2008/11/04 Javascript
JavaScript 开发中规范性的一点感想
2009/06/23 Javascript
JS判断客服QQ号在线还是离线状态的方法
2015/01/13 Javascript
javascript中返回顶部按钮的实现
2015/05/05 Javascript
基于JS代码实现简单易用的倒计时 x 天 x 时 x 分 x 秒效果
2017/07/13 Javascript
关于axios不能使用Vue.use()浅析
2018/01/12 Javascript
vue-devtools的安装步骤
2018/04/23 Javascript
微信小程序之onLaunch与onload异步问题详解
2019/03/28 Javascript
vue.js高德地图实现热点图代码实例
2019/04/18 Javascript
JavaScript相等运算符的九条规则示例详解
2019/10/20 Javascript
[01:07:11]Secret vs Newbee 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/17 DOTA
Python比较文件夹比另一同名文件夹多出的文件并复制出来的方法
2015/03/05 Python
通过代码实例展示Python中列表生成式的用法
2015/03/31 Python
Python开发之快速搭建自动回复微信公众号功能
2016/04/22 Python
python脚本爬取字体文件的实现方法
2017/04/29 Python
对python中的xlsxwriter库简单分析
2018/05/04 Python
使用python实现mqtt的发布和订阅
2019/05/05 Python
详解python程序中的多任务
2020/09/16 Python
python更新数据库中某个字段的数据(方法详解)
2020/11/18 Python
python使用smtplib模块发送邮件
2020/12/17 Python
Python机器学习工具scikit-learn的使用笔记
2021/01/28 Python
常用的四种CSS透明属性介绍
2014/04/12 HTML / CSS
TripAdvisor德国:全球领先的旅游网站
2017/12/07 全球购物
什么是三层交换,说说和路由的区别在那里
2014/09/01 面试题
小学德育工作经验交流材料
2014/05/22 职场文书
科级干部群众路线教育实践活动个人对照检查材料
2014/09/19 职场文书
2014年接待工作总结
2014/11/26 职场文书
在职证明书模板
2015/06/15 职场文书
警示教育片观后感
2015/06/17 职场文书
2015年电气技术员工作总结
2015/07/24 职场文书
2016年“12.3”国际残疾人日活动总结
2016/04/01 职场文书
Redis 哨兵集群的实现
2021/06/18 Redis