Vue响应式原理详解


Posted in Javascript onApril 18, 2017

Vue 嘴显著的特性之一便是响应式系统(reactivity system),模型层(model)只是普通JavaScript对象,修改它则更新视图(view)。

Vue 响应式系统的底层细节

如何追踪变化

把一个普通的JavaScript对象传给Vue实例的data选项,Vue将遍历此对象的所有属性,并使用Object.defineProperty 把这些属性全部转为 getter/setter.Object.defineProperty是仅ES5支持,并无法shim的特性,这也就是为什么Vue不支持IE8以及更低版本浏览器的原因。

用户看不到getter/setter,但是在内部他们让Vue跟踪依赖,在属性被访问和修改时通知变化。这里需要注意的问题是浏览器控制台在打印数据对象时 getter/setter 的格式化并不同,所以你可能需要安装 vue-devtools 来获取更加友好的检查接口。

每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖。之后当依赖项的setter 被调用时,会调用 watcher 重新计算,从而致使它关联的组件得以更新。

Vue响应式原理详解

变化检测问题

受现代JavaScript的限制,Vue不能检测到对象属性的添加和删除。由于Vue会在初始化实例时对属性执行getter/setter转化过程,所以属性必须在data对象上存在才能让Vue转化它,这样才能让它是相应的。例如:

var vm=nrew Vue({

el:'#app',


data:{



a:1


}

});

//vm.a是响应的

vm.b=3;//vm.b是非响应的

Vue不允许在已经创建的实例上动态添加新的根级响应式属性(root-lever reactive property).然而它可以使用Vue.set(object,key,value)方法

将响应属性添加到嵌套的对象上:

Vue.set(object.someObject,'b',2);

你哈可以使用vm.$set实例方法,这也是全局Vue.set方法的别名:

this.$set(this.someObject,'b',2)

有时你想向已有对象上添加一些属性,例如使用Object.assign()或_.extend()方法来添加属性。但是,添加到对象上新属性不会触发更新。这这种情况下可以常见一个新的对象,让它包含元对象的属性和新的属性:

//替换 ‘object.assign(this.someObject,{a:1,b:2})'

this.someObject=Object.assign({},this.someObject,{a:1,b:2});

声明响应式属性

由于Vue不允许动态添加根级响应式属性,所以你必须在初始化实例前声明根级响应式属性,哪怕只是一个空值:

var vm = new Vue({
 data: {
 // 声明 message 为一个空值字符串
 message: ''
 },
 template: '<div>{{ message }}</div>'
})
// 之后设置 `message` 
vm.message = 'Hello!'

如果在data选项中未声明message,Vue将警告你渲染函数在视图访问的属性不存在。

这样的限制在背后是有其技术原因的,它消除了在依赖项跟踪系统中一类边界情况,也使Vue实例在类型检查系统的帮助下运行的更高效。而且在代码可维护性方面也有一点重要的考虑: data 对象就像组件状态的概要,提前声明所有额响应式属性,可以让组件代码在以后重新阅读或者其他开发人员阅读时更易于被理解。

异步更新队列

Vue异步执行DOM更新。只要观察到数据变化,Vue将开启一个队列,并缓冲在同一个事件循环中发生的所有数据改变。如果同一个watcher被多次触发,只会一次推入到队列中。这种在缓冲时去除重复数据对于避免不必要的计算和DOM操作上非常重要。然后,在下一个事件循环‘tick'中,Vue刷新队列并执行实际工作。Vue在内部尝试对异步队列使用原生的Promise.then,MutationObserver,如果执行环境不支持,会采用setTimeout(fn,0)代替。

例如,当你设置vm.someData='new value',该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个‘tick'更新。多数情况下我们不需要关心这个过程,但是如果你想在DOM状态更新后做点儿什么,这就可能会有些棘手。虽然Vue.js通常鼓励开发人员沿着‘数据驱动'的方式思考,避免直接接触DOM,但是有时我们确实要这么做。为了在数据变化之后等待Vue完成更新DOM,可以在数据变化之后立即使用Vue.nextTick(callback).这样回调函数在DOM更新完成后就会调用。例如:

<div id="example">{{message}}</div>
var vm = new Vue({
 el: '#example',
 data: {
 message: '123'
 }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
 vm.$el.textContent === 'new message' // true
})

在组件中使用vm.$nextTick()实例方法特别方便,因为它不需要全局Vue,并且回调函数中的 this 将自动绑定到当前的Vue实例上:

Vue.component('example', {
 template: '<span>{{ message }}</span>',
 data: function () {
 return {
  message: 'not updated'
 }
 },
 methods: {
 updateMessage: function () {
  this.message = 'updated'
  console.log(this.$el.textContent) // => '没有更新'
  this.$nextTick(function () {
  console.log(this.$el.textContent) // => '更新完成'
  })
 }
 }
})

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

Javascript 相关文章推荐
js操作ajax返回的json的注意问题!
Feb 23 Javascript
innerHTML 和 getElementsByName 在IE下面的bug 的解决
Apr 09 Javascript
javascript动态加载二
Aug 22 Javascript
解决自定义$(id)的方法与jquery选择器$冲突的问题
Jun 14 Javascript
使用json来定义函数,在里面可以定义多个函数的实现方法
Oct 28 Javascript
javascript判断元素存在和判断元素存在于实时的dom中的方法
Jan 17 Javascript
jQuery中的deferred对象和extend方法详解
May 08 jQuery
Vee-Validate的使用方法详解
Sep 22 Javascript
AngularJS中scope的绑定策略实例分析
Oct 30 Javascript
Three.js加载外部模型的教程详解
Nov 10 Javascript
详解puppeteer使用代理
Dec 27 Javascript
JS立即执行函数功能与用法分析
Jan 15 Javascript
详解vue-router基本使用
Apr 18 #Javascript
Vue键盘事件用法总结
Apr 18 #Javascript
javascript实现日期三级联动下拉框选择菜单
Dec 03 #Javascript
jQuery加密密码到cookie的实现代码
Apr 18 #jQuery
微信小程序实战之自定义模态弹窗(8)
Apr 18 #Javascript
微信小程序开发之麦克风动画 帧动画 放大 淡出
Apr 18 #Javascript
JavaScript错误处理和堆栈追踪详解
Apr 18 #Javascript
You might like
PHP 开发工具
2006/12/06 PHP
PHP 加密/解密函数 dencrypt(动态密文,带压缩功能,支持中文)
2009/01/30 PHP
php比较相似字符串的方法
2015/06/05 PHP
全面了解PHP中的全局变量
2016/06/17 PHP
Laravel框架基础语法与知识点整理【模板变量、输出、include引入子视图等】
2019/12/03 PHP
JS 动态获取节点代码innerHTML分析 [IE,FF]
2009/11/30 Javascript
javascript 树形导航菜单实例代码
2013/08/13 Javascript
JavaScript使用function定义对象并调用的方法
2015/03/23 Javascript
javascript事件绑定学习要点
2016/03/09 Javascript
BootStrap Progressbar 实现大文件上传的进度条的实例代码
2016/06/27 Javascript
JS获取多维数组中相同键的值实现方法示例
2017/01/06 Javascript
使用nodejs下载风景壁纸
2017/02/05 NodeJs
JSON与js对象序列化实例详解
2017/03/16 Javascript
小程序文字跑马灯效果
2018/12/28 Javascript
小程序两种滚动公告栏的实现方法
2019/09/17 Javascript
基于js实现判断浏览器类型代码实例
2020/07/17 Javascript
Python编写简单的HTML页面合并脚本
2016/07/11 Python
浅谈python 线程池threadpool之实现
2017/11/17 Python
Python3.6.x中内置函数总结及讲解
2019/02/22 Python
python实现nao机器人身体躯干和腿部动作操作
2019/04/29 Python
Python人脸识别第三方库face_recognition接口说明文档
2019/05/03 Python
Python爬虫实现使用beautifulSoup4爬取名言网功能案例
2019/09/15 Python
win10下opencv-python特定版本手动安装与pip自动安装教程
2020/03/05 Python
python GUI库图形界面开发之PyQt5滚动条控件QScrollBar详细使用方法与实例
2020/03/06 Python
CSS3 3D酷炫立方体变换动画的实现
2019/03/26 HTML / CSS
如何使用css3实现一个类在线直播的队列动画的示例代码
2020/06/17 HTML / CSS
html5服务器推送_动力节点Java学院整理
2017/07/12 HTML / CSS
HTML5边玩边学(2)基础绘图实现方法
2010/09/21 HTML / CSS
社团文化节策划书
2014/02/01 职场文书
新学期决心书
2014/03/11 职场文书
房地产广告词大全
2014/03/19 职场文书
毕业实习计划书
2015/01/16 职场文书
圆明园纪录片观后感
2015/06/03 职场文书
2019最新校园运动会广播稿!
2019/06/28 职场文书
CSS3 制作的悬停缩放特效
2021/04/13 HTML / CSS
带你了解CSS基础知识,样式
2021/07/21 HTML / CSS