# Vue.js 响应式原理
# 关于 Vue.js
Vue.js 是一款 MVVM 框架,上手快速简单易用,通过响应式在修改数据的时候更新视图。Vue.js 的响应式原理依赖于Object.defineProperty,这也是 Vue.js 不支持 IE8 以及更低版本浏览器的原因。Vue 通过设定对象属性的 setter/getter 方法来监听数据的变化,通过 getter 进行依赖收集,而每个 setter 方法就是一个观察者,在数据变更的时候通知订阅者更新视图。
# 将数据 data 变成可观察(observable)的
那么 Vue 是如何将所有 data 下面的所有属性变成可观察的(observable)呢?
function observe(value, cb) {
Object.keys(value).forEach((key) => defineReactive(value, key, value[key], cb));
}
function defineReactive(obj, key, val, cb) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
/*....依赖收集等....*/
return val;
},
set: (newVal) => {
val = newVal;
cb(); /*订阅者收到消息的回调*/
}
});
}
class Vue {
constructor(options) {
this._data = options.data;
observe(this._data, options.render);
}
}
let app = new Vue({
el: '#app',
data: {
text: 'text',
text2: 'text2'
},
render() {
console.log('render');
}
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
为了便于理解,首先考虑一种最简单的情况,不考虑数组等情况,代码如上所示。在initData中会调用observe这个函数将 Vue 的数据设置成 observable 的。当_data 数据发生改变的时候就会触发 set,对订阅者进行回调(在这里是 render)。
那么问题来了,需要对 app._data.text 操作才会触发 set。为了偷懒,我们需要一种方便的方法通过 app.text 直接设置就能触发 set 对视图进行重绘。那么就需要用到代理。
# 代理
我们可以在 Vue 的构造函数 constructor 中为 data 执行一个代理proxy。这样我们就把 data 上面的属性代理到了 vm 实例上。
_proxy.call(this, options.data); /*构造函数中*/
/*代理*/
function _proxy(data) {
const that = this;
Object.keys(data).forEach((key) => {
Object.defineProperty(that, key, {
configurable: true,
enumerable: true,
get: function proxyGetter() {
return that._data[key];
},
set: function proxySetter(val) {
that._data[key] = val;
}
});
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我们就可以用 app.text
代替 app._data.text
了。