# 「每日一題」有人上次在dy面試,面試官問我:vue數據綁定的實現原理。你說我該如何回答?

關注「鬆寶寫代碼」,精選好文,每日一題前端

​時間永遠是本身的vue

每分每秒也都是爲本身的未來鋪墊和增值面試

做者:saucxs | songEagle

來源:原創算法

1、前言

文章首發wx的「鬆寶寫代碼」數組

2020.12.23 日剛立的 flag,每日一題,題目類型不限制,能夠是:算法題,面試題,闡述題等等。瀏覽器

本文是「每日一題」第 5 題:「每日一題」到底該如何回答:vue數據綁定的實現原理?框架

每日一題

往期「每日一題」:學習

2、vue數據綁定的實現原理?

這個題目自己不是特別難,只能說是做爲社招的基礎面試題,可是若是想回答好這道題也不是很容易。spa

不信接着往下看設計

一、歸納回答

vue.js是一個很是優秀的前端開發框架,使用vue的版本是v2.x

vue幾個核心的地方:vue實例化,虛擬DOM,模板編譯過程,數據綁定。

咱們開始回到正題,vue.js的做者尤雨溪最初就是嘗試實現一個相似angular1的東西,發現裏面對於數據處理很是不優雅,因而創造性的嘗試利用ES5中的Object.defineProperty來實現數據綁定,因而就有了最初的vue。

vue的數據綁定的實現原理離不開vue中響應式的數據處理方式。

咱們能夠回想一下官網的圖:

vue中響應式的數據處理方式

vue的響應式基本原理:

  • 一、vue會遍歷此data中對象全部的屬性,
  • 二、並使用Object.defineProperty把這些屬性所有轉爲getter/setter,
  • 三、而每一個組件實例都有watcher對象,
  • 四、它會在組件渲染的過程當中把屬性記錄爲依賴,
  • 五、以後當依賴項的 setter被調用時,會通知watcher從新計算,從而導致它關聯的組件得以更新。

二、亮點回答

歸納回答咱們只回答了使用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的響應式原理設計三個重要對象的依賴關係

接着咱們須要補充的是:模板編譯過程當中的指令和數據綁定都會生成Watcher實例,實例中的watch屬性也會生成Watcher實例。

說的這些有沒有以爲有點亂,那咱們總結一下如何亮點回答

  • 一、在生命週期的initState方法中將data,prop,method,computed,watch中的數據劫持, 經過observe方法與Object.defineProperty方法將相關對象轉爲換Observer對象。
  • 二、而後在initRender方法中解析模板,經過Watcher對象,Dep對象與觀察者模式將模板中的 指令與對象的數據創建依賴關係,使用全局對象Dep.target實現依賴收集。
  • 三、當數據變化時,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實現監聽變量

三、進階回答

由於如今vue已經到3了,再也不是停留在2的時候,這個時候,能夠把3的原理簡單說一下。

這個時候不該該是ES6的proxy特性上場了,proxy是ES6的新增的功能,能夠用來定義對象中的操做。

let p = new Proxy(target, handler);
// `target` 表明須要添加代理的對象
// `handler` 用來自定義對象中的操做

若是上面代碼格式出現問題,能夠查看下面代碼圖片

使用Object.defineProperty實現監聽變量

能夠很方便的使用 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 來實現一個數據綁定和監聽

而後在對比vue2和vue3的區別是什麼?

以及爲啥在數據監聽上作了升級?

vue爲何對數組對象的深層監聽沒法實現,由於組件每次渲染都是將data裏的數據經過defineProperty進行響應式或者雙向綁定上,以前沒有後加的屬性是不會被綁定上,也就不會觸發更新渲染。

區別:

一、語法層面上

  • defineProperty只能響應首次渲染時候的屬性,
  • Proxy須要的是總體監聽,不須要關內心面有什麼屬性,並且Proxy的配置項有13種,能夠作更細緻的事情,這是以前的defineProperty沒法達到的。

二、兼容層面上

  • vue2.x之因此只能兼容到IE8就是由於defineProperty沒法兼容IE8,其餘瀏覽器也會存在輕微兼容問題。
  • proxy的話除了IE,其餘瀏覽器都兼容,此次vue3仍是使用了它,說明vue3直接放棄了IE的兼容考慮。

各類福利

鬆寶寫代碼

「鬆寶寫代碼」:開發知識體系構建,技術分享,項目實戰,實驗室,每日一題,帶你一塊兒學習新技術,總結學習過程,讓你進階到高級資深工程師,學習項目管理,思考職業發展,生活感悟,充實中成長起來。問題或建議,請公衆號留言。

一、字節內推福利

回覆「校招」獲取內推碼

回覆「社招」獲取內推

回覆「實習生」獲取內推

後續會有更多福利

二、學習資料福利

回覆「算法」獲取算法學習資料

三、每日一題

相關文章
相關標籤/搜索