當(dāng)面試官問:為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?
嗨,我是你穩(wěn)定更新、精通面試的勾勾。

以前面試的時候經(jīng)常被問到響應(yīng)式相關(guān)的內(nèi)容,而 Vue3.0 更新后,面試官又有了新的武器來考核面試者。
面試官:為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?

對于面試者來說,懵逼樹上懵逼果,懵逼樹下你和我,面試官在問什么,我該怎么回答,完全不知道怎么回事。
有些經(jīng)驗的小伙伴可能會從解釋 Proxy 的好處開始簡單聊一下,比如,Proxy 是直接代理對象,而不是劫持對象的屬性,更好的數(shù)組監(jiān)控等。
這樣的回答,勉強算是合格吧。
那到底應(yīng)該怎么答呢,才能順利通過面試,贏得 offer?
面試官背后的出題邏輯
別急,咱們先整理一下思路,孫子兵法有云:“知己知彼,百戰(zhàn)不殆”。
面試就像打仗,你來我往,所以我們需要換位思考,想一想。
為什么面試官會問這樣一個問題?
面試官想從這個問題里得到什么回答?
這個問題可以考察那些技術(shù)點?
想清楚這些問題,再回到自己身上,去思考這些技術(shù)點自己是否都已經(jīng)掌握。
說的直白一點,面試就像考試,你需要先讀題、審題才能答好這道題。
為什么很多人認(rèn)為“面試造火箭,工作擰螺絲”?
因為沒有換位思考,沒有想清楚面試題背后的邏輯。
那我們想清楚這個邏輯之后,需要我們做的就是提取技術(shù)點——整理思路——做出對應(yīng)解答。
當(dāng)然,前提是你需要具備這些技術(shù)能力。
那么接下來,我就嘗試拆解一下這個面試題了,提取其中的知識點。對于你來說,就是要看看這些知識點,你都掌握了多少?
為什么 Vue3.0 要重寫響應(yīng)式系統(tǒng)?
為什么要重寫?
如果之前好好的,重寫就沒有意義。那之前存在什么問題,現(xiàn)在是怎么解決的,這就是關(guān)鍵點了。
不知道你對 Vue2.x 的響應(yīng)式掌握多少,是不是欠下了技術(shù)的債呢?
沒關(guān)系,我來幫你還債,先梳理 Vue2.x 的響應(yīng)式。
其實基于這個面試題,背后還有很多技術(shù)點。上面這些,是與當(dāng)前題目有直接關(guān)系的。在實際面試中,很有可能基于這些技術(shù)點,再進(jìn)行深入交流,這里就不擴展了,你能把現(xiàn)在這些問題理清楚,就算賺到了!

Vue2.x 響應(yīng)式
關(guān)于這一點,在 Vue 的官方文檔中,早已經(jīng)有過說明了,而且說的非常詳細(xì)。
官方文檔:
https://cn.vuejs.org/v2/guide/reactivity.html

當(dāng)你把一個普通的 JavaScript 對象傳入 Vue 實例作為 data 選項,Vue 將遍歷此對象所有的 property,并使用 Object.defineProperty 把這些 property 全部轉(zhuǎn)為 getter/setter。
Object.defineProperty 是 ES5 中一個無法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。
這些 getter/setter 對用戶來說是不可見的,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴,在 property 被訪問和修改時通知變更。
這里需要注意的是,不同瀏覽器在控制臺打印數(shù)據(jù)對象時對 getter/setter 的格式化并不同,所以建議安裝 vue-devtools 來獲取對檢查數(shù)據(jù)更加友好的用戶界面。
每個組件實例都對應(yīng)一個 watcher 實例,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù) property 記錄為依賴。之后當(dāng)依賴項的 setter 觸發(fā)時,會通知 watcher,從而使它關(guān)聯(lián)的組件重新渲染。
我們使用官方給的一張圖示,來梳理整個流程。

先來看一段代碼:

響應(yīng)式原理
data 中的 obj 就是一個普通的 JavaScript 對象,通過點擊 Click 按鈕,將獲取到的隨機數(shù)賦值給 this.message ,而 this.message 指向的就是 data 中 obj 對象的 message 屬性。
當(dāng) message 發(fā)生數(shù)據(jù)改變時,頁面中 H1 標(biāo)簽的內(nèi)容會隨之改變,這個過程就是就是響應(yīng)式的。
那么 Vue 是如何實現(xiàn)的呢?
首先,Vue 內(nèi)部使用 Object.defineProperty() 將 Data 中的每一個成員都轉(zhuǎn)換為 getter / setter 的形式。
getter 用來依賴收集,setter 用來派發(fā)更新。而模板內(nèi)容,最終會被編譯為 render 函數(shù)。
在 render 函數(shù)中,我們能發(fā)現(xiàn)?_v(_s(message)) message 被訪問了,就會觸發(fā) getter 來進(jìn)行依賴收集。
而在代碼中的點擊事件中,一旦事件處理程序被觸發(fā)執(zhí)行,那么 message 則會被修改,就會觸發(fā) setter 來進(jìn)行派發(fā)更新。
雖然流程理清楚了,但是總感覺少點什么,怎么才能更通透呢?
我們用代碼來模擬整個的實現(xiàn)過程。
defineProperty 模擬代碼
defineProperty 的基本用法,直接看手冊就行了:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
我們來看看代碼:
<div id="app">hellodiv><script>// 模擬 Vue 中的 data 選項let data = {msg: 'hello'}// 模擬 Vue 的實例let vm = {}// 數(shù)據(jù)劫持:當(dāng)訪問或者設(shè)置 vm 中的成員的時候,做一些干預(yù)操作Object.defineProperty(vm, 'msg', {// 可枚舉(可遍歷)enumerable: true,// 可配置(可以使用 delete 刪除,可以通過 defineProperty 重新定義)configurable: true,// 當(dāng)獲取值的時候執(zhí)行get () {console.log('get: ', data.msg)return data.msg},// 當(dāng)設(shè)置值的時候執(zhí)行set (newValue) {console.log('set: ', newValue)if (newValue === data.msg) {return}data.msg = newValue// 數(shù)據(jù)更改,更新 DOM 的值document.querySelector('#app').textContent = data.msg}})// 測試vm.msg = 'Hello World'console.log(vm.msg)script>
你沒有看錯,加上注釋,一共 36行代碼,這就是 Vue2.x 對響應(yīng)式實現(xiàn)的整個流程。
?繼續(xù)實現(xiàn)多個數(shù)據(jù)的響應(yīng)。
<body><div id="app">hellodiv><script>// 模擬 Vue 中的 data 選項let data = {msg: 'hello',count: 10}// 模擬 Vue 的實例let vm = {}proxyData(data)function proxyData(data) {// 遍歷 data 對象的所有屬性Object.keys(data).forEach(key => {// 把 data 中的屬性,轉(zhuǎn)換成 vm 的 setter/setterObject.defineProperty(vm, key, {enumerable: true,configurable: true,get () {console.log('get: ', key, data[key])return data[key]},set (newValue) {console.log('set: ', key, newValue)if (newValue === data[key]) {return}data[key] = newValue// 數(shù)據(jù)更改,更新 DOM 的值document.querySelector('#app').textContent = data[key]}})})}// 測試vm.msg = 'Hello World'console.log(vm.msg)script>body>
上面的代碼只是模擬了響應(yīng)式的原理,但 Vue 在實現(xiàn)中,肯定不會那么簡單。
下周一,我們來康康源碼(●'?'●)。
推薦閱讀:
喜大普奔!Element UI for Vue 3.0 來了!
點點“贊”和“在看”,保護頭發(fā),減少bug。
