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 相关文章推荐
强大的jquery插件jqeuryUI做网页对话框效果!简单
Apr 14 Javascript
探索Emberjs制作一个简单的Todo应用
Nov 07 Javascript
开启Javascript中apply、call、bind的用法之旅模式
Oct 28 Javascript
原生js实现查询天气小应用
Dec 09 Javascript
Bootstrap常用组件学习(整理)
Mar 24 Javascript
angularjs指令之绑定策略(@、=、&amp;)
Apr 13 Javascript
详解angularjs的数组传参方式的简单实现
Jul 28 Javascript
js原生方法被覆盖,从新赋值原生的方法
Jan 02 Javascript
JS通过位运算实现权限加解密
Aug 14 Javascript
微信小程序swiper实现滑动放大缩小效果
Nov 15 Javascript
在layui框架中select下拉框监听更改事件的例子
Sep 20 Javascript
详解vue-flickity的fullScreen功能实现
Apr 07 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永久登录、记住我功能实现方法和安全做法
2015/04/27 PHP
各种快递查询--Api接口
2016/04/26 PHP
PHP+MYSQL实现读写分离简单实战
2017/03/13 PHP
PHP读取并输出XML文件数据的简单实现方法
2017/12/22 PHP
laravel框架如何设置公共头和公共尾
2019/10/22 PHP
如果文字过长,则将过长的部分变成省略号显示
2006/06/26 Javascript
JavaScript this 深入理解
2009/07/30 Javascript
javascrip关于继承的小例子
2013/05/10 Javascript
JavaScript中解决多浏览器兼容性23个问题的快速解决方法
2016/05/19 Javascript
原生js实现弹出层登录拖拽功能
2016/12/05 Javascript
详解nodejs操作mongodb数据库封装DB类
2017/04/10 NodeJs
前端天气插件tpwidget使用方法详解
2019/06/24 Javascript
JavaScript实现秒杀时钟倒计时
2019/09/29 Javascript
JS实现关闭小广告特效
2021/01/29 Javascript
如何手写简易的 Vue Router
2020/10/10 Javascript
使用Python编写简单的画图板程序的示例教程
2015/12/08 Python
python 字符串转列表 list 出现\ufeff的解决方法
2017/06/22 Python
Python2实现的LED大数字显示效果示例
2017/09/04 Python
Centos部署django服务nginx+uwsgi的方法
2019/01/02 Python
如何使用python进行pdf文件分割
2019/11/11 Python
TensorFlow实现自定义Op方式
2020/02/04 Python
如何实现在jupyter notebook中播放视频(不停地展示图片)
2020/04/23 Python
使用python创建Excel工作簿及工作表过程图解
2020/05/27 Python
详解css position 5种不同的值的用法
2019/07/30 HTML / CSS
全天然狗零食:Best Bully Sticks
2016/09/22 全球购物
京东全球售:直邮香港,澳门,台湾,美国,澳大利亚等地区
2017/09/24 全球购物
周仰杰(JIMMY CHOO)英国官方网站:闻名世界的鞋子品牌
2018/10/28 全球购物
乌克兰设计师和品牌的服装:Love&Live
2020/04/14 全球购物
什么是ARP(Address Resolution Protocol)地址解析协议
2013/10/31 面试题
管理学专业个人求职信范文
2013/09/21 职场文书
大学毕业生的自我鉴定
2013/11/30 职场文书
企业后勤岗位职责
2014/02/28 职场文书
禁止高声喧哗的标语
2014/06/11 职场文书
质量提升方案
2014/06/16 职场文书
科学育儿宣传标语
2014/10/08 职场文书
MySQL 语句执行顺序举例解析
2022/06/05 MySQL