2019.10.5日發佈了Vue3.0,如今2020年了,估計Vue3.0正式版也快出來了。html
2.0跟3.0的變化也挺大的,vue
Vue 2.0node
Vue2.0實現MVVM(雙向數據綁定)的原理是經過 Object.defineProperty 來劫持各個屬性的setter、getter,在數據變更時發佈消息給訂閱者,觸發相應的監聽回調。react
Vue官網也給出瞭解釋:算法
Vue 3.0 實現響應式基於ES6: Proxychrome
下面是基於Object.defineProperty ,一步步實現簡單版Vue2.0。數組
因爲Object.defineProperty 沒法監聽數組,因此數組類型實現響應式,須要處理。 判斷若是是數組類型,就重寫數組的原型方法('push','pop','shift',unshift)瀏覽器
// 從新定義數組原型,Object.defineProperty不具有監聽數組的方法
const oldArrayProperty = Array.prototype;
const arrProto = Object.create(oldArrayProperty);
["push","pop","shift","unshift","splice"].forEach(
methodName =>
(arrProto[methodName] = function() {
updateView();
oldArrayProperty[methodName].call(this, ...arguments);
})
)
複製代碼
將傳入的data屬性進行深度監聽,判斷是對象仍是數組。ide
function observer(target){
if(typeof target !== 'object' || target === null){
return target
}
// 若是是數組類型,重寫數組原型的方法("push","pop","shift","unshift","splice")
if(Array.isArray(target)){
target.__proto__ == arrProto;
}
// 若是是對象,遍歷對象全部的屬性,並使用Object.defineProperty把這些屬性所有轉爲getter/setter
for(let key in target){
defineReactive(target,key,target[key])
}
}
複製代碼
核心API Object.defineProperty,將傳入屬性轉爲 getter/setter函數
function defineReactive(target, key, value){
// 若是對象有更多的層級,再次調用observer監聽方法,實現深層次的監聽。
observer(value);
Object.defineProperty(target, key, {
get(){
return value;
},
set(newValue){
// 設置值的時候也須要深度監聽
observer(value);
if(newValue !== value){
value = newValue;
// 數據驅動視圖,若是數據改變,就調用視圖更新的方法。對應到Vue中是執行VDOM
updateView();
}
}
})
}
複製代碼
數據更新會觸發視圖更新,這是MVVM的綁定原理,這就會涉及到Vue的 template 編譯爲 render 函數,在執行 Virtual Dom, Diff算法, Vnode等 這些東西了。
function updateView(){
console.log('視圖更新')
}
複製代碼
5.使用
const data = {
name: "zhangsan",
age: 20,
info: {
address: "北京" // 須要深度監聽
},
nums: [10, 20, 30]
};
observer(data);
複製代碼
Vue3.0基於Proxy來作數據大劫持代理,能夠原生支持到數組的響應式,不須要重寫數組的原型,還能夠直接支持新增和刪除屬性, 比Vue2.x的Object.defineProperty更加的清晰明瞭。
核心代碼(很是少)
const proxyData = new Proxy(data, {
get(target,key,receive){
// 只處理自己(非原型)的屬性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get',key) // 監聽
}
const result = Reflect.get(target,key,receive)
return result
},
set(target, key, val, reveive){
// 重複的數據,不處理
const oldVal = target[key]
if(val == oldVal){
return true
}
const result = Reflect.set(target, key, val,reveive)
return result
},
// 刪除屬性
deleteProperty(target, key){
const result = Reflect.deleteProperty(target,key)
return result
}
})
複製代碼
使用
const data = {
name: "zhangsan",
age: 20,
info: {
address: "北京" // 須要深度監聽
},
nums: [10, 20, 30]
};
複製代碼
直接這樣就能夠了,也不須要聲明,Proxy直接會代理監聽data的內容,很是的簡單方便,惟一的不足就是部分瀏覽器沒法兼容Proxy,也不能hack,因此目前只能兼容到IE11。
可直接將代碼複製到chrome瀏覽器的控制檯,直接調試打印。
複製代碼
Vue2.0
function defineReactive(target, key, value) {
//深度監聽
observer(value);
Object.defineProperty(target, key, {
get() {
return value;
},
set(newValue) {
//深度監聽
observer(value);
if (newValue !== value) {
value = newValue;
updateView();
}
}
});
}
function observer(target) {
if (typeof target !== "object" || target === null) {
return target;
}
if (Array.isArray(target)) {
target.__proto__ = arrProto;
}
for (let key in target) {
defineReactive(target, key, target[key]);
}
}
// 從新定義數組原型
const oldAddrayProperty = Array.prototype;
const arrProto = Object.create(oldAddrayProperty);
["push", "pop", "shift", "unshift", "spluce"].forEach(
methodName =>
(arrProto[methodName] = function() {
updateView();
oldAddrayProperty[methodName].call(this, ...arguments);
})
);
// 視圖更新
function updateView() {
console.log("視圖更新");
}
// 聲明要響應式的對象
const data = {
name: "zhangsan",
age: 20,
info: {
address: "北京" // 須要深度監聽
},
nums: [10, 20, 30]
};
// 執行響應式
observer(data);
複製代碼
Vue3.0
const proxyData = new Proxy(data, {
get(target,key,receive){
// 只處理自己(非原型)的屬性
const ownKeys = Reflect.ownKeys(target)
if(ownKeys.includes(key)){
console.log('get',key) // 監聽
}
const result = Reflect.get(target,key,receive)
return result
},
set(target, key, val, reveive){
// 重複的數據,不處理
const oldVal = target[key]
if(val == oldVal){
return true
}
const result = Reflect.set(target, key, val,reveive)
console.log('set', key, val)
return result
},
deleteProperty(target, key){
const result = Reflect.deleteProperty(target,key)
console.log('delete property', key)
console.log('result',result)
return result
}
})
// 聲明要響應式的對象,Proxy會自動代理
const data = {
name: "zhangsan",
age: 20,
info: {
address: "北京" // 須要深度監聽
},
nums: [10, 20, 30]
};
複製代碼