「觀感度:🌟🌟🌟🌟🌟」html
「口味:紅燒豬蹄」前端
「烹飪時間:15min」vue
本文已收錄在Github
github.com/Geekhyt,歡迎Star。node
從鏡片的厚度和黃黑相見的格子襯衫我察覺到,面前坐着的這位面試官應該是來者不善。我像以往同樣,準備花3分鐘的時間進行自我介紹。在此期間,爲了不尷尬,我盯着面試官的眉毛中間,不過面試官明顯對個人經歷不是很感興趣。他在1分半的時候打斷了我。react
❝你以爲本身最擅長的技術棧是什麼?git
❞
Vue吧,我很喜歡尤大,最近剛發佈了Vue的首部紀錄片,真的很好看。github
MVVM是Model-View-ViewModel
縮寫,也就是把MVC
中的Controller
演變成ViewModel
。Model層表明數據模型,View表明UI組件,ViewModel是View和Model層的橋樑,數據會綁定到viewModel層並自動將數據渲染到頁面中,視圖變化的時候會通知viewModel層更新數據。面試
Vue在初始化數據時,會使用Object.defineProperty
從新定義data中的全部屬性,當頁面使用對應屬性時,首先會進行依賴收集(收集當前組件的watcher
)若是屬性發生變化會通知相關依賴進行更新操做(發佈訂閱
)。正則表達式
(還好我有看,這個難不倒我)算法
Vue3.x改用Proxy
替代Object.defineProperty。由於Proxy能夠直接監聽對象和數組的變化,而且有多達13種攔截方法。而且做爲新標準將受到瀏覽器廠商重點持續的性能優化。
❝Proxy只會代理對象的第一層,那麼Vue3又是怎樣處理這個問題的呢?
❞
(很簡單啊)
判斷當前Reflect.get的返回值是否爲Object,若是是則再經過reactive
方法作代理, 這樣就實現了深度觀測。
❝監測數組的時候可能觸發屢次get/set,那麼如何防止觸發屢次呢?
❞
咱們能夠判斷key是否爲當前被代理對象target自身屬性,也能夠判斷舊值與新值是否相等,只有知足以上兩個條件之一時,纔有可能執行trigger。
面試官擡起了頭。內心暗想
(這小子還行,比上兩個強,應該是多多少少看過Vue3的源碼了)
使用了函數劫持的方式,重寫了數組的方法,Vue將data中的數組進行了原型鏈重寫,指向了本身定義的數組原型方法。這樣當調用數組api時,能夠通知依賴更新。若是數組中包含着引用類型,會對數組中的引用類型再次遞歸遍歷進行監控。這樣就實現了監測數組變化。
(能問到這的面試官都比較注重深度,這些常規操做要記牢)
(原型鏈的細節能夠參考個人另外一篇專欄)
在下次 DOM 更新循環結束以後執行延遲迴調。nextTick主要使用了宏任務和微任務。根據執行環境分別嘗試採用
定義了一個異步方法,屢次調用nextTick會將方法存入隊列中,經過這個異步方法清空當前隊列。
(關於宏任務和微任務以及事件循環能夠參考個人另兩篇專欄)
(看到這你就會發現,其實問框架最終仍是考驗你的原生JavaScript功底)
beforeCreate
是new Vue()以後觸發的第一個鉤子,在當前階段data、methods、computed以及watch上的數據和方法都不能被訪問。
created
在實例建立完成後發生,當前階段已經完成了數據觀測,也就是可使用數據,更改數據,在這裏更改數據不會觸發updated函數。能夠作一些初始數據的獲取,在當前階段沒法與Dom進行交互,若是非要想,能夠經過vm.$nextTick來訪問Dom。
beforeMount
發生在掛載以前,在這以前template模板已導入渲染函數編譯。而當前階段虛擬Dom已經建立完成,即將開始渲染。在此時也能夠對數據進行更改,不會觸發updated。
mounted
在掛載完成後發生,在當前階段,真實的Dom掛載完畢,數據完成雙向綁定,能夠訪問到Dom節點,使用$refs屬性對Dom進行操做。
beforeUpdate
發生在更新以前,也就是響應式數據發生更新,虛擬dom從新渲染以前被觸發,你能夠在當前階段進行更改數據,不會形成重渲染。
updated
發生在更新完成以後,當前階段組件Dom已完成更新。要注意的是避免在此期間更改數據,由於這可能會致使無限循環的更新。
beforeDestroy
發生在實例銷燬以前,在當前階段實例徹底能夠被使用,咱們能夠在這時進行善後收尾工做,好比清除計時器。
destroyed
發生在實例銷燬以後,這個時候只剩下了dom空殼。組件已被拆解,數據綁定被卸除,監聽被移出,子實例也通通被銷燬。
(關於Vue的生命週期詳解感興趣的也請移步個人另外一篇專欄)
接口請求通常放在mounted
中,但須要注意的是服務端渲染時不支持mounted,須要放到created
中。
Computed
本質是一個具有緩存的watcher,依賴的屬性發生變化就會更新視圖。 適用於計算比較消耗性能的計算場景。當表達式過於複雜時,在模板中放入過多邏輯會讓模板難以維護,能夠將複雜的邏輯放入計算屬性中處理。
Watch
沒有緩存性,更多的是觀察的做用,能夠監聽某些數據執行回調。當咱們須要深度監聽對象中的屬性時,能夠打開deep:true
選項,這樣便會對對象中的每一項進行監聽。這樣會帶來性能問題,優化的話可使用字符串形式
監聽,若是沒有寫到組件中,不要忘記使用unWatch手動註銷
哦。
當條件不成立時,v-if
不會渲染DOM元素,v-show
操做的是樣式(display),切換當前DOM的顯示和隱藏。
一個組件被複用屢次的話,也就會建立多個實例。本質上,這些實例用的都是同一個構造函數
。若是data是對象的話,對象屬於引用類型,會影響到全部的實例。因此爲了保證組件不一樣的實例之間data不衝突,data必須是一個函數。
v-model
本質就是一個語法糖,能夠當作是value + input
方法的語法糖。 能夠經過model屬性的prop
和event
屬性來進行自定義。原生的v-model,會根據標籤的不一樣生成不一樣的事件和屬性。
原生事件綁定是經過addEventListener
綁定給真實元素的,組件事件綁定是經過Vue自定義的$on
實現的。
❝面試官:(這小子基礎還能夠,接下來我得上上難度了)
❞
簡單說,Vue的編譯過程就是將template
轉化爲render
函數的過程。會經歷如下階段:
首先解析模版,生成AST語法樹
(一種用JavaScript對象的形式來描述整個模板)。 使用大量的正則表達式對模板進行解析,遇到標籤、文本的時候都會執行對應的鉤子進行相關處理。
Vue的數據是響應式的,但其實模板中並非全部的數據都是響應式的。有一些數據首次渲染後就不會再變化,對應的DOM也不會變化。那麼優化過程就是深度遍歷AST樹,按照相關條件對樹節點進行標記。這些被標記的節點(靜態節點)咱們就能夠跳過對它們的比對
,對運行時的模板起到很大的優化做用。
編譯的最後一步是將優化後的AST樹轉換爲可執行的代碼
。
❝面試官:(精神小夥啊,有點東西,難度提高,不信難不倒你)
❞
簡單來講,diff算法有如下過程
正常Diff兩個樹的時間複雜度是O(n^3)
,但實際狀況下咱們不多會進行跨層級的移動DOM
,因此Vue將Diff進行了優化,從O(n^3) -> O(n)
,只有當新舊children都爲多個子節點時才須要用核心的Diff算法進行同層級比較。
Vue2的核心Diff算法採用了雙端比較
的算法,同時重新舊children的兩端開始進行比較,藉助key值找到可複用的節點,再進行相關操做。相比React的Diff算法,一樣狀況下能夠減小移動節點次數,減小沒必要要的性能損耗,更加的優雅。
在建立VNode時就肯定其類型,以及在mount/patch
的過程當中採用位運算
來判斷一個VNode的類型,在這個基礎之上再配合核心的Diff算法,使得性能上較Vue2.x有了提高。(實際的實現能夠結合Vue3.x源碼看。)
該算法中還運用了動態規劃
的思想求解最長遞歸子序列。
(看到這你還會發現,框架內無處不蘊藏着數據結構和算法的魅力)
❝面試官:(能夠能夠,看來是個苗子,不過自我介紹屬實有些無聊,下一題)
❞
(基操,勿6)
因爲在瀏覽器中操做DOM是很昂貴的。頻繁的操做DOM,會產生必定的性能問題。這就是虛擬Dom的產生緣由
。
Vue2的Virtual DOM借鑑了開源庫snabbdom
的實現。
Virtual DOM本質就是用一個原生的JS對象去描述一個DOM節點。是對真實DOM的一層抽象。
(也就是源碼中的VNode類,它定義在src/core/vdom/vnode.js中。)
VirtualDOM映射到真實DOM要經歷VNode的create、diff、patch等階段。
「key的做用是儘量的複用 DOM 元素。」
新舊 children 中的節點只有順序是不一樣的時候,最佳的操做應該是經過移動元素的位置來達到更新的目的。
須要在新舊 children 的節點中保存映射關係,以便可以在舊 children 的節點中找到可複用的節點。key也就是children中節點的惟一標識。
keep-alive
能夠實現組件緩存,當組件切換時不會對當前組件進行卸載。
經常使用的兩個屬性include/exclude
,容許組件有條件的進行緩存。
兩個生命週期activated/deactivated
,用來得知當前組件是否處於活躍狀態。
keep-alive的中還運用了LRU(Least Recently Used)
算法。
(又是數據結構與算法,原來算法在前端也有這麼多的應用)
組件的調用順序都是先父後子
,渲染完成的順序是先子後父
。
組件的銷燬操做是先父後子
,銷燬完成的順序是先子後父
。
父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount- >子mounted->父mounted
父beforeUpdate->子beforeUpdate->子updated->父updated
父 beforeUpdate -> 父 updated
父beforeDestroy->子beforeDestroy->子destroyed->父destroyed
父子組件通訊
父->子props
,子->父 $on、$emit
獲取父子組件實例 $parent、$children
Ref
獲取實例的方式調用組件的屬性或者方法
Provide、inject
官方不推薦使用,可是寫組件庫時很經常使用
兄弟組件通訊
Event Bus
實現跨組件通訊 Vue.prototype.$bus = new Vue
Vuex
跨級組件通訊
Vuex
$attrs、$listeners
Provide、inject
SSR也就是服務端渲染,也就是將Vue在客戶端把標籤渲染成HTML的工做放在服務端完成,而後再把html直接返回給客戶端
。
SSR有着更好的SEO、而且首屏加載速度更快等優勢。不過它也有一些缺點,好比咱們的開發條件會受到限制,服務器端渲染只支持beforeCreate
和created
兩個鉤子,當咱們須要一些外部擴展庫時須要特殊處理,服務端渲染應用程序也須要處於Node.js的運行環境。還有就是服務器會有更大的負載需求。
還可使用緩存(客戶端緩存、服務端緩存)優化、服務端開啓gzip壓縮等。
(優化是個大工程,會涉及不少方面,這裏申請另開一個專欄)
location.hash
的值實際就是URL中#
後面的東西。
history實際採用了HTML5中提供的API來實現,主要有history.pushState()
和history.replaceState()
。
面試官拿起旁邊已經涼透的咖啡,喝了一口。
(我難道問不倒這小子了麼)
「持續更新……」
1.看到這裏了就點個贊支持下吧,你的「贊」是我創做的動力。
2.關注公衆號前端食堂
,「你的前端食堂,記得按時吃飯」!
3. github.com/Geekhyt,感謝Star。