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 相关文章推荐
关于使用runtimeStyle属性问题讨论文章
Mar 08 Javascript
简单实例处理url特殊符号&amp;处理(2种方法)
Apr 02 Javascript
setInterval()和setTimeout()的用法和区别示例介绍
Nov 17 Javascript
使用jQuery快速解决input中placeholder值在ie中无法支持的问题
Jan 02 Javascript
使用jquery解析XML的方法
Sep 05 Javascript
javascript关于运动的各种问题经典总结
Apr 27 Javascript
AngularJS入门教程之路由与多视图详解
Aug 19 Javascript
tweenjs缓动算法的使用实例分析
Aug 26 Javascript
在Express中提供静态文件的实现方法
Oct 17 Javascript
layui数据表格重载实现往后台传参
Nov 15 Javascript
浅谈Vue2.4.0 $attrs与inheritAttrs的具体使用
Mar 08 Javascript
JS前端宏任务微任务及Event Loop使用详解
Jul 23 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 daddslashes()和 saddslashes()有哪些区别分析
2012/10/26 PHP
PHP计数器的实现代码
2013/06/08 PHP
PHP中让curl支持sock5的代码实例
2015/01/21 PHP
详解PHP+AJAX无刷新分页实现方法
2015/11/03 PHP
在Yii2中使用Pjax导致Yii2内联脚本载入失败的原因分析
2016/03/06 PHP
PHP自定义错误用法示例
2016/09/28 PHP
让您的菜单不离网站
2006/10/03 Javascript
Yii-自定义删除确认弹框(zyd)jquery实现代码
2013/03/04 Javascript
浅谈node.js 命令行工具(cli)
2018/05/10 Javascript
深入Vue-Router路由嵌套理解
2018/08/13 Javascript
原生JS实现旋转轮播图+文字内容切换效果【附源码】
2018/09/29 Javascript
javascript实现的字符串转换成数组操作示例
2019/06/13 Javascript
Element 默认勾选表格 toggleRowSelection的实现
2019/09/04 Javascript
layui监听单元格编辑前后交互的例子
2019/09/16 Javascript
vue中组件通信详解(父子组件, 爷孙组件, 兄弟组件)
2020/07/27 Javascript
[01:32]2014DOTA2西雅图邀请赛 CIS我们有信心进入正赛
2014/07/08 DOTA
[01:19:54]DOTA2上海特级锦标赛主赛事日 - 2 败者组第二轮#1Alliance VS EHOME
2016/03/03 DOTA
使用Python编写一个最基础的代码解释器的要点解析
2016/07/12 Python
python中获得当前目录和上级目录的实现方法
2017/10/12 Python
python多线程之事件Event的使用详解
2018/04/27 Python
Python设计模式之备忘录模式原理与用法详解
2019/01/15 Python
Python实现简单石头剪刀布游戏
2021/01/20 Python
Django项目使用CircleCI的方法示例
2019/07/14 Python
解决Django layui {{}}冲突的问题
2019/08/29 Python
解决django-xadmin列表页filter关联对象搜索问题
2019/11/15 Python
python GUI库图形界面开发之PyQt5复选框控件QCheckBox详细使用方法与实例
2020/02/28 Python
python3实现将json对象存入Redis以及数据的导入导出
2020/07/16 Python
Python 执行矩阵与线性代数运算
2020/08/01 Python
CSS3选择器新增问题的实现
2021/01/21 HTML / CSS
以实惠的价格轻松租车,免费取消:Easyrentcars
2019/07/16 全球购物
Ray-Ban雷朋太阳眼镜英国官网:Ray-Ban UK
2019/11/23 全球购物
KEEN美国官网:美国人气户外休闲鞋品牌
2021/03/09 全球购物
模特职业生涯规划范文
2014/02/26 职场文书
2014五年级班主任工作总结
2014/12/05 职场文书
公司2015年终工作总结
2015/05/26 职场文书
升级 Win11 还是坚守 Win10?微软 Win11 新系统缺失功能大盘点
2022/04/05 数码科技