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 相关文章推荐
JScript中的&quot;this&quot;关键字使用方式补充材料
Mar 08 Javascript
Javascript前端UI框架Kit使用指南之kitjs事件管理
Nov 28 Javascript
jQuery拖拽插件gridster使用指南
Apr 21 Javascript
javascript实现瀑布流加载图片原理
Feb 02 Javascript
JSP基于Bootstrap分页显示实例解析
Jun 12 Javascript
js定时器实例分享
Dec 20 Javascript
解决Layui选择全部,换页checkbox复选框重新勾选的问题方法
Aug 14 Javascript
js实现input密码框显示/隐藏功能
Sep 10 Javascript
JavaScript实现获取两个排序数组的中位数算法示例
Feb 26 Javascript
Jquery的autocomplete插件用法及参数讲解
Mar 12 jQuery
layui字体图标 loading图标静止不旋转的解决方法
Sep 23 Javascript
在Vue中实现随hash改变响应菜单高亮
Mar 09 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操作MongoDB时的整数问题及对策说明
2011/05/02 PHP
如何在smarty中增加类似foreach的功能自动加载数据
2013/06/26 PHP
浅析PHP中的字符串编码转换(自动识别原编码)
2013/07/02 PHP
phpstorm编辑器乱码问题解决
2014/12/01 PHP
Yii使用smsto短信接口的函数demo示例
2016/07/13 PHP
thinkphp 框架数据库切换实现方法分析
2020/05/18 PHP
jQuery学习笔记[1] jQuery中的DOM操作
2010/12/03 Javascript
使用Jquery打造最佳用户体验的登录页面的实现代码
2011/07/08 Javascript
A标签触发onclick事件而不跳转的多种解决方法
2013/06/27 Javascript
Jquery网页出现的乱码问题的三种解决方法
2013/06/30 Javascript
细说javascript函数从函数的构成开始
2013/08/29 Javascript
jQuery插件bgStretcher.js实现全屏背景特效
2015/06/05 Javascript
AngularJs 60分钟入门基础教程
2016/04/03 Javascript
JS动态的把左边列表添加到右边的实现代码(可上下移动)
2016/11/17 Javascript
node中间层实现文件上传功能
2018/06/11 Javascript
浅谈webpack+react多页面开发终极架构
2018/11/11 Javascript
小程序如何支持使用 async/await详解
2019/09/12 Javascript
js实现AI五子棋人机大战
2020/05/28 Javascript
vue3 watch和watchEffect的使用以及有哪些区别
2021/01/26 Vue.js
Python 含参构造函数实例详解
2017/05/25 Python
使用Turtle画正螺旋线的方法
2017/09/22 Python
python用户管理系统的实例讲解
2017/12/23 Python
python 日期操作类代码
2018/05/05 Python
Python中反射和描述器总结
2018/09/23 Python
python 3.74 运行import numpy as np 报错lib\site-packages\numpy\__init__.py
2019/10/06 Python
基于PyQT实现区分左键双击和单击
2020/05/19 Python
django中related_name的用法说明
2020/05/20 Python
Python读取图像并显示灰度图的实现
2020/12/01 Python
英国在线购买马术服装:EQUUS
2019/07/12 全球购物
买卖正宗运动鞋:GOAT
2019/12/06 全球购物
Derek Rose官网:英国高档睡衣、家居服和内衣品牌
2020/01/18 全球购物
电信专业毕业生推荐信
2013/11/18 职场文书
小学红领巾中秋节广播稿
2014/01/13 职场文书
承诺函格式模板
2015/01/21 职场文书
Pandas||过滤缺失数据||pd.dropna()函数的用法说明
2021/05/14 Python
Python+Appium实现自动抢微信红包
2021/05/21 Python