最近我參加了一次來自西安的電話面試(第二輪,技術面),是大廠還是小作坊我在這裡按下不表,先來說說這次電面給我留下印象較深的幾道面試題,這次先來談談Vue的資料雙向繫結原理。
情景再現:
當我手機鈴聲響起,看著螢幕上面顯示的歸屬地是來自陝西西安的電話,我知道屬於我人生的第一次電話面試要來了。接起電話後,電腦那頭傳來了面試官的聲音(中間省略了一些客套,直接上面試題。)面試官發問,“談談你對Vue資料雙向繫結的認識”。
面試官的這個問題也可以理解成為“你是怎麼理解Vue資料繫結,知道它背後實現的原理麼”。一般剛畢業的前端新人可能會說,用v-model。(當然,這可能是句廢話)
如果簡單說下v-model指令,是Vue的語法糖之類的,可能不會讓面試官滿意,也看不出你對Vue的熟練程度。只能說明你看過Vue的官方文件,如下圖所示:
如果你的回答點到此為止,基本上是不合格的。此時面試官可能會含蓄地追問:然後呢?
其實,如果面試官就這個問題追問,你應該要往兩方面想。往淺了說,如果不用v-model指令,你能用自己的思路實現雙向繫結嗎?往深了挖,他是想問v-model實現背後的原理。
如果你能get到這一點,說明你已經上道了,起碼是在公司中開發過業務程式碼的小碼農。
那如何在元件中自定義實現類似v-model的資料繫結呢?
我先擼為敬:
import Vue from 'vue'
const component = {
template: `
<div>
<input type="text" @input="handleInput">
</div>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
new Vue({
conponents: {
CompA: component
},
el: '#root',
template: `
<div>
<comp-a></comp-a>
</div>
`
})
複製程式碼
這是一個初始化的demo,定義了一個元件component,例項化了一個Vue物件。v-model繫結的值,是從外層的Vue例項中傳進去的。首先我們要在元件component裡面定義一個props:
props: ['value']
複製程式碼
然後就可以在Vue例項的template模板裡面去加上這個value,同時繫結input事件:
template: `
<div>
<comp-a :value="value" @input="value = arguments[0]"></comp-a>
</div>
`,
data () {
return {
value: 'runtu'
}
}
複製程式碼
解釋一下,上面程式碼中的arguments就是元件template裡面的$emit傳出來的值,所有的引數都會放到arguments裡面,類似於陣列。所以這邊我們把arguments[0]賦值給了value。
同樣,元件component裡面的input也得繫結value:
const component = {
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value">
</div>
`,
methods: {
handleInput (e) {
this.$emit('input', e.target.value)
}
}
}
複製程式碼
等執行完以上步驟,江湖規矩,先在terminal裡面跑一下 npm run dev
:
看到demo執行成功地跑在本地8080埠之後,再將視線轉移到瀏覽器裡看一下:
你可以看到Root裡面的value是“runtu”,當我們在input框裡輸入什麼,它的data裡面的值就會變成什麼。相當於我們在Vue例項模板中使用v-model,就等價於我們去繫結了:value
和 @input
。
到此,這個demo已經實現了v-model的功能。
當然,此時的template裡面可以直接將:value
和 @input
替換為v-model,效果是一樣的:
template: `
<div>
<comp-a v-model="value"></comp-a>
</div>
`,
複製程式碼
這應該是最簡單的實現v-model資料繫結的demo。只需要在一個元件裡面有個props,加上一個value,然後當元件要去修改資料的時候, $emit一個input事件,並且把新的值傳出去。這就實現了Vue裡面的資料雙向繫結。
其實,v-model指令就是在元件上加了一個props,以及增加了一個事件監聽(比如本demo中的input事件),說白了,在v-model裡面作者幫我們封裝了這個雙向繫結的邏輯,我們只管拿去用就好。
當然這個demo還可以更進一步,給變數的名稱定義一下,這樣看起來更加靈活:
const conmponent = {
model: {
prop: 'value',
event: 'change'
},
props: ['value'],
template: `
<div>
<input type="text" @input="handleInput" :value="value">
</div>
`,
methods: {
handleInput (e) {
this.$emit('change', e.target.value)
}
}
}
複製程式碼
總結
一句話總結就是:在資料渲染時使用prop渲染資料 將prop繫結到子元件自身的資料上,修改資料時更新自身資料來替代prop, watch子元件自身資料的改變,觸發事件通知父元件更改繫結到prop的資料。
面試官可能還會不厭其煩地問你,Vue資料繫結這樣做的好處是什麼?
敲黑板劃重點:父元件資料改變時,不會修改儲存prop的子元件資料,只是以子元件資料為媒介,完成對prop的雙向修改。
如果還要繼續深挖,就得搬個小板凳泡上一壺茶準備好瓜子花生,坐下來跟面試官好好聊一聊Vue的響應式原理了,Object.defineProperty() 通過 getter 和 setter 劫持了物件賦值的過程,在這個過程中可以進行更新 dom 操作等等。
當你能聊到這部分的時候,說明你對Vue的研究達到了一定的程度,面試官也能通過這個問題了解到電話那頭的你對Vue.js知識掌握的深淺,不止停留在使用API做業務開發層面。
當然,這道面試題僅僅是我此次西安電話面試的開胃菜,接下來還有更多面試題等著我去回答,此電面系列文章會第一時間更新在我的公眾號<閏土大叔>裡面,歡迎大家關注。
另外,跟大家透個底,目前為止,通過幾輪的面試,我已經成功地拿到了這家上市公司的offer。
未完待續......