Vue 數(shù)據(jù)響應(yīng)依賴于Object.defineProperty,這也是Vue不支持IE8的原因道媚。Vue通過設(shè)定對(duì)象屬性的setter/getter方法來監(jiān)聽數(shù)據(jù)的變化言蛇。通過getter進(jìn)行依賴收集,而每個(gè)setter方法就是一個(gè)觀察者福青,在數(shù)據(jù)變更的時(shí)候通知訂閱者更新視圖。
看看簡單的雙向綁定實(shí)現(xiàn)
const obj = {
object.defineProperty(obj, 'hello', {
get(value) {
console.log('use get')
},
set(newVal, oldVal) {
console.log('user')
}
<input type="text" id="a"/>
<span id="b"></span>
<script>
const obj = {};
Object.defineProperty(obj,'hello',{
get(){
console.log("啦啦啦,方法被調(diào)用了");
},
set(newVal){
document.getElementById('a').value = newVal;
document.getElementById('b').innerHTML = newVal;
}
})
document.addEventListener('keyup',function(e){
obj.hello = e.target.value;
})
</script>
將數(shù)據(jù)data變成可觀察(observable)
function observer(val) {
Object
.key
.forEach((val) => {
defineReactive(val, key, value[key], cb)
})
}
function defineReactive(obj, key, val, cb) {
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: () => {
/*....依賴收集等....*/
/*Github:https://github.com/answershuto*/
},
set: newVal => {
cb();/*訂閱者收到消息的回調(diào)*/
}
})
}
class Vue {
constructor(options) {
this._data = options.data;
observer(this._data, options.render)
}
}
let app = new Vue({
el: '#app',
data: {
text: 'text',
text2: 'text2'
},
render(){
console.log("render");
}
})
為了便于理解颊乘,首先考慮一種最簡單的情況,不考慮數(shù)組等情況醉锄,代碼如上所示乏悄。在initData中會(huì)調(diào)用observe這個(gè)函數(shù)將Vue的數(shù)據(jù)設(shè)置成observable的。當(dāng)_data數(shù)據(jù)發(fā)生改變的時(shí)候就會(huì)觸發(fā)set恳不,對(duì)訂閱者進(jìn)行回調(diào)(在這里是render)檩小。
那么問題來了,需要對(duì)app._date.text操作才會(huì)觸發(fā)set烟勋。為了偷懶规求,我們需要一種方便的方法通過app.text直接設(shè)置就能觸發(fā)set對(duì)視圖進(jìn)行重繪。那么就需要用到代理卵惦。
代理
我們可以在Vue的構(gòu)造函數(shù)constructor中為data執(zhí)行一個(gè)代理proxy阻肿。這樣我們就把data上面的屬性代理到了vm實(shí)例上。
_proxy(options.data);/*構(gòu)造函數(shù)中*/
/*代理*/
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;
}
})
});
}
我們就可以用app.text代替app._data.text了沮尿。
下面是一個(gè)模擬Vue數(shù)據(jù)綁定的方法