2020年大廠面試指南 - Vue篇

導讀

vue的一些基礎知識,以及相關實現原理,一直是面試中比較熱門的題目,本文梳理了常見的Vue面試題。系列文章將在公衆號「前端小苑」首發,可微信掃描文章底部二維碼添加。javascript

注意:關於底層實現原理,建議最好仍是參照源碼進行學習,有些原理相對比較複雜,很難經過一篇文章去深刻理解,最終可能只是只知其一;不知其二,面試時一深刻提問,就露餡了。
本文主要是對Vue可能的面試點進行梳理,平時仍是要注重知識的積累和儲備,才能遊刃有餘的應對工做和麪試,部分題目會給出相關連接供詳細學習。css

組件通訊相關問題

1. 組件通訊方式有哪些?

父子組件通訊:html

propseventv-model.syncref$parent$children前端

非父子組件通訊:vue

$attr$listenersprovideinjecteventbus、經過根實例$root訪問、vuexdispatchbrodcastjava

通訊方式屬於較基礎的面試題,具體的可參考個人文章—— vue 組件通訊看這篇就夠了node

2. 子組件爲何不能夠修改父組件傳遞的Prop?

Vue提倡單向數據流,即父級 props 的更新會流向子組件,可是反過來則不行。這是爲了防止意外的改變父組件狀態,使得應用的數據流變得難以理解。若是破壞了單向數據流,當應用複雜時,debug 的成本會很是高。git

3. v-model是如何實現雙向綁定的?

v-model 是用來在表單控件或者組件上建立雙向綁定的,他的本質是 v-bindv-on 的語法糖,在一個組件上使用 v-model,默認會爲組件綁定名爲 valueprop 和名爲 input 的事件。
文章—— vue 組件通訊看這篇就夠了 中也有其詳細介紹github

4. Vuex和單純的全局對象有什麼區別?

Vuex和全局對象主要有兩大區別:面試

  1. Vuex 的狀態存儲是響應式的。當 Vue 組件從 store 中讀取狀態的時候,若 store 中的狀態發生變化,那麼相應的組件也會相應地獲得高效更新。
  2. 不能直接改變 store 中的狀態。改變 store 中的狀態的惟一途徑就是顯式地提交 (commit) mutation。這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。

5. 爲何 Vuex 的 mutation 中不能作異步操做?

Vuex中全部的狀態更新的惟一途徑都是mutation,異步操做經過 Action 來提交 mutation實現,這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。

每一個mutation執行完成後都會對應到一個新的狀態變動,這樣devtools就能夠打個快照存下來,而後就能夠實現 time-travel 了。若是mutation支持異步操做,就沒有辦法知道狀態是什麼時候更新的,沒法很好的進行狀態的追蹤,給調試帶來困難。 參考尤大大回答: www.zhihu.com/question/48…

聲明週期相關問題

1. vue組件有哪些聲明週期鉤子?

beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
<keep-alive> 有本身獨立的鉤子函數 activated 和 deactivated。

2. Vue 的父組件和子組件生命週期鉤子執行順序是什麼?

渲染過程:
父組件掛載完成必定是等子組件都掛載完成後,纔算是父組件掛載完,因此父組件的mounted在子組件mouted以後
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted

子組件更新過程:

  1. 影響到父組件: 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
  2. 不影響父組件: 子beforeUpdate -> 子updated

父組件更新過程:

  1. 影響到子組件: 父beforeUpdate -> 子beforeUpdate->子updated -> 父updted
  2. 不影響子組件: 父beforeUpdate -> 父updated

銷燬過程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed

看起來不少好像很難記憶,其實只要理解了,不論是哪一種狀況,都必定是父組件等待子組件完成後,纔會執行本身對應完成的鉤子,就能夠很容易記住。

相關屬性的做用 & 類似屬性對比

1. v-show 和 v-if 有哪些區別?

v-if 會在切換過程當中對條件塊的事件監聽器和子組件進行銷燬和重建,若是初始條件是false,則什麼都不作,直到條件第一次爲true時纔開始渲染模塊。
v-show 只是基於css進行切換,無論初始條件是什麼,都會渲染。
因此,v-if 切換的開銷更大,而 v-show 初始化渲染開銷更大,在須要頻繁切換,或者切換的部分dom很複雜時,使用 v-show 更合適。渲染後不多切換的則使用 v-if 更合適。

2. computed 和 watch 有什麼區別?

computed 計算屬性,是依賴其餘屬性的計算值,而且有緩存,只有當依賴的值變化時纔會更新。
watch 是在監聽的屬性發生變化時,在回調中執行一些邏輯。
因此,computed 適合在模板渲染中,某個值是依賴了其餘的響應式對象甚至是計算屬性計算而來,而 watch 適合監聽某個值的變化去完成一段複雜的業務邏輯。

3. computed vs methods

計算屬性是基於他們的響應式依賴進行緩存的,只有在依賴發生變化時,纔會計算求值,而使用 methods,每次都會執行相應的方法。

4. keep-alive 的做用是什麼?

keep-alive 能夠在組件切換時,保存其包裹的組件的狀態,使其不被銷燬,防止屢次渲染。
其擁有兩個獨立的生命週期鉤子函數 actived 和 deactived,使用 keep-alive 包裹的組件在切換時不會被銷燬,而是緩存到內存中並執行 deactived 鉤子函數,命中緩存渲染後會執行 actived 鉤子函數。

5. Vue 中 v-html 會致使什麼問題

在網站上動態渲染任意 HTML,很容易致使 XSS 攻擊。因此只能在可信內容上使用 v-html,且永遠不能用於用戶提交的內容上。

原理分析相關題目

這部分的面試題,只看答案部分是不夠的,最好結合源碼來分析,能夠有更深的理解。我在以前的文章對某些源碼作過度析的,會給出連接。

1. Vue 的響應式原理

若是面試被問到這個問題,又描述不清楚,能夠直接畫出 Vue 官方文檔的這個圖,對着圖來解釋效果會更好。

Vue 的響應式是經過 Object.defineProperty 對數據進行劫持,並結合觀察者模式實現。 Vue 利用 Object.defineProperty 建立一個 observe 來劫持監聽全部的屬性,把這些屬性所有轉爲 gettersetter。Vue 中每一個組件實例都會對應一個 watcher 實例,它會在組件渲染的過程當中把使用過的數據屬性經過 getter 收集爲依賴。以後當依賴項的 setter 觸發時,會通知 watcher,從而使它關聯的組件從新渲染。

2. Object.defineProperty有哪些缺點?

這道題目也能夠問成 「爲何vue3.0使用proxy實現響應式?」 其實都是對Object.defineProperty 和 proxy實現響應式的對比。

  1. Object.defineProperty 只能劫持對象的屬性,而 Proxy 是直接代理對象
    因爲 Object.defineProperty 只能對屬性進行劫持,須要遍歷對象的每一個屬性。而 Proxy 能夠直接代理對象。
  2. Object.defineProperty 對新增屬性須要手動進行 Observe, 因爲 Object.defineProperty 劫持的是對象的屬性,因此新增屬性時,須要從新遍歷對象,對其新增屬性再使用 Object.defineProperty 進行劫持。 也正是由於這個緣由,使用 Vue 給 data 中的數組或對象新增屬性時,須要使用 vm.$set 才能保證新增的屬性也是響應式的。
  3. Proxy 支持13種攔截操做,這是 defineProperty 所不具備的。
  4. 新標準性能紅利
    Proxy 做爲新標準,長遠來看,JS引擎會繼續優化 Proxy ,但 gettersetter 基本不會再有針對性優化。
  5. Proxy 兼容性差 目前並無一個完整支持 Proxy 全部攔截方法的Polyfill方案

更詳細的對比,能夠查看個人文章 爲何Vue3.0再也不使用defineProperty實現數據監聽?

3. Vue中如何檢測數組變化?

Vue 的 Observer 對數組作了單獨的處理,對數組的方法進行編譯,並賦值給數組屬性的 __proto__ 屬性上,由於原型鏈的機制,找到對應的方法就不會繼續往上找了。編譯方法中會對一些會增長索引的方法(pushunshiftsplice)進行手動 observe。 具體一樣能夠參考個人這篇文章 爲何Vue3.0再也不使用defineProperty實現數據監聽?,裏面有詳細的源碼分析。

4. 組件的 data 爲何要寫成函數形式?

Vue 的組件都是可複用的,一個組件建立好後,能夠在多個地方複用,而無論複用多少次,組件內的 data 都應該是相互隔離,互不影響的,因此組件每複用一次,data 就應該複用一次,每一處複用組件的 data 改變應該對其餘複用組件的數據不影響。
爲了實現這樣的效果,data 就不能是單純的對象,而是以一個函數返回值的形式,因此每一個組件實例能夠維護獨立的數據拷貝,不會相互影響。

5. nextTick是作什麼用的,其原理是什麼?

能回答清楚這道問題的前提,是清楚 EventLoop 過程。
在下次 DOM 更新循環結束後執行延遲迴調,在修改數據以後當即使用 nextTick 來獲取更新後的 DOM。
nextTick 對於 micro task 的實現,會先檢測是否支持 Promise,不支持的話,直接指向 macro task,而 macro task 的實現,優先檢測是否支持 setImmediate(高版本IE和Etage支持),不支持的再去檢測是否支持 MessageChannel,若是仍不支持,最終降級爲 setTimeout 0;
默認的狀況,會先以 micro task 方式執行,由於 micro task 能夠在一次 tick 中所有執行完畢,在一些有重繪和動畫的場景有更好的性能。
可是因爲 micro task 優先級較高,在某些狀況下,可能會在事件冒泡過程當中觸發,致使一些問題(能夠參考 Vue 這個 issue:github.com/vuejs/vue/i…),因此有些地方會強制使用 macro task (如 v-on)。

注意:之因此將 nextTick 的回調函數放入到數組中一次性執行,而不是直接在 nextTick 中執行回調函數,是爲了保證在同一個tick內屢次執行了 nextTcik,不會開啓多個異步任務,而是把這些異步任務都壓成一個同步任務,在下一個tick內執行完畢。

6. Vue 的模板編譯原理

vue模板的編譯過程分爲3個階段:

  1. 第一步:解析
    將模板字符串解析生成 AST,生成的AST 元素節點總共有 3 種類型,1 爲普通元素, 2 爲表達式,3爲純文本。
  2. 第二步:優化語法樹
    Vue 模板中並非全部數據都是響應式的,有不少數據是首次渲染後就永遠不會變化的,那麼這部分數據生成的 DOM 也不會變化,咱們能夠在 patch 的過程跳過對他們的比對。

此階段會深度遍歷生成的 AST 樹,檢測它的每一顆子樹是否是靜態節點,若是是靜態節點則它們生成 DOM 永遠不須要改變,這對運行時對模板的更新起到極大的優化做用。

  1. 生成代碼
const code = generate(ast, options)
複製代碼

經過 generate 方法,將ast生成 render 函數。 更多關於 AST,Vue 模板編譯原理,以及和 AST 相關的 Babel 工做原理等,我在 掌握了AST,不再怕被問babel,vue編譯,Prettier等原理 中作了詳細介紹。

7. v-for 中 key 的做用是什麼?

清晰回答這道問題,須要先清楚 Vue 的 diff 過程,關於 diff 原理,推薦一篇文章 my.oschina.net/u/3060934/b…

key 是給每一個 vnode 指定的惟一 id,在同級的 vnode diff 過程當中,能夠根據 key 快速的對比,來判斷是否爲相同節點,而且利用 key 的惟一性能夠生成 map 來更快的獲取相應的節點。
另外指定 key 後,就再也不採用「就地複用」策略了,能夠保證渲染的準確性。

8. 爲何 v-for 和 v-if 不建議用在一塊兒

v-forv-if 處於同一個節點時,v-for 的優先級比 v-if 更高,這意味着 v-if 將分別重複運行於每一個 v-for 循環中。若是要遍歷的數組很大,而真正要展現的數據不多時,這將形成很大的性能浪費。
這種場景建議使用 computed,先對數據進行過濾。

路由相關問題

1. Vue-router 導航守衛有哪些

全局前置/鉤子:beforeEach、beforeResolve、afterEach

路由獨享的守衛:beforeEnter

組件內的守衛:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

完整的導航解析流程以下:

  1. 導航被觸發。
  2. 在失活的組件裏調用離開守衛。
  3. 調用全局的 beforeEach 守衛。
  4. 在重用的組件裏調用 beforeRouteUpdate 守衛 (2.2+)。
  5. 在路由配置裏調用 beforeEnter。
  6. 解析異步路由組件。
  7. 在被激活的組件裏調用 beforeRouteEnter。
  8. 調用全局的 beforeResolve 守衛 (2.5+)。
  9. 導航被確認。
  10. 調用全局的 afterEach 鉤子。
  11. 觸發 DOM 更新。
  12. 用建立好的實例調用 beforeRouteEnter 守衛中傳給 next 的回調函數。

2. vue-router hash 模式和 history 模式有什麼區別?

區別:

  1. url 展現上,hash 模式有「#」,history 模式沒有
  2. 刷新頁面時,hash 模式能夠正常加載到 hash 值對應的頁面,而 history 沒有處理的話,會返回 404,通常須要後端將全部頁面都配置重定向到首頁路由。
  3. 兼容性。hash 能夠支持低版本瀏覽器和 IE。

3. vue-router hash 模式和 history 模式是如何實現的?

hash 模式:
#後面 hash 值的變化,不會致使瀏覽器向服務器發出請求,瀏覽器不發出請求,就不會刷新頁面。同時經過監聽 hashchange 事件能夠知道 hash 發生了哪些變化,而後根據 hash 變化來實現更新頁面部份內容的操做。

history 模式:
history 模式的實現,主要是 HTML5 標準發佈的兩個 API,pushStatereplaceState,這兩個 API 能夠在改變 url,可是不會發送請求。這樣就能夠監聽 url 變化來實現更新頁面部份內容的操做。

關於 vue-router 部分,推薦文章 juejin.im/post/5b10b4…

本文是系列文章《2020年大廠面試指南》的第一篇文章,後續系列文章更新,請關注個人公衆號【前端小苑】。有任何問題也歡迎加我我的微信yu_shihu_進行交流

相關文章
相關標籤/搜索