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

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

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

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

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

先來(lái)看一段代碼:

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