在我们使用Vue的过程中,我们很容易就能发现通过更改Vue组件中的状态,视图就会自动更新。这样使用者就不需要关心dom的操作,只需把精力放在对数据的处理上。给开发人员带来的很大的便利,也极大的提升了开发的效率。这一切离不开我们的Vue的响应式原理。一起来了解下吧。
现在可以开始我们的正题了......
一、响应式数据如何实现
在研究源码前,我们首先应该明确需求,这是vue需要达成的基本目标,了解这些这有利于我们对源码的理解。
"数据改变,然后视图更新"。
1、数据在赋值之后,还有另一个操作,视图需要更新。这里需要增加视图更新逻辑。
2、视图更新,是哪个视图需要更新?你可能会说,更新组件内模板对应的视图啊,但是代码无法理解"组件内对应的视图"这段语言,我们需要用代码建立响应式数据跟视图渲染之间的联系。所以这里需要增加数据和渲染视图关系绑定的逻辑。
需求明确了,接下来就是代码实现了。这里我们假定我们需要监听的属性是a,于是便有了如下的伪代码。
let rValue;
Object.defineProperty(data,'a',{
set(val){
rValue = val;
// 增加视图更新逻辑
},
get(){
// 数据和视图关系绑定
return rValue;
}
})
二、vue源码实现
Vue组件定义在data中的数据最后都会被封装在一个对象中返回,这个对象接下来会进行响应式处理,使得data中的每一个属性都能变成响应式。
为了方便理解我们结合一个简单的视图案例一起来看下。
视图案例:有一个新闻类别的新闻列表,切换新闻类别可以获取到这个新闻列表下的新闻。当然在没有数据时,页面应该是空的。
————————————————————————————
体育
. 詹姆斯打篮球 --- 得20分
. 梅西踢足球 ----- 进球2个
————————————————————————————
你的模板中会有如下代码
<template>
{{category}}
<ul>
<li v-for="nl in newsList" :key="nl">
<span>{{nl.label}}</sapn>
<span>{{nl.value}}</sapn>
</li>
</ul>
</template>
<script>
data(){
return {
category:'', // 新闻类别 1、体育 2、宠物
newsList:[] // 新闻列表
}
}
</script>
vue源码实现。为了方便理解,已做部分删减。 Vue在初始化的过程中,会以你在data中定义的对象为参数创建Observer实例,内部会遍历你在data上定义的每一个属性,然后做数据劫持。
function Observer(object) {
this.value = object;
this.dep = new Dep(); //@答疑1
def(object, '__ob__', this); //定义一个属性到object上--> object.__obj__ = this,
if (isArray(object)) {
value.__proto__ = arrayMethods;
this.observeArray(object);
}else {
var keys = Object.keys(object);
for (var i = 0; i < keys.length; i++) {
defineReactive(object, keys[i], object[key]);
}
}
}
Observer.prototype.observeArray = function (value) {
for (var i = 0, l = value.length; i < l; i++) {
observe(value[i]);
}
};
// 定义响应式的函数
function defineReactive(obj, key, val) {
var dep = new Dep(); //@答疑1
var childOb = observe(val);
Object.defineProperty(obj, key, {
set: function (newVal) {
val = newVal;
childOb = observe(val);
dep.notify();
},
get: function () {
var value = val;
if (Dep.target) {
dep.depend();
if (childOb) {
childOb.dep.depend();
}
}
return value;
}
});
}
// Dep 负责帮助相应式数据与依赖建立联系 响应式数据变化后通知对应的 依赖更新
function Dep() {
this.subs = [];
}
Dep.prototype.addSub = function (sub) {
this.subs.push(sub);
};
Dep.prototype.depend = function () {
if (Dep.target) {
Dep.target.addDep(this);
this.addSub()
}
};
Dep.prototype.notify = function (info) { //遍历依赖 并让依赖更新
for (var i = 0, l = subs.length; i < l; i++) {
subs[i].update();
}
};
三、vue源码答疑
1、 var dep = new Dep(); // 怎么搞出来个不认识的Dep,他是干嘛的? 答:在第一节"响应式数据如何实现" 中提到需要建立响应式数据和视图渲染的联系。这里Dep负责的就是建立视图渲染和响应式数据的联系,并可以在合适的时候通知视图渲染逻辑的执行。 在defineReactive 函数中,获取属性值的get函数中,有dep.depend()这个逻辑,