您的位置:首页 > 产品设计 > UI/UE

Vue的响应式原理

2017-05-15 23:09 411 查看
下面的是一张来自Vue官网的图片:



如何追踪变化??

当把一个普通的JavaScript对象传递给Vue实例的
data
选项,Vue将遍历这个对象的所有属性,并且利用Object.defineProperty这些属性全部转换为getter/setter,当然由于兼容性问题,所以Vue不支持IE8及更低版本的浏览器

从上面的图可以看出,对于数据Data来说,它里面的getter与setter是对于外部是不可见的,但是在内部完全可以使用它们,在内部,通过Vue进行追踪依赖,当属性被修改或者访问时通知变化。每个组件的实例都会有相应的watcher实例对象,它会在组件渲染的过程中将属性记为依赖,之后当依赖项的
setter
被调用时,会通知
watcher
重新计算,从而让和它相关联的组件得以更新

变化的检测问题

受现代浏览器的限制(以及废弃的Object.observe),Vue不能检测到对象属性的添加或者删除,由于Vue在初始的时候就会对属性实行
getter/setter
转换过程,所以属性必须存在于
data
对象时才能让Vue转换它,这样它才是响应的,这段话的意思等同于下面的代码:

//初始化之前写在data中的可以让Vue转换它,所以a是响应的
var v = new Vue({
data : {
a : 1
}
});

//初始化之后添加的属性b是非响应的
v.b = 2;


Vue不允许在已经创建的实例上动态添加新的根级响应式属性,然而它可以使用
Vue.set(Object, key, value)
方法将响应属性添加到嵌套的对象上

Vue.set(vm.someObject, 'b', 2)


也可以使用
v.$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完成更新DOM,可以在数据变化之后立即使用
Vue.nextTick(callback)
这样回掉函数在DOM更新后就会调用。

<div id="example">{{message}}</div>
<script type="text/javascript">
var vm = new Vue({
el: '#example',
data: {
message: '123'
}
});
vm.message = 'new message' // 更改数据
console.log(vm.$el.textContent);   //123

Vue.nextTick(function () {
alert(2323);
console.log(vm.$el.textContent);  //new message
});
</script>


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

Vue.component('my-component', {
template: '<span> {{message}}</span>',
data : function() {
return {
message: 'not update'
}
},
methods : {
updateMessage : function() {
this.message = 'update';
console.log(this.$el.textContent);  //'not update'
this.$nextTick(function() {
console.log(this.$el.textContent); //'update'
});
}
}
});


参考:https://cn.vuejs.org/v2/guide/reactivity.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: