vue的一些基礎知識,以及相關實現原理,一直是面試中比較熱門的題目,本文梳理了常見的Vue面試題。系列文章將在公衆號「前端小苑」首發,可微信掃描文章底部二維碼添加。javascript
注意:關於底層實現原理,建議最好仍是參照源碼進行學習,有些原理相對比較複雜,很難經過一篇文章去深刻理解,最終可能只是只知其一;不知其二,面試時一深刻提問,就露餡了。
本文主要是對Vue可能的面試點進行梳理,平時仍是要注重知識的積累和儲備,才能遊刃有餘的應對工做和麪試,部分題目會給出相關連接供詳細學習。css
父子組件通訊:html
props
和 event
、v-model
、 .sync
、 ref
、 $parent
和 $children
前端
非父子組件通訊:vue
$attr
和 $listeners
、 provide
和 inject
、eventbus
、經過根實例$root
訪問、vuex
、dispatch
和 brodcast
java
通訊方式屬於較基礎的面試題,具體的可參考個人文章—— vue 組件通訊看這篇就夠了node
Vue提倡單向數據流,即父級 props
的更新會流向子組件,可是反過來則不行。這是爲了防止意外的改變父組件狀態,使得應用的數據流變得難以理解。若是破壞了單向數據流,當應用複雜時,debug 的成本會很是高。git
v-model
是用來在表單控件或者組件上建立雙向綁定的,他的本質是 v-bind
和 v-on
的語法糖,在一個組件上使用 v-model
,默認會爲組件綁定名爲 value
的 prop
和名爲 input
的事件。
文章—— vue 組件通訊看這篇就夠了 中也有其詳細介紹github
Vuex和全局對象主要有兩大區別:面試
Vuex中全部的狀態更新的惟一途徑都是mutation,異步操做經過 Action 來提交 mutation實現,這樣使得咱們能夠方便地跟蹤每個狀態的變化,從而讓咱們可以實現一些工具幫助咱們更好地瞭解咱們的應用。
每一個mutation執行完成後都會對應到一個新的狀態變動,這樣devtools就能夠打個快照存下來,而後就能夠實現 time-travel 了。若是mutation支持異步操做,就沒有辦法知道狀態是什麼時候更新的,沒法很好的進行狀態的追蹤,給調試帶來困難。 參考尤大大回答: www.zhihu.com/question/48…
beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
<keep-alive>
有本身獨立的鉤子函數 activated 和 deactivated。
渲染過程:
父組件掛載完成必定是等子組件都掛載完成後,纔算是父組件掛載完,因此父組件的mounted在子組件mouted以後
父beforeCreate -> 父created -> 父beforeMount -> 子beforeCreate -> 子created -> 子beforeMount -> 子mounted -> 父mounted
子組件更新過程:
父組件更新過程:
銷燬過程:
父beforeDestroy -> 子beforeDestroy -> 子destroyed -> 父destroyed
看起來不少好像很難記憶,其實只要理解了,不論是哪一種狀況,都必定是父組件等待子組件完成後,纔會執行本身對應完成的鉤子,就能夠很容易記住。
v-if
會在切換過程當中對條件塊的事件監聽器和子組件進行銷燬和重建,若是初始條件是false,則什麼都不作,直到條件第一次爲true時纔開始渲染模塊。
v-show
只是基於css進行切換,無論初始條件是什麼,都會渲染。
因此,v-if
切換的開銷更大,而 v-show
初始化渲染開銷更大,在須要頻繁切換,或者切換的部分dom很複雜時,使用 v-show
更合適。渲染後不多切換的則使用 v-if
更合適。
computed
計算屬性,是依賴其餘屬性的計算值,而且有緩存,只有當依賴的值變化時纔會更新。
watch
是在監聽的屬性發生變化時,在回調中執行一些邏輯。
因此,computed
適合在模板渲染中,某個值是依賴了其餘的響應式對象甚至是計算屬性計算而來,而 watch
適合監聽某個值的變化去完成一段複雜的業務邏輯。
計算屬性是基於他們的響應式依賴進行緩存的,只有在依賴發生變化時,纔會計算求值,而使用 methods
,每次都會執行相應的方法。
keep-alive
能夠在組件切換時,保存其包裹的組件的狀態,使其不被銷燬,防止屢次渲染。
其擁有兩個獨立的生命週期鉤子函數 actived 和 deactived,使用 keep-alive
包裹的組件在切換時不會被銷燬,而是緩存到內存中並執行 deactived 鉤子函數,命中緩存渲染後會執行 actived 鉤子函數。
在網站上動態渲染任意 HTML,很容易致使 XSS 攻擊。因此只能在可信內容上使用 v-html,且永遠不能用於用戶提交的內容上。
這部分的面試題,只看答案部分是不夠的,最好結合源碼來分析,能夠有更深的理解。我在以前的文章對某些源碼作過度析的,會給出連接。
若是面試被問到這個問題,又描述不清楚,能夠直接畫出 Vue 官方文檔的這個圖,對着圖來解釋效果會更好。
Object.defineProperty
對數據進行劫持,並結合觀察者模式實現。 Vue 利用
Object.defineProperty
建立一個
observe
來劫持監聽全部的屬性,把這些屬性所有轉爲
getter
和
setter
。Vue 中每一個組件實例都會對應一個
watcher
實例,它會在組件渲染的過程當中把使用過的數據屬性經過
getter
收集爲依賴。以後當依賴項的
setter
觸發時,會通知
watcher
,從而使它關聯的組件從新渲染。
這道題目也能夠問成 「爲何vue3.0使用proxy實現響應式?」 其實都是對Object.defineProperty 和 proxy實現響應式的對比。
Object.defineProperty
只能劫持對象的屬性,而 Proxy
是直接代理對象Object.defineProperty
只能對屬性進行劫持,須要遍歷對象的每一個屬性。而 Proxy
能夠直接代理對象。Object.defineProperty
對新增屬性須要手動進行 Observe
, 因爲 Object.defineProperty
劫持的是對象的屬性,因此新增屬性時,須要從新遍歷對象,對其新增屬性再使用 Object.defineProperty
進行劫持。 也正是由於這個緣由,使用 Vue 給 data
中的數組或對象新增屬性時,須要使用 vm.$set
才能保證新增的屬性也是響應式的。Proxy
支持13種攔截操做,這是 defineProperty
所不具備的。Proxy
做爲新標準,長遠來看,JS引擎會繼續優化 Proxy
,但 getter
和 setter
基本不會再有針對性優化。Proxy
兼容性差 目前並無一個完整支持 Proxy
全部攔截方法的Polyfill方案更詳細的對比,能夠查看個人文章 爲何Vue3.0再也不使用defineProperty實現數據監聽?
Vue 的 Observer
對數組作了單獨的處理,對數組的方法進行編譯,並賦值給數組屬性的 __proto__
屬性上,由於原型鏈的機制,找到對應的方法就不會繼續往上找了。編譯方法中會對一些會增長索引的方法(push
,unshift
,splice
)進行手動 observe。 具體一樣能夠參考個人這篇文章 爲何Vue3.0再也不使用defineProperty實現數據監聽?,裏面有詳細的源碼分析。
Vue 的組件都是可複用的,一個組件建立好後,能夠在多個地方複用,而無論複用多少次,組件內的 data
都應該是相互隔離,互不影響的,因此組件每複用一次,data
就應該複用一次,每一處複用組件的 data
改變應該對其餘複用組件的數據不影響。
爲了實現這樣的效果,data
就不能是單純的對象,而是以一個函數返回值的形式,因此每一個組件實例能夠維護獨立的數據拷貝,不會相互影響。
能回答清楚這道問題的前提,是清楚 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內執行完畢。
vue模板的編譯過程分爲3個階段:
此階段會深度遍歷生成的 AST 樹,檢測它的每一顆子樹是否是靜態節點,若是是靜態節點則它們生成 DOM 永遠不須要改變,這對運行時對模板的更新起到極大的優化做用。
const code = generate(ast, options)
複製代碼
經過 generate
方法,將ast生成 render
函數。 更多關於 AST,Vue 模板編譯原理,以及和 AST 相關的 Babel 工做原理等,我在 掌握了AST,不再怕被問babel,vue編譯,Prettier等原理 中作了詳細介紹。
清晰回答這道問題,須要先清楚 Vue 的 diff 過程,關於 diff 原理,推薦一篇文章 my.oschina.net/u/3060934/b…
key
是給每一個 vnode
指定的惟一 id
,在同級的 vnode
diff 過程當中,能夠根據 key
快速的對比,來判斷是否爲相同節點,而且利用 key
的惟一性能夠生成 map
來更快的獲取相應的節點。
另外指定 key
後,就再也不採用「就地複用」策略了,能夠保證渲染的準確性。
當 v-for
和 v-if
處於同一個節點時,v-for
的優先級比 v-if
更高,這意味着 v-if
將分別重複運行於每一個 v-for
循環中。若是要遍歷的數組很大,而真正要展現的數據不多時,這將形成很大的性能浪費。
這種場景建議使用 computed
,先對數據進行過濾。
全局前置/鉤子:beforeEach、beforeResolve、afterEach
路由獨享的守衛:beforeEnter
組件內的守衛:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave
完整的導航解析流程以下:
區別:
hash 模式:
#
後面 hash 值的變化,不會致使瀏覽器向服務器發出請求,瀏覽器不發出請求,就不會刷新頁面。同時經過監聽 hashchange 事件能夠知道 hash 發生了哪些變化,而後根據 hash 變化來實現更新頁面部份內容的操做。
history 模式:
history 模式的實現,主要是 HTML5 標準發佈的兩個 API,pushState
和 replaceState
,這兩個 API 能夠在改變 url,可是不會發送請求。這樣就能夠監聽 url 變化來實現更新頁面部份內容的操做。
關於 vue-router
部分,推薦文章 juejin.im/post/5b10b4…
本文是系列文章《2020年大廠面試指南》的第一篇文章,後續系列文章更新,請關注個人公衆號【前端小苑】。有任何問題也歡迎加我我的微信yu_shihu_進行交流