拓展:Vue.js 面試衫冻、常見問題答疑
在過去的很多面試中,我會經(jīng)常問候選人一些關(guān)于 Vue.js 的問題谒出。這些問題從題面來看很簡單隅俘,但仔細(xì)想又不是那么簡單,不同的人笤喳,會答出不同的層次为居,從而更好地了解一個人對 Vue.js 的理解程度。
題目
v-show 與 v-if 區(qū)別
第一題應(yīng)該是最簡單的杀狡,提這個問題颜骤,也是想讓候選人不那么緊張,因?yàn)榈灿眠^ Vue.js捣卤,多少知道 v-show
和 v-if
的區(qū)別忍抽,否則就沒得聊了。不過這最簡單的一道題董朝,有三個層次鸠项,我會逐一追問。首先子姜,基本所有人都會說到:
v-show
只是 CSS 級別的 display: none;
和 display: block;
之間的切換祟绊,而 v-if
決定是否會選擇代碼塊的內(nèi)容(或組件)。
回答這些哥捕,已經(jīng)可以得到 50 分了牧抽,緊接著我會追問,什么時候用 v-show遥赚,什么時候用 v-if 扬舒?到這里一部分人會比較吞吐,可能是知道凫佛,但表達(dá)不出來讲坎。我比較傾向的回答是:
頻繁操作時,使用 v-show
愧薛,一次性渲染完的晨炕,使用 v-if
,只要意思對就好毫炉。
第二問可以得到 80 分了瓮栗,最后一問很少有人能答上:**那使用 v-if
在性能優(yōu)化上有什么經(jīng)驗(yàn)?**這是一個加分項(xiàng),要對 Vue.js 的組件編譯有一定的理解费奸。說一下期望的答案:
因?yàn)楫?dāng) v-if="false"
時鲸郊,內(nèi)部組件是不會渲染的,所以在特定條件才渲染部分組件(或內(nèi)容)時货邓,可以先將條件設(shè)置為 false
秆撮,需要時(或異步,比如 $nextTick)再設(shè)置為 true
换况,這樣可以優(yōu)先渲染重要的其它內(nèi)容职辨,合理利用,可以進(jìn)行性能優(yōu)化戈二。
綁定 class 的數(shù)組用法
動態(tài)綁定 class 應(yīng)該不陌生吧舒裤,這也是最基本的,但是這個問題卻有點(diǎn)繞觉吭,什么叫**綁定 class 的數(shù)組用法腾供?**我們看一下,最常用的綁定 class 怎么寫:
<template>
<div :class="{show: isShow}">內(nèi)容</div>
</template>
<script>
export default {
data () {
return {
isShow: true
}
}
}
</script>
綁定 class 的對象用法能滿足大部分業(yè)務(wù)需求鲜滩,不過伴鳖,在復(fù)雜的場景下,會用到數(shù)組徙硅,來看示例:
<template>
<div :class="classes"></div>
</template>
<script>
export default {
computed: {
classes () {
return [
`${prefixCls}`,
`${prefixCls}-${this.type}`,
{
[`${prefixCls}-long`]: this.long,
[`${prefixCls}-${this.shape}`]: !!this.shape,
[`${prefixCls}-${this.size}`]: this.size !== 'default',
[`${prefixCls}-loading`]: this.loading != null && this.loading,
[`${prefixCls}-icon-only`]: !this.showSlot && (!!this.icon || !!this.customIcon || this.loading),
[`${prefixCls}-ghost`]: this.ghost
}
];
}
}
}
</script>
示例來自 iView 的 Button 組件榜聂,可以看到,數(shù)組里嗓蘑,可以是固定的值须肆,還有動態(tài)值(對象)的混合。
計算屬性和 watch 的區(qū)別
回答該題前桩皿,一般都會思考一下豌汇。很多人會偏題,直接去答計算屬性和 watch 怎么用泄隔,這是不得分的拒贱,因?yàn)轭}目是問區(qū)別,并不是用法梅尤。
計算屬性是自動監(jiān)聽依賴值的變化柜思,從而動態(tài)返回內(nèi)容岩调,監(jiān)聽是一個過程巷燥,在監(jiān)聽的值變化時,可以觸發(fā)一個回調(diào)号枕,并做一些事情缰揪。
所以區(qū)別來源于用法,只是需要動態(tài)值,那就用計算屬性钝腺;需要知道值的改變后執(zhí)行業(yè)務(wù)邏輯抛姑,才用 watch,用反或混用雖然可行艳狐,但都是不正確的用法定硝。
這個問題會延伸出幾個問題:
computed 是一個對象時,它有哪些選項(xiàng)毫目?
computed 和 methods 有什么區(qū)別蔬啡?
computed 是否能依賴其它組件的數(shù)據(jù)?
watch 是一個對象時镀虐,它有哪些選項(xiàng)箱蟆?
問題 1,已經(jīng)在 16 小節(jié)介紹過刮便,有 get 和 set 兩個選項(xiàng)空猜。
問題 2,methods 是一個方法恨旱,它可以接受參數(shù)辈毯,而 computed 不能;computed 是可以緩存的搜贤,methods 不會漓摩;一般在 v-for
里,需要根據(jù)當(dāng)前項(xiàng)動態(tài)綁定值時入客,只能用 methods 而不能用 computed管毙,因?yàn)?computed 不能傳參。
問題 3桌硫,computed 可以依賴其它 computed夭咬,甚至是其它組件的 data。
問題 4铆隘,第 16 小節(jié)也有提到卓舵,有以下常用的配置:
- handler 執(zhí)行的函數(shù)
- deep 是否深度
- immediate 是否立即執(zhí)行
事件修飾符
這個問題我會先寫一段代碼:
<custom-component>內(nèi)容</custom-component>
然后問:怎樣給這個自定義組件 custom-component 綁定一個原生
的 click 事件?
我一開始并不會問什么是事件修飾符膀钠,但是如果候選人說 <custom-component @click="xxx">
掏湾,就已經(jīng)錯了,說明它對這個沒有概念肿嘲。這里的 @click
是自定義事件 click融击,并不是原生事件 click。綁定原生的 click 是這樣的:
<custom-component @click.native="xxx">內(nèi)容</custom-component>
該問題會引申很多雳窟,比如常見的事件修飾符有哪些尊浪?如果你能說上 .exact
,說明你是個很愛探索的人,會大大加分哦拇涤。
.exact 是 Vue.js 2.5.0 新加的捣作,它允許你控制由精確的系統(tǒng)修飾符組合觸發(fā)的事件,比如:
<!-- 即使 Alt 或 Shift 被一同按下時也會觸發(fā) --> <button @click.ctrl="onClick">A</button> <!-- 有且只有 Ctrl 被按下的時候才觸發(fā) --> <button @click.ctrl.exact="onCtrlClick">A</button> <!-- 沒有任何系統(tǒng)修飾符被按下的時候才觸發(fā) --> <button @click.exact="onClick">A</button>
你可能還需要了解常用的幾個事件修飾符:
.stop
.prevent
.capture
.self
而且鹅士,事件修飾符在連用時券躁,是有先后順序的。
組件中 data 為什么是函數(shù)
為什么組件中的 data 必須是一個函數(shù)掉盅,然后 return 一個對象嘱朽,而 new Vue 實(shí)例里,data 可以直接是一個對象怔接?
因?yàn)榻M件是用來復(fù)用的搪泳,JS 里對象是引用關(guān)系,這樣作用域沒有隔離扼脐,而 new Vue 的實(shí)例岸军,是不會被復(fù)用的,因此不存在引用對象的問題瓦侮。
keep-alive 的理解
這是個概念題艰赞,主要考察候選人是否知道這個用法。簡單說肚吏,就是把一個組件的編譯緩存起來方妖。在第 14 節(jié)有過詳細(xì)介紹,也可以看看 Vue.js 的文檔罚攀。
遞歸組件的要求
回答這道題党觅,首先你得知道什么是遞歸組件。而不到 10% 的人知道遞歸組件斋泄。其實(shí)在實(shí)際業(yè)務(wù)中用的確實(shí)不多杯瞻,在獨(dú)立組件中會經(jīng)常使用,第 14 節(jié)和 15 節(jié)專門講過遞歸組件炫掐。那回到問題魁莉,遞歸組件的要求是什么?主要有兩個:
- 要給組件設(shè)置 name募胃;
- 要有一個明確的結(jié)束條件旗唁。
自定義組件的語法糖 v-model 是怎樣實(shí)現(xiàn)的
在第 16 節(jié)已經(jīng)詳細(xì)介紹過,這里的 v-model痹束,并不是給普通輸入框 <input />
用的那種 v-model检疫,而是在自定義組件上使用。既然是語法糖参袱,就能夠還原电谣,我們先還原一下:
<template>
<div>
{{ currentValue }}
<button @click="handleClick">Click</button>
</div>
</template>
<script>
export default {
props: {
value: {
type: Number,
default: 0
}
},
data () {
return {
currentValue: this.value
}
},
methods: {
handleClick () {
this.currentValue += 1;
this.$emit('input', this.currentValue);
}
},
watch: {
value (val) {
this.currentValue = val;
}
}
}
</script>
這個組件中秽梅,只有一個 props抹蚀,但是名字叫 value
剿牺,內(nèi)部還有一個 currentValue
,當(dāng)改變 currentValue 時环壤,會觸發(fā)一個自定義事件 @input
晒来,并把 currentValue 的值返回。這就是一個 v-model
的語法糖郑现,它要求 props 有一個叫 value
的項(xiàng)湃崩,同時觸發(fā)的自定義事件必須叫 input
。這樣就可以在自定義組件上用 v-model
了:
<custom-component v-model="value"></custom-component>
如果你能說到 model
選項(xiàng)接箫,絕對是加分的攒读。
Vuex 中 mutations 和 actions 的區(qū)別
主要的區(qū)別是,actions 可以執(zhí)行異步辛友。actions 是調(diào)用 mutations薄扁,而 mutations 來修改 store。
Render 函數(shù)
這是比較難的一題了废累,因?yàn)楹苌儆腥藭チ私?Vue.js 的 Render 函數(shù)邓梅,因?yàn)榛居貌坏健ender 函數(shù)的內(nèi)容本小冊已經(jīng)很深入的講解過了邑滨,遇到這個問題日缨,一般可以從這幾個方面來回答:
- 什么是 Render 函數(shù),它的使用場景是什么掖看。
- createElement 是什么匣距?
- Render 函數(shù)有哪些常用的參數(shù)?
說到 Render 函數(shù)哎壳,就要說到虛擬 DOM(Virtual DOM),Virtual DOM 并不是真正意義上的 DOM墨礁,而是一個輕量級的 JavaScript 對象,在狀態(tài)發(fā)生變化時耳峦,Virtual DOM 會進(jìn)行 Diff 運(yùn)算恩静,來更新只需要被替換的 DOM,而不是全部重繪蹲坷。
它的使用場景驶乾,就是完全發(fā)揮 JavaScript 的編程能力,有時需要結(jié)合 JSX 來使用循签。
createElement 是 Render 函數(shù)的核心级乐,它構(gòu)成了 Vue Virtual DOM 的模板,它有 3 個參數(shù):
createElement () {
// {String | Object | Function}
// 一個 HTML 標(biāo)簽县匠,組件選項(xiàng)风科,或一個函數(shù)
// 必須 return 上述其中一個
'div',
// {Object}
// 一個對應(yīng)屬性的數(shù)據(jù)對象撒轮,可選
// 您可以在 template 中使用
{
// 詳細(xì)的屬性
},
// {String | Array}
// 子節(jié)點(diǎn)(VNodes),可選
[
createElement('h1', 'hello world'),
createElement(MyComponent, {
props: {
someProps: 'foo'
}
}),
'bar'
]
}
常用的參數(shù)贼穆,主要是指上面第二個參數(shù)里的值了题山,這個比較多,得去看 Vue.js 的文檔故痊。
怎樣理解單向數(shù)據(jù)流
這個概念出現(xiàn)在組件通信顶瞳。父組件是通過 prop 把數(shù)據(jù)傳遞到子組件的铺呵,但是這個 prop 只能由父組件修改调缨,子組件不能修改,否則會報錯鸥滨。子組件想修改時戴甩,只能通過 $emit
派發(fā)一個自定義事件符喝,父組件接收到后,由父組件修改甜孤。
一般來說协饲,對于子組件想要更改父組件狀態(tài)的場景,可以有兩種方案:
-
在子組件的 data 中拷貝一份 prop课蔬,data 是可以修改的囱稽,但 prop 不能:
export default { props: { value: String }, data () { return { currentValue: this.value } } }
-
如果是對 prop 值的轉(zhuǎn)換,可以使用計算屬性:
export default { props: ['size'], computed: { normalizedSize: function () { return this.size.trim().toLowerCase(); } } }
如果你能提到 v-model 實(shí)現(xiàn)數(shù)據(jù)的雙向綁定二跋、.sync 用法战惊,會大大加分的,這些在第 16 節(jié)已經(jīng)詳細(xì)介紹過扎即。
生命周期
Vue.js 生命周期 主要有 8 個階段:
創(chuàng)建前 / 后(beforeCreate / created):在 beforeCreate 階段吞获,Vue 實(shí)例的掛載元素 el 和數(shù)據(jù)對象 data 都為 undefined,還未初始化谚鄙。在 created 階段各拷,Vue 實(shí)例的數(shù)據(jù)對象 data 有了,el 還沒有闷营。
載入前 / 后(beforeMount / mounted):在 beforeMount 階段烤黍,Vue 實(shí)例的 $el 和 data 都初始化了,但還是掛載之前為虛擬的 DOM 節(jié)點(diǎn)傻盟,data 尚未替換速蕊。在 mounted 階段,Vue 實(shí)例掛載完成娘赴,data 成功渲染规哲。
更新前 / 后(beforeUpdate / updated):當(dāng) data 變化時,會觸發(fā) beforeUpdate 和 updated 方法诽表。這兩個不常用唉锌,且不推薦使用隅肥。
銷毀前 / 后(beforeDestroy / destroyed):beforeDestroy 是在 Vue 實(shí)例銷毀前觸發(fā),一般在這里要通過 removeEventListener 解除手動綁定的事件袄简。實(shí)例銷毀后腥放,觸發(fā) destroyed。
組件間通信
本小冊一半的篇幅都在講組件的通信痘番,如果能把這些都吃透捉片,基本上 Vue.js 的面試就穩(wěn)了平痰。
這個問題看似簡單汞舱,卻比較大,回答時宗雇,可以拆分為幾種場景:
-
父子通信:
父向子傳遞數(shù)據(jù)是通過 props昂芜,子向父是通過 events(parent / $children)赔蒲;
ref
也可以訪問組件實(shí)例泌神;provide / inject API。 -
兄弟通信:
Bus舞虱;Vuex欢际;
-
跨級通信:
Bus;Vuex矾兜;provide / inject API损趋。
除了常規(guī)的通信方法,本冊介紹的 dispatch / broadcast 和 findComponents 系列方法也可以說的椅寺,如果能說到這些浑槽,說明你對 Vue.js 組件已經(jīng)有較深入的研究。
路由的跳轉(zhuǎn)方式
一般有兩種:
- 通過
<router-link to="home">
返帕,router-link 標(biāo)簽會渲染為<a>
標(biāo)簽桐玻,在 template 中的跳轉(zhuǎn)都是用這種; - 另一種是編程式導(dǎo)航荆萤,也就是通過 JS 跳轉(zhuǎn)镊靴,比如
router.push('/home')
。
Vue.js 2.x 雙向綁定原理
這個問題幾乎是面試必問的链韭,回答也是有深有淺偏竟。基本上要知道核心的 API 是通過 Object.defineProperty()
來劫持各個屬性的 setter / getter梧油,在數(shù)據(jù)變動時發(fā)布消息給訂閱者苫耸,觸發(fā)相應(yīng)的監(jiān)聽回調(diào),這也是為什么 Vue.js 2.x 不支持 IE8 的原因(IE 8 不支持此 API儡陨,且無法通過 polyfill 實(shí)現(xiàn))褪子。
Vue.js 文檔已經(jīng)對 深入響應(yīng)式原理 解釋的很透徹了量淌。
什么是 MVVM,與 MVC 有什么區(qū)別
MVVM 模式是由經(jīng)典的軟件架構(gòu) MVC 衍生來的嫌褪。當(dāng) View(視圖層)變化時呀枢,會自動更新到 ViewModel(視圖模型),反之亦然笼痛。View 和 ViewModel 之間通過雙向綁定(data-binding)建立聯(lián)系裙秋。與 MVC 不同的是,它沒有 Controller 層缨伊,而是演變?yōu)?ViewModel摘刑。
ViewModel 通過雙向數(shù)據(jù)綁定把 View 層和 Model 層連接了起來,而 View 和 Model 之間的同步工作是由 Vue.js 完成的刻坊,我們不需要手動操作 DOM枷恕,只需要維護(hù)好數(shù)據(jù)狀態(tài)。
結(jié)語
一個人的簡歷谭胚,是由簡單到復(fù)雜再到簡單徐块,技術(shù)是無止盡的,接觸的越多灾而,越能感到自己的渺小胡控。