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 相关文章推荐
javascript smipleChart 简单图标类
Jan 12 Javascript
window.open关于浏览器拦截问题分析及解决方法
Feb 05 Javascript
jquery scroll()区分横向纵向滚动条的方法
Apr 04 Javascript
jQuery/CSS3图片特效插件整理推荐
Dec 07 Javascript
png在IE6 下无法透明的解决方法汇总
May 21 Javascript
关于js原型的面试题讲解
Sep 25 Javascript
学习使用Bootstrap栅格系统
May 11 Javascript
详解vue 模版组件的三种用法
Jul 21 Javascript
详解各版本React路由的跳转的方法
May 10 Javascript
解决Vue-cli npm run build生产环境打包,本地不能打开的问题
Sep 20 Javascript
JSON字符串操作移除空串更改key/value的介绍
Jan 05 Javascript
vue.js实现简单购物车功能
May 30 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
javascript 小型动画组件与实现代码
2010/06/02 PHP
PHP中使用asort进行中文排序失效的问题处理
2014/08/18 PHP
phpmyadmin提示The mbstring extension is missing的解决方法
2014/12/17 PHP
php获得文件夹下所有文件的递归算法的简单实例
2016/11/01 PHP
PHP实现微信JS-SDK接口选择相册及拍照并上传的方法
2016/12/05 PHP
JavaScipt基本教程之前言
2008/01/16 Javascript
javascript 设计模式之单体模式 面向对象学习基础
2010/04/18 Javascript
jQuery中filter(),not(),split()使用方法
2010/07/06 Javascript
Express的路由详解
2015/12/10 Javascript
如何用JS/HTML将时间戳转换为“xx天前”的形式
2017/02/06 Javascript
Nodejs调用WebService的示例代码
2017/09/29 NodeJs
学习jQuery中的noConflict()用法
2018/09/28 jQuery
JS实现网页烟花动画效果
2020/03/10 Javascript
[05:45]Ti4观战指南(下)
2014/07/07 DOTA
[56:35]DOTA2上海特级锦标赛主赛事日 - 5 总决赛Liquid VS Secret第一局
2016/03/06 DOTA
Python实现的堆排序算法原理与用法实例分析
2017/11/22 Python
如何利用python查找电脑文件
2018/04/27 Python
Python GUI编程 文本弹窗的实例
2019/06/11 Python
Python爬虫库BeautifulSoup的介绍与简单使用实例
2020/01/25 Python
Python批量启动多线程代码实例
2020/02/18 Python
Python TKinter如何自动关闭主窗口
2020/02/26 Python
python Cartopy的基础使用详解
2020/11/01 Python
Python更改pip镜像源的方法示例
2020/12/01 Python
使用CSS3来匹配横屏竖屏的简单方法
2015/08/04 HTML / CSS
Cole Haan官方网站:美国时尚潮流品牌
2017/12/06 全球购物
L’urv官网:精品女性运动服品牌
2019/07/07 全球购物
高速铁道技术专业求职信
2014/08/09 职场文书
大学生入党积极分子党校学习思想汇报
2014/10/25 职场文书
白鹤梁导游词
2015/02/06 职场文书
2015学校师德师风工作总结
2015/04/22 职场文书
学校趣味运动会开幕词
2016/03/04 职场文书
python如何利用traceback获取详细的异常信息
2021/06/05 Python
图解排序算法之希尔排序Java实现
2021/06/26 Java/Android
教你使用Jenkins集成Harbor自动发布镜像
2022/04/03 Servers
《艾尔登法环》Boss腐烂树灵很有可能是《黑暗之魂3》的一个废案
2022/04/11 其他游戏
MySQL索引失效十种场景与优化方案
2023/05/08 MySQL