将data下边所有的属性变成可观察的 - observable
Vue.js是一款MVVM框架。怎么做到更改数据时,驱动视图刷新的呢。
总体来说: Vue通过设定对象属性的 setter/getter 方法来监听数据的变化,通过getter进行依赖收集,而每个setter方法就是一个观察者,在数据变更的时候通知订阅者更新视图。
这个函数将Vue的数据设置成observable的。当_data数据发生改变的时候就会触发set,对订阅者进行回调(在这里是render)
js
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, {
//但是默认是不可枚举的(for in打印打印不出来),可:enumerable: true
//默认不可以修改,可:wirtable:true
//默认不可以删除,可:configurable:true
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");
}
})
这样也存在一个问题,我们需要对app._data.text才能触发数据的set。我们可以做一个代理,做到通过app.text直接设置就能触发set对视图进行重绘通。
代理
我们可以在Vue的构造函数constructor中为data执行一个代理
javascript
_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;
}
})
});
}
总结
不考虑数组情况,一个简单的响应式原理就是这样。遍历data下所有属性,通过 defineProperty 劫持到数据的get和set方法,在set数据时,回调runder函数。实现视图刷新。同时需要 _proxy代理_data上的数据到app上。
javascript
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, {
//但是默认是不可枚举的(for in打印打印不出来),可:enumerable: true
//默认不可以修改,可:wirtable:true
//默认不可以删除,可:configurable:true
enumerable: true,
configurable: true,
get: ()=>{
/*....依赖收集等....*/
return val
},
set:newVal=> {
val = newVal;
cb(); /*订阅者收到消息的回调*/
}
})
}
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;
}
})
});
}
class Vue {
constructor(options) {
_proxy.call(this, options.data);
this._data = options.data;
observe(this._data, options.render)
}
}
let app = new Vue({
el: '#app',
data: {
text: 'text',
text2: 'text2'
},
render(){
console.log("render");
}
})
app.text = '22'
console.log(app)