關注「松寶寫代碼」,精選好文,每日一題
時間永遠是自己的
每分每秒也都是為自己的將來鋪墊和增值
作者:saucxs | songEagle
來源:原創
一、前言
文章首發在「松寶寫代碼」
2020.12.23 日剛立的 flag,每日一題,題目類型不限制,可以是:算法題,面試題,闡述題等等。
本文是「每日一題」第 5 題:「每日一題」到底該如何回答:vue數據綁定的實現原理?
往期「每日一題」:
二、vue數據綁定的實現原理?
這個題目本身不是特別難,只能說是作為社招的基礎面試題,但是如果想回答好這道題也不是很容易。
不信接着往下看
1、概括回答
vue.js是一個非常優秀的前端開發框架,使用vue的版本是v2.x
vue幾個核心的地方:vue實例化,虛擬DOM,模板編譯過程,數據綁定。
我們開始回到正題,vue.js的作者尤雨溪最初就是嘗試實現一個類似angular1的東西,發現里面對於數據處理非常不優雅,於是創造性的嘗試利用ES5中的Object.defineProperty來實現數據綁定,於是就有了最初的vue。
vue的數據綁定的實現原理離不開vue中響應式的數據處理方式。
我們可以回想一下官網的圖:
vue的響應式基本原理:
-
1、vue會遍歷此data中對象所有的屬性,
-
2、並使用Object.defineProperty把這些屬性全部轉為getter/setter,
-
3、而每個組件實例都有watcher對象,
-
4、它會在組件渲染的過程中把屬性記錄為依賴,
-
5、之后當依賴項的 setter被調用時,會通知watcher重新計算,從而致使它關聯的組件得以更新。
2、亮點回答
概括回答我們只回答了使用ES5的方法 Object.defineProperty 實現數據的監聽的,那么具體是如何實現還是沒有講的很清楚。
這時候我們需要問自己,如何找亮點?
vue的響應式原理設計三個重要對象:Observer,Watcher,Dep。
-
Observer對象:vue中的數據對象在初始化過程中轉換為Observer對象。
-
Watcher對象:將模板和Observer對象結合在一起生成Watcher實例,Watcher是訂閱者中的訂閱者。
-
Dep對象:Watcher對象和Observer對象之間紐帶,每一個Observer都有一個Dep實例,用來存儲訂閱者Watcher。
當屬性變化會執行主題對象Observer的dep.notify方法, 這個方法會遍歷訂閱者Watcher列表向其發送消息, Watcher會執行run方法去更新視圖。
依賴關系圖如下,更能方面我們的理解
接着我們需要補充的是:模板編譯過程中的指令和數據綁定都會生成Watcher實例,實例中的watch屬性也會生成Watcher實例。
說的這些有沒有覺得有點亂,那我們總結一下如何亮點回答
- 1、在生命周期的initState方法中將data,prop,method,computed,watch中的數據劫持, 通過observe方法與Object.defineProperty方法將相關對象轉為換Observer對象。
- 2、然后在initRender方法中解析模板,通過Watcher對象,Dep對象與觀察者模式將模板中的 指令與對象的數據建立依賴關系,使用全局對象Dep.target實現依賴收集。
- 3、當數據變化時,setter被調用,觸發Object.defineProperty方法中的dep.notify方法, 遍歷該數據依賴列表,執行器update方法通知Watcher進行視圖更新。
- vue是無法檢測到對象屬性的添加和刪除,但是可以使用全局Vue.set方法(或vm.$set實例方法)。
- vue無法檢測利用索引設置數組,但是可以使用全局Vue.set方法(或vm.$set實例方法)。
- 無法檢測直接修改數組長度,但是可以使用splice
然后寫一個使用Object.defineProperty實現監聽變量
var obj = {};
var a;
Object.defineProperty(obj, 'a', {
get: function() {
console.log('get val');
return a;
},
set: function(newVal) {
console.log('set val:' + newVal);
a = newVal;
}
});
obj.a; // get val
obj.a = 'saucxs' //set val
如果上面代碼格式出現問題,可以查看下面代碼圖片
3、進階回答
因為現在vue已經到3了,不再是停留在2的時候,這個時候,可以把3的原理簡單說一下。
這個時候不應該是ES6的proxy特性上場了,proxy是ES6的新增的功能,可以用來定義對象中的操作。
let p = new Proxy(target, handler);
// `target` 代表需要添加代理的對象
// `handler` 用來自定義對象中的操作
如果上面代碼格式出現問題,可以查看下面代碼圖片
可以很方便的使用 Proxy 來實現一個數據綁定和監聽.
let onWatch = (obj, setBind, getLogger) => {
let handler = {
get(target, property, receiver) {
getLogger(target, property)
return Reflect.get(target, property, receiver);
},
set(target, property, value, receiver) {
setBind(value);
return Reflect.set(target, property, value);
}
};
return new Proxy(obj, handler);
};
let obj = { saucxs: 1 }
let value
let p = onWatch(obj, (v) => {
value = v
}, (target, property) => {
console.log(`Get '${property}' = ${target[property]}`);
})
p.saucxs = songEagle // bind `value` to `songEagle`
p.saucxs // -> Get 'saucxs' = songEagle
如果上面代碼格式出現問題,可以查看下面代碼圖片
然后在對比vue2和vue3的區別是什么?
以及為啥在數據監聽上做了升級?
vue為什么對數組對象的深層監聽無法實現,因為組件每次渲染都是將data里的數據通過defineProperty進行響應式或者雙向綁定上,之前沒有后加的屬性是不會被綁定上,也就不會觸發更新渲染。
區別:
1、語法層面上
- defineProperty只能響應首次渲染時候的屬性,
- Proxy需要的是整體監聽,不需要關心里面有什么屬性,而且Proxy的配置項有13種,可以做更細致的事情,這是之前的defineProperty無法達到的。
2、兼容層面上
- vue2.x之所以只能兼容到IE8就是因為defineProperty無法兼容IE8,其他瀏覽器也會存在輕微兼容問題。
- proxy的話除了IE,其他瀏覽器都兼容,這次vue3還是使用了它,說明vue3直接放棄了IE的兼容考慮。
各種福利
文章首發在「松寶寫代碼。「松寶寫代碼」:開發知識體系構建,技術分享,項目實戰,實驗室,每日一題,帶你一起學習新技術,總結學習過程,讓你進階到高級資深工程師,學習項目管理,思考職業發展,生活感悟,充實中成長起來。問題或建議,請公眾號留言。
1、字節內推福利
回復「校招」獲取內推碼
回復「社招」獲取內推
回復「實習生」獲取內推
后續會有更多福利
2、學習資料福利
回復「算法」獲取算法學習資料
3、每日一題
-
本文是「每日一題」第 5 題:「每日一題」到底該如何回答:vue數據綁定的實現原理?