console
function Vue(options) {
this._init(options);
}
Vue.prototype._init = function(options) {
this.$options = options;
initData(this);
}
function initData(vm) {
let { data } = vm.$options
if (!data) {
vm._data = {}
} else {
vm._data = typeof data === 'function' ? data() : data
}
for (let key in vm._data) {
proxy(vm, '_data', key)
}
observe(vm._data);
}
function proxy(target, sourceKey, key) {
Object.defineProperty(target, key, {
get() {
return target[sourceKey][key]
},
set(newV) {
target[sourceKey][key] = newV
}
})
}
function observe(value) {
if (typeof value !== 'object') return
if (value.__ob__) return value.__ob__
return new Observer(value)
}
function Observer(value) {
Object.defineProperty(value, '__ob__', {
value: this,
enumerable: false,
writable: true,
configurable: true
})
if (Array.isArray(value)) {
protoArgument(value)
this.observeArray(value)
} else {
this.walk(value)
}
}
Observer.prototype.walk = function (obj) {
for (let key in obj) {
defindReactive(obj, key, obj[key]);
}
}
Observer.prototype.observeArray = function (arr) {
for(let item of arr) {
observe(item);
}
}
function defindReactive(obj, key, val) {
observe(val);
Object.defineProperty(obj, key, {
get() {
ctrLog(`getter: key = ${key}`);
return val;
},
set(newValue) {
if (newValue === val) return;
val = newValue;
}
})
}
const arrayProto = Array.prototype;
const arrayMethods = Object.create(arrayProto);
const arrayToPatch = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'];
arrayToPatch.forEach(method=>{
Object.defineProperty(arrayMethods, method, {
value: function(...args) {
const ret = arrayProto[method].apply(this, args);
ctrLog(`array reactive`)
return ret;
},
enumerable: true,
writable: true,
configurable: true
});
});
function protoArgument(arr) {
arr.__proto__ = arrayMethods
}
const ins = new Vue({
el: '#app',
data() {
return {
t: 't value',
t1: {
tt1: 'tt1 value'
},
arr: [1, 2, 3],
counter: 0,
title: '看我',
inputVal: 'test',
isChecked: true,
selectValue: 2
}
},
methods: {
handleAdd() {
this.counter++
},
handleMinus() {
this.counter--
}
},
})
setTimeout(() => {
ctrLog('********** 属性值为原始值时的 getter、setter ************')
ctrLog(ins.t)
ins.t = 'change t value'
ctrLog(ins.t)
}, 1000)
setTimeout(() => {
ctrLog('********** 属性的新值为对象的情况 ************')
ins.t = {
tt: 'tt value'
}
ctrLog(ins.t.tt)
}, 2000)
setTimeout(() => {
ctrLog('********** 验证对深层属性的 getter、setter 拦截 ************')
ins.t1.tt1 = 'change tt1 value'
ctrLog(ins.t1.tt1)
}, 3000)
setTimeout(() => {
ctrLog('********** 将值为对象的属性更新为原始值 ************')
ctrLog(ins.t1)
ins.t1 = 't1 value'
ctrLog(ins.t1)
}, 4000)
setTimeout(() => {
ctrLog('********** 数组操作方法的拦截 ************')
ctrLog(ins.arr)
ins.arr.push(4)
ctrLog(ins.arr)
}, 5000)
function ctrLog(info) {
const isShowLog = true;
if (isShowLog) {
console.log(info);
}
}
<div id="app">
<h3>数据响应式更新 原理</h3>
<div>{{ t }}</div>
<div>{{ t1 }}</div>
<div>{{ arr }}</div>
<h3>methods + 事件 + 数据响应式更新 原理</h3>
<div>
<p>{{ counter }}</p>
<button v-on:click="handleAdd"> Add </button>
<button v-on:click="handleMinus"> Minus </button>
</div>
<h3>v-bind</h3>
<span v-bind:title="title">右键审查元素查看我的 title 属性</span>
<h3>v-model 原理</h3>
<div>
<input type="text" v-model="inputVal" />
<div>{{ inputVal }}</div>
</div>
<div>
<input type="checkbox" v-model="isChecked">
<div>{{ isChecked }}</div>
</div>
<div>
<select v-model="selectValue">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<div>{{ selectValue }}</div>
</div>
</div>