本文首發於微信公衆號「程序員面試官」前端
Vue框架部分咱們會涉及一些高頻且有必定探討價值的面試題,咱們不會涉及一些很是初級的在官方文檔就能查看的純記憶性質的面試題,好比:vue
首先,上述類型的面試題在文檔中可查,沒有比官方文檔更權威的答案了,其次這種問題沒有太大價值,除了考察候選人的記憶力,最後,這種面試題只要用過vue的都知道,沒有必要佔用咱們的篇幅.node
咱們的問題並很少,可是難度可能會高一些,若是你真的搞懂了這些問題,在絕大多數狀況下會有觸類旁通的效果,能夠說基本能拿下Vue相關的全部重要知識點了.程序員
MVVM 模式,顧名思義即 Model-View-ViewModel 模式。它萌芽於2005年微軟推出的基於 Windows 的用戶界面框架 WPF ,前端最先的 MVVM 框架 knockout 在2010年發佈。面試
Model 層: 對應數據層的域模型,它主要作域模型的同步。經過 Ajax/fetch 等 API 完成客戶端和服務端業務 Model 的同步。在層間關係裏,它主要用於抽象出 ViewModel 中視圖的 Model。算法
View 層:做爲視圖模板存在,在 MVVM 裏,整個 View 是一個動態模板。除了定義結構、佈局外,它展現的是 ViewModel 層的數據和狀態。View 層不負責處理狀態,View 層作的是 數據綁定的聲明、 指令的聲明、 事件綁定的聲明。vuex
ViewModel 層:把 View 須要的層數據暴露,並對 View 層的 數據綁定聲明、 指令聲明、 事件綁定聲明 負責,也就是處理 View 層的具體業務邏輯。ViewModel 底層會作好綁定屬性的監聽。當 ViewModel 中數據變化,View 層會獲得更新;而當 View 中聲明瞭數據的雙向綁定(一般是表單元素),框架也會監聽 View 層(表單)值的變化。一旦值變化,View 層綁定的 ViewModel 中的數據也會獲得自動更新。vue-cli
優勢:npm
缺點:數組
Vue 實例有一個完整的生命週期,也就是從開始建立、初始化數據、編譯模版、掛載Dom -> 渲染、更新 -> 渲染、卸載等一系列過程,咱們稱這是Vue的生命週期。
生命週期 | 描述 |
---|---|
beforeCreate | 組件實例被建立之初,組件的屬性生效以前 |
created | 組件實例已經徹底建立,屬性也綁定,但真實dom尚未生成,$el 還不可用 |
beforeMount | 在掛載開始以前被調用:相關的 render 函數首次被調用 |
mounted | el 被新建立的 vm.$el 替換,並掛載到實例上去以後調用該鉤子 |
beforeUpdate | 組件數據更新以前調用,發生在虛擬 DOM 打補丁以前 |
update | 組件數據更新以後 |
activited | keep-alive專屬,組件被激活時調用 |
deadctivated | keep-alive專屬,組件被銷燬時調用 |
beforeDestory | 組件銷燬前調用 |
destoryed | 組件銷燬後調用 |
官方實例的異步請求是在mounted生命週期中調用的,而實際上也能夠在created生命週期中調用。
Vue組件通訊的方法以下:
props/$emit+v-on
: 經過props將數據自上而下傳遞,而經過$emit和v-on來向上傳遞信息。$attrs/$listeners
: Vue2.4中加入的$attrs/$listeners
能夠進行跨級的組件通訊還有一些用solt插槽或者ref實例進行通訊的,使用場景過於有限就不贅述了。
computed:
computed
是計算屬性,也就是計算值,它更多用於計算值的場景computed
具備緩存性,computed的值在getter執行後是會緩存的,只有在它依賴的屬性值改變以後,下一次獲取computed的值時纔會從新調用對應的getter來計算computed
適用於計算比較消耗性能的計算場景watch:
props
$emit
或者本組件的值,當數據變化時來執行回調進行後續操做小結:
利用Object.defineProperty
劫持對象的訪問器,在屬性值發生變化時咱們能夠獲取變化,而後根據變化進行後續響應,在vue3.0中經過Proxy代理對象進行相似的操做。
// 這是將要被劫持的對象 const data = { name: '', }; function say(name) { if (name === '古天樂') { console.log('給你們推薦一款超好玩的遊戲'); } else if (name === '渣渣輝') { console.log('戲我演過不少,可遊戲我只玩貪玩懶月'); } else { console.log('來作個人兄弟'); } } // 遍歷對象,對其屬性值進行劫持 Object.keys(data).forEach(function(key) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { console.log('get'); }, set: function(newVal) { // 當屬性值發生變化時咱們能夠進行額外操做 console.log(`你們好,我係${newVal}`); say(newVal); }, }); }); data.name = '渣渣輝'; //你們好,我係渣渣輝 //戲我演過不少,可遊戲我只玩貪玩懶月 複製代碼
Proxy的優點以下:
Object.defineProperty
不具有的Object.defineProperty
只能遍歷對象屬性直接修改Object.defineProperty的優點以下:
響應式系統簡述:
優勢:
缺點:
詳細實現見虛擬DOM原理?
考點: Vue的變化偵測原理
前置知識: 依賴收集、虛擬DOM、響應式系統
現代前端框架有兩種方式偵測變化,一種是pull一種是push
pull: 其表明爲React,咱們能夠回憶一下React是如何偵測到變化的,咱們一般會用setState
API顯式更新,而後React會進行一層層的Virtual Dom Diff操做找出差別,而後Patch到DOM上,React從一開始就不知道究竟是哪發生了變化,只是知道「有變化了」,而後再進行比較暴力的Diff操做查找「哪發生變化了」,另一個表明就是Angular的髒檢查操做。
push: Vue的響應式系統則是push的表明,當Vue程序初始化的時候就會對數據data進行依賴的收集,一但數據發生變化,響應式系統就會馬上得知,所以Vue是一開始就知道是「在哪發生變化了」,可是這又會產生一個問題,若是你熟悉Vue的響應式系統就知道,一般一個綁定一個數據就須要一個Watcher,一但咱們的綁定細粒度太高就會產生大量的Watcher,這會帶來內存以及依賴追蹤的開銷,而細粒度太低會沒法精準偵測變化,所以Vue的設計是選擇中等細粒度的方案,在組件級別進行push偵測的方式,也就是那套響應式系統,一般咱們會第一時間偵測到發生變化的組件,而後在組件內部進行Virtual Dom Diff獲取更加具體的差別,而Virtual Dom Diff則是pull操做,Vue是push+pull結合的方式進行變化偵測的.
考點: Vue的變化偵測原理
前置知識: 依賴收集、虛擬DOM、響應式系統
根本緣由是Vue與React的變化偵測方式有所不一樣
React是pull的方式偵測變化,當React知道發生變化後,會使用Virtual Dom Diff進行差別檢測,可是不少組件其實是確定不會發生變化的,這個時候須要用shouldComponentUpdate進行手動操做來減小diff,從而提升程序總體的性能.
Vue是pull+push的方式偵測變化的,在一開始就知道那個組件發生了變化,所以在push的階段並不須要手動控制diff,而組件內部採用的diff方式其實是能夠引入相似於shouldComponentUpdate相關生命週期的,可是一般合理大小的組件不會有過量的diff,手動優化的價值有限,所以目前Vue並無考慮引入shouldComponentUpdate這種手動優化的生命週期.
key
是爲Vue中的vnode標記的惟一id,經過這個key,咱們的diff操做能夠更準確、更快速
diff算法的過程當中,先會進行新舊節點的首尾交叉對比,當沒法匹配的時候會用新節點的key
與舊節點進行比對,而後超出差別.
diff程能夠歸納爲:oldCh和newCh各有兩個頭尾的變量StartIdx和EndIdx,它們的2個變量相互比較,一共有4種比較方式。若是4種比較都沒匹配,若是設置了key,就會用key進行比較,在比較的過程當中,變量會往中間靠,一旦StartIdx>EndIdx代表oldCh和newCh至少有一個已經遍歷完了,就會結束比較,這四種比較方式就是首、尾、舊尾新頭、舊頭新尾.
key
,那麼vue會選擇複用節點(Vue的就地更新策略),致使以前節點的狀態被保留下來,會產生一系列的bug.