1. 程式人生 > >「每日一題」有人上次在dy面試,面試官問我:vue資料繫結的實現原理。你說我該如何回答?

「每日一題」有人上次在dy面試,面試官問我:vue資料繫結的實現原理。你說我該如何回答?

關注「鬆寶寫程式碼」,精選好文,每日一題 ​時間永遠是自己的 每分每秒也都是為自己的將來鋪墊和增值 >作者:saucxs | songEagle >來源:原創 ## 一、前言 文章首發在「鬆寶寫程式碼」 2020.12.23 日剛立的 flag,每日一題,題目型別不限制,可以是:演算法題,面試題,闡述題等等。 本文是「每日一題」第 5 題:[「每日一題」到底該如何回答:vue資料繫結的實現原理?](https://mp.weixin.qq.com/s/8eo4frdB-zMA7nD_1wdnLw) ![每日一題](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/daily-question/daily_question.png) 往期「每日一題」: + 第 4 道[「每日一題」與面試官手撕程式碼:如何科學高效的尋找重複元素?](https://mp.weixin.qq.com/s/jFZ_2f272LhBBPuuLaWnyg) + 第 3 道[「「每日一題」面試官問你對 Promise 的理解?可能是需要你能手動實現各個特性」](https://mp.weixin.qq.com/s/QuuPd2KCp50snN7F2o3oYg) + 第 2 道[「[每日一題]ES6 中為什麼要使用 Symbol?」](https://mp.weixin.qq.com/s/omeVJdtabo5MeN3DItDfWg) + 第 1 道[「一道面試題是如何引發深層次的靈魂拷問?」](https://mp.weixin.qq.com/s/O8j9gM5tD5rjLz1kdda3LA) ## 二、vue資料繫結的實現原理? 這個題目本身不是特別難,只能說是作為社招的基礎面試題,但是如果想回答好這道題也不是很容易。 不信接著往下看 ### 1、概括回答 vue.js是一個非常優秀的前端開發框架,使用vue的版本是v2.x vue幾個核心的地方:vue例項化,虛擬DOM,模板編譯過程,資料繫結。 我們開始回到正題,vue.js的作者尤雨溪最初就是嘗試實現一個類似angular1的東西,發現裡面對於資料處理非常不優雅,於是創造性的嘗試利用ES5中的Object.defineProperty來實現資料繫結,於是就有了最初的vue。 vue的資料繫結的實現原理離不開vue中響應式的資料處理方式。 我們可以回想一下官網的圖: ![vue中響應式的資料處理方式](https://www.mwcxs.top/static/upload/pics/2019/1/25Ukf6H30WNPe0MANDBIvVfbpo.png) 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方法去更新檢視。 依賴關係圖如下,更能方面我們的理解 ![vue的響應式原理設計三個重要物件的依賴關係](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/image/font-end-image/vue-reactive.jpg) 接著我們需要補充的是:模板編譯過程中的指令和資料繫結都會生成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 ``` 如果上面程式碼格式出現問題,可以檢視下面程式碼圖片 ![使用Object.defineProperty實現監聽變數](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/daily-question/vue/data_upgrade/carbon_0_1.png) ### 3、進階回答 因為現在vue已經到3了,不再是停留在2的時候,這個時候,可以把3的原理簡單說一下。 這個時候不應該是ES6的proxy特性上場了,proxy是ES6的新增的功能,可以用來定義物件中的操作。 ``` let p = new Proxy(target, handler); // `target` 代表需要新增代理的物件 // `handler` 用來自定義物件中的操作 ``` 如果上面程式碼格式出現問題,可以檢視下面程式碼圖片 ![使用Object.defineProperty實現監聽變數](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/daily-question/vue/data_upgrade/carbon_1.png) 可以很方便的使用 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 ``` 如果上面程式碼格式出現問題,可以檢視下面程式碼圖片 ![Proxy 來實現一個數據繫結和監聽](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/daily-question/vue/data_upgrade/carbon_2.png) 然後在對比vue2和vue3的區別是什麼? 以及為啥在資料監聽上做了升級? vue為什麼對陣列物件的深層監聽無法實現,因為元件每次渲染都是將data裡的資料通過defineProperty進行響應式或者雙向繫結上,之前沒有後加的屬性是不會被繫結上,也就不會觸發更新渲染。 區別: 1、語法層面上 + defineProperty只能響應首次渲染時候的屬性, + Proxy需要的是整體監聽,不需要關心裡面有什麼屬性,而且Proxy的配置項有13種,可以做更細緻的事情,這是之前的defineProperty無法達到的。 2、相容層面上 + vue2.x之所以只能相容到IE8就是因為defineProperty無法相容IE8,其他瀏覽器也會存在輕微相容問題。 + proxy的話除了IE,其他瀏覽器都相容,這次vue3還是使用了它,說明vue3直接放棄了IE的相容考慮。 ## 各種福利 ![鬆寶寫程式碼](https://raw.githubusercontent.com/saucxs/full_stack_knowledge_list/master/daily-question/dongtai.gif) 文章首發在「鬆寶寫程式碼。「鬆寶寫程式碼」:開發知識體系構建,技術分享,專案實戰,實驗室,每日一題,帶你一起學習新技術,總結學習過程,讓你進階到高階資深工程師,學習專案管理,思考職業發展,生活感悟,充實中成長起來。問題或建議,請公眾號留言。 ### 1、位元組內推福利 回覆「校招」獲取內推碼 回覆「社招」獲取內推 回覆「實習生」獲取內推 後續會有更多福利 ### 2、學習資料福利 回覆「演算法」獲取演算法學習資料 ### 3、每日一題 + 本文是「每日一題」第 5 題:[「每日一題」到底該如何回答:vue資料繫結的實現原理?](https://mp.weixin.qq.com/s/8eo4frdB-zMA7nD_1wdnLw) + 第 4 道[「每日一題」與面試官手撕程式碼:如何科學高效的尋找重複元素?](https://mp.weixin.qq.com/s/jFZ_2f272LhBBPuuLaWnyg) + 第 3 道[「「每日一題」面試官問你對 Promise 的理解?可能是需要你能手動實現各個特性」](https://mp.weixin.qq.com/s/QuuPd2KCp50snN7F2o3oYg) + 第 2 道[「[每日一題]ES6 中為什麼要使用 Symbol?」](https://mp.weixin.qq.com/s/omeVJdtabo5MeN3DItDfWg) + 第 1 道[「一道面試題是如何引發深層次的靈魂拷問?」](https://mp.weixin.qq.com/s/O8j9gM5tD5rjLz1k