Vue最全知識點(diǎn)逛尚,面試必備(基礎(chǔ)到進(jìn)階,覆蓋vue3.0,持續(xù)更新整理,歡迎補(bǔ)充討論)
參考文章傳送:1.童歐巴對vue知識的整理 2.我是你的超級英雄對vue知識的整理 3.vue官網(wǎng)
基礎(chǔ)篇
說說你對MVVM的理解
- Model-View-ViewModel的縮寫谅畅,Model代表數(shù)據(jù)模型,View代表UI組件,ViewModel將Model和View關(guān)聯(lián)起來
- 數(shù)據(jù)會綁定到viewModel層并自動將數(shù)據(jù)渲染到頁面中噪服,視圖變化的時候會通知viewModel層更新數(shù)據(jù)
Vue2.x響應(yīng)式數(shù)據(jù)/雙向綁定原理
Vue 數(shù)據(jù)雙向綁定主要是指:數(shù)據(jù)變化更新視圖毡泻,視圖變化更新數(shù)據(jù)。其中粘优,View變化更新Data仇味,可以通過事件監(jiān)聽的方式來實(shí)現(xiàn),所以 Vue數(shù)據(jù)雙向綁定的工作主要是如何根據(jù)Data變化更新View雹顺。
-
簡述:
- 當(dāng)你把一個普通的 JavaScript 對象傳入 Vue 實(shí)例作為 data 選項丹墨,Vue 將遍歷此對象所有的 property,并使用 Object.defineProperty 把這些 property 全部轉(zhuǎn)為 getter/setter嬉愧。
- 這些 getter/setter 對用戶來說是不可見的贩挣,但是在內(nèi)部它們讓 Vue 能夠追蹤依賴,在 property 被訪問和修改時通知變更没酣。
- 每個組件實(shí)例都對應(yīng)一個 watcher 實(shí)例王财,它會在組件渲染的過程中把“接觸”過的數(shù)據(jù) property 記錄為依賴。之后當(dāng)依賴項的 setter 觸發(fā)時裕便,會通知 watcher搪搏,從而使它關(guān)聯(lián)的組件重新渲染。
[圖片上傳失敗...(image-75cc09-1601173469405)]
-
深入理解:
- 監(jiān)聽器 Observer:對數(shù)據(jù)對象進(jìn)行遍歷闪金,包括子屬性對象的屬性疯溺,利用 Object.defineProperty() 對屬性都加上 setter 和 getter。這樣的話哎垦,給這個對象的某個值賦值囱嫩,就會觸發(fā) setter,那么就能監(jiān)聽到了數(shù)據(jù)變化漏设。
- 解析器 Compile:解析 Vue 模板指令墨闲,將模板中的變量都替換成數(shù)據(jù),然后初始化渲染頁面視圖郑口,并將每個指令對應(yīng)的節(jié)點(diǎn)綁定更新函數(shù)鸳碧,添加監(jiān)聽數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動犬性,收到通知瞻离,調(diào)用更新函數(shù)進(jìn)行數(shù)據(jù)更新。
- 訂閱者 Watcher:Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁 乒裆,主要的任務(wù)是訂閱 Observer 中的屬性值變化的消息套利,當(dāng)收到屬性值變化的消息時,觸發(fā)解析器 Compile 中對應(yīng)的更新函數(shù)。每個組件實(shí)例都有相應(yīng)的 watcher 實(shí)例對象肉迫,它會在組件渲染的過程中把屬性記錄為依賴验辞,之后當(dāng)依賴項的 setter 被調(diào)用時,會通知 watcher 重新計算喊衫,從而致使它關(guān)聯(lián)的組件得以更新——這是一個典型的觀察者模式
- 訂閱器 Dep:訂閱器采用 發(fā)布-訂閱 設(shè)計模式跌造,用來收集訂閱者 Watcher,對監(jiān)聽器 Observer 和 訂閱者 Watcher 進(jìn)行統(tǒng)一管理族购。
你知道Vue3.x響應(yīng)式數(shù)據(jù)原理嗎壳贪?
Vue3.x改用Proxy替代Object.defineProperty。
因為Proxy可以直接監(jiān)聽對象和數(shù)組的變化联四,并且有多達(dá)13種攔截方法撑碴。并且作為新標(biāo)準(zhǔn)將受到瀏覽器廠商重點(diǎn)持續(xù)的性能優(yōu)化撑教。
-
Proxy只會代理對象的第一層朝墩,Vue3是怎樣處理這個問題的呢?
- 判斷當(dāng)前Reflect.get的返回值是否為Object伟姐,如果是則再通過reactive方法做代理收苏, 這樣就實(shí)現(xiàn)了深度觀測。
- 監(jiān)測數(shù)組的時候可能觸發(fā)多次get/set愤兵,那么如何防止觸發(fā)多次呢鹿霸?我們可以判斷key是否為當(dāng)前被代理對象target自身屬性,也可以判斷舊值與新值是否相等秆乳,只有滿足以上兩個條件之一時懦鼠,才有可能執(zhí)行trigger。
Proxy 與 Object.defineProperty 優(yōu)劣對比
-
Proxy 的優(yōu)勢如下:
- Proxy 可以直接監(jiān)聽對象而非屬性屹堰;
-
Proxy 可以直接監(jiān)聽數(shù)組的變化肛冶;
- Proxy 有多達(dá) 13 種攔截方法,不限于 apply、ownKeys扯键、deleteProperty睦袖、has 等等是 Object.defineProperty 不具備的;
- Proxy 返回的是一個新對象,我們可以只操作新的對象達(dá)到目的,而 Object.defineProperty 只能遍歷對象屬性直接修改荣刑;
- Proxy 作為新標(biāo)準(zhǔn)將受到瀏覽器廠商重點(diǎn)持續(xù)的性能優(yōu)化馅笙,也就是傳說中的新標(biāo)準(zhǔn)的性能紅利;
-
Object.defineProperty 的優(yōu)勢如下:
- 兼容性好厉亏,支持 IE9董习,而 Proxy 的存在瀏覽器兼容性問題,而且無法用 polyfill 磨平,因此 Vue 的作者才聲明需要等到下個大版本( 3.0 )才能用 Proxy 重寫爱只。
VUEX篇
Vuex 是什么阱飘?
運(yùn)用到了js設(shè)計模式中的單例模式,單例模式想要做到的是,不管我們嘗試去創(chuàng)建多少次沥匈,它都只給你返回第一次所創(chuàng)建的那唯一的一個實(shí)例蔗喂。
-
Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。每一個 Vuex 應(yīng)用的核心就是 store(倉庫)高帖$侄“store” 基本上就是一個容器,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )散址。
- Vuex 的狀態(tài)存儲是響應(yīng)式的乖阵。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時候,若 store 中的狀態(tài)發(fā)生變化预麸,那么相應(yīng)的組件也會相應(yīng)地得到高效更新瞪浸。
- 改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化吏祸。
Vuex 使用單一狀態(tài)樹对蒲,用一個對象就包含了全部的應(yīng)用層級狀態(tài)。至此它便作為一個“唯一數(shù)據(jù)源 (SSOT)”而存在贡翘。這也意味著蹈矮,每個應(yīng)用將僅僅包含一個 store 實(shí)例。單一狀態(tài)樹讓我們能夠直接地定位任一特定的狀態(tài)片段鸣驱,在調(diào)試的過程中也能輕易地取得整個當(dāng)前應(yīng)用狀態(tài)的快照泛鸟。 ——Vuex官方文檔
-
主要包括以下幾個模塊:
- State:定義了應(yīng)用狀態(tài)的數(shù)據(jù)結(jié)構(gòu),可以在這里設(shè)置默認(rèn)的初始狀態(tài)踊东。
- Getter:允許組件從 Store 中獲取數(shù)據(jù)北滥,mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計算屬性。
- Mutation:是唯一更改 store 中狀態(tài)的方法闸翅,且必須是同步函數(shù)再芋。
- Action:用于提交 mutation,而不是直接變更狀態(tài)缎脾,可以包含任意異步操作祝闻。
- Module:允許將單一的 Store 拆分為多個 store 且同時保存在單一的狀態(tài)樹中。
什么情況下使用 Vuex遗菠?
- 如果應(yīng)用夠簡單联喘,最好不要使用 Vuex,一個簡單的 store 模式即可
- 需要構(gòu)建一個中大型單頁應(yīng)用時辙纬,使用Vuex能更好地在組件外部管理狀態(tài)
Vuex和單純的全局對象有什么區(qū)別豁遭?
- Vuex 的狀態(tài)存儲是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時候贺拣,若 store 中的狀態(tài)發(fā)生變化蓖谢,那么相應(yīng)的組件也會相應(yīng)地得到高效更新捂蕴。
- 不能直接改變 store 中的狀態(tài)。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation闪幽。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化啥辨,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用。
為什么 Vuex 的 mutation 中不能做異步操作盯腌?
- Vuex中所有的狀態(tài)更新的唯一途徑都是mutation溉知,異步操作通過 Action 來提交 mutation實(shí)現(xiàn),這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化腕够,從而讓我們能夠?qū)崿F(xiàn)一些工具幫助我們更好地了解我們的應(yīng)用级乍。
- 每個mutation執(zhí)行完成后都會對應(yīng)到一個新的狀態(tài)變更,這樣devtools就可以打個快照存下來帚湘,然后就可以實(shí)現(xiàn) time-travel 了玫荣。如果mutation支持異步操作,就沒有辦法知道狀態(tài)是何時更新的大诸,無法很好的進(jìn)行狀態(tài)的追蹤捅厂,給調(diào)試帶來困難。
新增:vuex的action有返回值嗎底挫?返回的是什么恒傻?
- store.dispatch 可以處理被觸發(fā)的 action 的處理函數(shù)返回的 Promise脸侥,并且 store.dispatch 仍舊返回 Promise
- Action 通常是異步的建邓,要知道 action 什么時候結(jié)束或者組合多個 action以處理更加復(fù)雜的異步流程,可以通過定義action時返回一個promise對象睁枕,就可以在派發(fā)action的時候就可以通過處理返回的 Promise處理異步流程
一個 store.dispatch 在不同模塊中可以觸發(fā)多個 action 函數(shù)官边。在這種情況下,只有當(dāng)所有觸發(fā)函數(shù)完成后外遇,返回的 Promise 才會執(zhí)行注簿。
新增:為什么不直接分發(fā)mutation,而要通過分發(fā)action之后提交 mutation變更狀態(tài)
- mutation 必須同步執(zhí)行,我們可以在 action 內(nèi)部執(zhí)行異步操作
- 可以進(jìn)行一系列的異步操作跳仿,并且通過提交 mutation 來記錄 action 產(chǎn)生的副作用(即狀態(tài)變更)
常規(guī)篇
computed 和 watch 的區(qū)別和運(yùn)用的場景诡渴?
computed:是計算屬性,依賴其它屬性值菲语,并且 computed 的值有緩存妄辩,只有它依賴的屬性值發(fā)生改變,下一次獲取 computed 的值時才會重新計算 computed 的值山上;
watch:沒有緩存性眼耀,更多的是「觀察」的作用,類似于某些數(shù)據(jù)的監(jiān)聽回調(diào) 佩憾,每當(dāng)監(jiān)聽的數(shù)據(jù)變化時都會執(zhí)行回調(diào)進(jìn)行后續(xù)操作哮伟;當(dāng)我們需要深度監(jiān)聽對象中的屬性時干花,可以打開deep:true選項,這樣便會對對象中的每一項進(jìn)行監(jiān)聽
-
運(yùn)用場景:
- 當(dāng)我們需要進(jìn)行數(shù)值計算楞黄,并且依賴于其它數(shù)據(jù)時池凄,應(yīng)該使用 computed,因為可以利用 computed 的緩存特性鬼廓,避免每次獲取值時修赞,都要重新計算;
- 當(dāng)我們需要在數(shù)據(jù)變化時執(zhí)行異步或開銷較大的操作時桑阶,應(yīng)該使用 watch柏副,使用watch選項允許我們執(zhí)行異步操作 ( 訪問一個 API ),限制我們執(zhí)行該操作的頻率蚣录,并在我們得到最終結(jié)果前割择,設(shè)置中間狀態(tài)。這些都是計算屬性無法做到的萎河。
Vue2.x組件通信有哪些方式荔泳?
-
父子組件通信
- 事件機(jī)制(**父->子props,子->父
$on、$emit
) - 獲取父子組件實(shí)例
$parent虐杯、$children
- Ref 獲取實(shí)例的方式調(diào)用組件的屬性或者方法
- Provide玛歌、inject (不推薦使用,組件庫時很常用)
- 事件機(jī)制(**父->子props,子->父
-
兄弟組件通信
- eventBus 這種方法通過一個空的 Vue實(shí)例作為中央事件總線(事件中心)擎椰,用它來觸發(fā)事件和監(jiān)聽事件支子,從而實(shí)現(xiàn)任何組件間的通信,包括父子达舒、隔代值朋、兄弟組件
Vue.prototype.
$bus
= new Vue- Vuex
-
跨級組件通信
- Vuex
$attrs、$listeners
- Provide巩搏、inject
說一下v-if和v-show的區(qū)別
- 當(dāng)條件不成立時昨登,v-if不會渲染DOM元素,v-show操作的是樣式(display)贯底,切換當(dāng)前DOM的顯示和隱藏丰辣。
- v-if 適用于在運(yùn)行時很少改變條件,不需要頻繁切換條件的場景禽捆;
- v-show 則適用于需要非常頻繁切換條件的場景笙什。
為什么 v-for 和 v-if 不建議用在一起
- 當(dāng) v-for 和 v-if 處于同一個節(jié)點(diǎn)時,v-for 的優(yōu)先級比 v-if 更高睦擂,這意味著 v-if 將分別重復(fù)運(yùn)行于每個 v-for 循環(huán)中得湘。如果要遍歷的數(shù)組很大,而真正要展示的數(shù)據(jù)很少時顿仇,這將造成很大的性能浪費(fèi)
- 這種場景建議使用 computed淘正,先對數(shù)據(jù)進(jìn)行過濾
組件中的data為什么是一個函數(shù)摆马?
- 一個組件被復(fù)用多次的話,也就會創(chuàng)建多個實(shí)例鸿吆。本質(zhì)上囤采,這些實(shí)例用的都是同一個構(gòu)造函數(shù)。
- 如果data是對象的話惩淳,對象屬于引用類型蕉毯,會影響到所有的實(shí)例。所以為了保證組件不同的實(shí)例之間data不沖突思犁,data必須是一個函數(shù)代虾。
子組件為什么不可以修改父組件傳遞的Prop?/怎么理解vue的單向數(shù)據(jù)流激蹲?
- Vue提倡單向數(shù)據(jù)流,即父級props的更新會流向子組件,但是反過來則不行棉磨。
- 這是為了防止意外的改變父組件狀態(tài),使得應(yīng)用的數(shù)據(jù)流變得難以理解学辱。
- 如果破壞了單向數(shù)據(jù)流乘瓤,當(dāng)應(yīng)用復(fù)雜時,debug 的成本會非常高策泣。
v-model是如何實(shí)現(xiàn)雙向綁定的衙傀?
- v-model是用來在表單控件或者組件上創(chuàng)建雙向綁定的
- 他的本質(zhì)是v-bind和v-on的語法糖
- 在一個組件上使用v-model,默認(rèn)會為組件綁定名為value的prop和名為input的事件
nextTick的實(shí)現(xiàn)原理是什么萨咕?
- 在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行延遲回調(diào)统抬,在修改數(shù)據(jù)之后立即使用 nextTick 來獲取更新后的 DOM。
- nextTick主要使用了宏任務(wù)和微任務(wù)任洞。
- 根據(jù)執(zhí)行環(huán)境分別嘗試采用Promise蓄喇、MutationObserver发侵、setImmediate交掏,如果以上都不行則采用setTimeout定義了一個異步方法,多次調(diào)用nextTick會將方法存入隊列中刃鳄,通過這個異步方法清空當(dāng)前隊列盅弛。
Vue不能檢測數(shù)組的哪些變動?Vue 怎么用 vm.$set()
解決對象新增屬性不能響應(yīng)的問題 叔锐?
-
Vue 不能檢測以下數(shù)組的變動:
當(dāng)你利用索引直接設(shè)置一個數(shù)組項時挪鹏,例如:vm.items[indexOfItem] = newValue
當(dāng)你修改數(shù)組的長度時,例如:vm.items.length = newLength
-
解決辦法:
- 第一類問題
// 法一:Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// 法二:Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
- 第二類問題愉烙,可使用 splice:
vm.items.splice(newLength)
-
vm.
$set
的實(shí)現(xiàn)原理是:- 如果目標(biāo)是數(shù)組讨盒,直接使用數(shù)組的 splice 方法觸發(fā)相應(yīng)式;
- 如果目標(biāo)是對象步责,會先判讀屬性是否存在返顺、對象是否是響應(yīng)式禀苦,最終如果要對屬性進(jìn)行響應(yīng)式處理,則是通過調(diào)用 defineReactive 方法進(jìn)行響應(yīng)式處理( defineReactive 方法就是 Vue 在初始化對象時遂鹊,給對象屬性采用 Object.defineProperty 動態(tài)添加 getter 和 setter 的功能所調(diào)用的方法)
Vue事件綁定原理是什么振乏?
- 原生事件綁定是通過addEventListener綁定給真實(shí)元素的,組件事件綁定是通過Vue自定義的
$on
實(shí)現(xiàn)的秉扑。
說一下虛擬Dom以及key屬性的作用
由于在瀏覽器中操作DOM是很昂貴的慧邮。頻繁的操作DOM,會產(chǎn)生一定的性能問題舟陆。這就是虛擬Dom的產(chǎn)生原因误澳。
Virtual DOM本質(zhì)就是用一個原生的JS對象去描述一個DOM節(jié)點(diǎn)。是對真實(shí)DOM的一層抽象秦躯。(也就是源碼中的VNode類脓匿,它定義在src/core/vdom/vnode.js中。)
-
虛擬 DOM 的實(shí)現(xiàn)原理主要包括以下 3 部分:
- 用 JavaScript 對象模擬真實(shí) DOM 樹宦赠,對真實(shí) DOM 進(jìn)行抽象陪毡;
- diff 算法 — 比較兩棵虛擬 DOM 樹的差異;
- pach 算法 — 將兩個虛擬 DOM 對象的差異應(yīng)用到真正的 DOM 樹勾扭。
-
key 是為 Vue 中 vnode 的唯一標(biāo)記毡琉,通過這個 key,我們的 diff 操作可以更準(zhǔn)確妙色、更快速
- 更準(zhǔn)確:因為帶 key 就不是就地復(fù)用了桅滋,在 sameNode 函數(shù)a.key === b.key對比中可以避免就地復(fù)用的情況。所以會更加準(zhǔn)確身辨。
- 更快速:利用 key 的唯一性生成 map 對象來獲取對應(yīng)節(jié)點(diǎn)丐谋,比遍歷方式更快
為什么不建議用index作為key?
- 不建議 用index 作為 key,和沒寫基本上沒區(qū)別煌珊,因為不管你數(shù)組的順序怎么顛倒号俐,index 都是 0, 1, 2 這樣排列,導(dǎo)致 Vue 會復(fù)用錯誤的舊子節(jié)點(diǎn)定庵,做很多額外的工作
生命周期篇
說一下你對Vue的生命周期的理解
-
簡單回答
- beforeCreate吏饿、created、beforeMount蔬浙、mounted猪落、beforeUpdate、updated畴博、beforeDestroy笨忌、destroyed。
- keep-alive 有自己獨(dú)立的鉤子函數(shù) activated 和 deactivated俱病。
復(fù)雜回答
|
生命周期
| 發(fā)生了什么 |
| --- | --- |
| beforeCreate | 在當(dāng)前階段data官疲、methods杂曲、computed以及watch上的數(shù)據(jù)和方法都不能被訪問 |
| created | 在實(shí)例創(chuàng)建完成后發(fā)生,當(dāng)前階段已經(jīng)完成了數(shù)據(jù)觀測袁余,也就是可以使用數(shù)據(jù)擎勘,更改數(shù)據(jù),在這里更改數(shù)據(jù)不會觸發(fā)updated函數(shù)颖榜∨锒可以做一些初始數(shù)據(jù)的獲取,在當(dāng)前階段無法與Dom進(jìn)行交互掩完,如果非要想噪漾,可以通過vm.$nextTick
來訪問Dom |
| beforeMount | 發(fā)生在掛載之前,在這之前template模板已導(dǎo)入渲染函數(shù)編譯且蓬。而當(dāng)前階段虛擬Dom已經(jīng)創(chuàng)建完成欣硼,即將開始渲染。在此時也可以對數(shù)據(jù)進(jìn)行更改恶阴,不會觸發(fā)updated |
| mounted | 在掛載完成后發(fā)生诈胜,在當(dāng)前階段,真實(shí)的Dom掛載完畢冯事,數(shù)據(jù)完成雙向綁定焦匈,可以訪問到Dom節(jié)點(diǎn),使用$refs
屬性對Dom進(jìn)行操作 |
| beforeUpdate | 發(fā)生在更新之前昵仅,也就是響應(yīng)式數(shù)據(jù)發(fā)生更新缓熟,虛擬dom重新渲染之前被觸發(fā),你可以在當(dāng)前階段進(jìn)行更改數(shù)據(jù)摔笤,不會造成重渲染 |
| updated | 發(fā)生在更新完成之后够滑,當(dāng)前階段組件Dom已完成更新。要注意的是避免在此期間更改數(shù)據(jù)吕世,因為這可能會導(dǎo)致無限循環(huán)的更新 |
| beforeDestroy | 發(fā)生在實(shí)例銷毀之前彰触,在當(dāng)前階段實(shí)例完全可以被使用,我們可以在這時進(jìn)行善后收尾工作寞冯,比如清除計時器 |
| destroyed | 發(fā)生在實(shí)例銷毀之后渴析,這個時候只剩下了dom空殼。組件已被拆解吮龄,數(shù)據(jù)綁定被卸除,監(jiān)聽被移出咆疗,子實(shí)例也統(tǒng)統(tǒng)被銷毀 |
| activited keep-alive 專屬 | 組件被激活時調(diào)用 |
| deactivated keep-alive 專屬 | 組件被銷毀時調(diào)用 |
Vue中組件生命周期調(diào)用順序是什么樣的漓帚?
- 組件的調(diào)用順序都是先父后子,渲染完成的順序是先子后父。
- 組件的銷毀操作是先父后子午磁,銷毀完成的順序是先子后父尝抖。
在什么階段才能訪問操作DOM毡们?
在鉤子函數(shù) mounted 被調(diào)用前,Vue 已經(jīng)將編譯好的模板掛載到頁面上昧辽,所以在 mounted 中可以訪問操作 DOM衙熔。
你的接口請求一般放在哪個生命周期中?
可以在鉤子函數(shù) created搅荞、beforeMount红氯、mounted 中進(jìn)行調(diào)用,因為在這三個鉤子函數(shù)中咕痛,data 已經(jīng)創(chuàng)建痢甘,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。
-
但是推薦在 created 鉤子函數(shù)中調(diào)用異步請求茉贡,因為在 created 鉤子函數(shù)中調(diào)用異步請求有以下優(yōu)點(diǎn):
- 能更快獲取到服務(wù)端數(shù)據(jù)塞栅,減少頁面loading 時間;
- ssr不支持 beforeMount 腔丧、mounted 鉤子函數(shù)放椰,所以放在 created 中有助于一致性;
路由篇
vue路由hash模式和history模式實(shí)現(xiàn)原理分別是什么愉粤,他們的區(qū)別是什么庄敛?
-
hash 模式:
-
后面 hash 值的變化,不會導(dǎo)致瀏覽器向服務(wù)器發(fā)出請求科汗,瀏覽器不發(fā)出請求藻烤,就不會刷新頁面
- 通過監(jiān)聽 hashchange 事件可以知道 hash 發(fā)生了哪些變化,然后根據(jù) hash 變化來實(shí)現(xiàn)更新頁面部分內(nèi)容的操作头滔。
-
-
history 模式:
- history 模式的實(shí)現(xiàn)怖亭,主要是 HTML5 標(biāo)準(zhǔn)發(fā)布的兩個 API,pushState 和 replaceState坤检,這兩個 API 可以在改變 url兴猩,但是不會發(fā)送請求。這樣就可以監(jiān)聽 url 變化來實(shí)現(xiàn)更新頁面部分內(nèi)容的操作
-
區(qū)別
- url 展示上早歇,hash 模式有“#”倾芝,history 模式?jīng)]有
- 刷新頁面時,hash 模式可以正常加載到 hash 值對應(yīng)的頁面箭跳,而 history 沒有處理的話晨另,會返回 404,一般需要后端將所有頁面都配置重定向到首頁路由
- 兼容性谱姓,hash 可以支持低版本瀏覽器和 IE借尿。
路由懶加載是什么意思?如何實(shí)現(xiàn)路由懶加載?
路由懶加載的含義:把不同路由對應(yīng)的組件分割成不同的代碼塊路翻,然后當(dāng)路由被訪問的時候才加載對應(yīng)組件
-
實(shí)現(xiàn):結(jié)合 Vue 的異步組件和 Webpack 的代碼分割功能
- 可以將異步組件定義為返回一個 Promise 的工廠函數(shù) (該函數(shù)返回的 Promise 應(yīng)該 resolve 組件本身)
const Foo = () => Promise.resolve({ /* 組件定義對象 */ })
- 在 Webpack 2 中狈癞,我們可以使用動態(tài) import語法來定義代碼分塊點(diǎn) (split point)
import('./Foo.vue') // 返回 Promise
* 結(jié)合這兩者,這就是如何定義一個能夠被 Webpack 自動代碼分割的異步組件
const Foo = () => import('./Foo.vue')
const router = new VueRouter({ routes: [ { path: '/foo', component: Foo } ]})
- 使用命名 chunk茂契,和webpack中的魔法注釋就可以把某個路由下的所有組件都打包在同個異步塊 (chunk) 中
chunkconst Foo = () => import(/* webpackChunkName: "group-foo" */ './Foo.vue')
Vue-router 導(dǎo)航守衛(wèi)有哪些
- 全局前置/鉤子:beforeEach蝶桶、beforeResolve、afterEach
- 路由獨(dú)享的守衛(wèi):beforeEnter
- 組件內(nèi)的守衛(wèi):beforeRouteEnter掉冶、beforeRouteUpdate真竖、beforeRouteLeave
進(jìn)階篇
說說vue和react的異同
-
同
- 使用 Virtual DOM
- 提供了響應(yīng)式 (Reactive) 和組件化 (Composable) 的視圖組件。
- 將注意力集中保持在核心庫郭蕉,而將其他功能如路由和全局狀態(tài)管理交給相關(guān)的庫疼邀。
-
異
- 在 React 應(yīng)用中,當(dāng)某個組件的狀態(tài)發(fā)生變化時召锈,它會以該組件為根旁振,重新渲染整個組件子樹(除非使用PureComponent/shouldComponentUpdate),在 Vue 應(yīng)用中涨岁,組件的依賴是在渲染過程中自動追蹤的拐袜,所以系統(tǒng)能精確知曉哪個組件確實(shí)需要被重渲染
- 在 React 中,一切都是 JavaScript梢薪。不僅僅是 HTML 可以用 JSX 來表達(dá)蹬铺,現(xiàn)在的潮流也越來越多地將 CSS 也納入到 JavaScript 中來處理
- Vue 的路由庫和狀態(tài)管理庫都是由官方維護(hù)支持且與核心庫同步更新的。React 則是選擇把這些問題交給社區(qū)維護(hù)秉撇,因此創(chuàng)建了一個更分散的生態(tài)系統(tǒng)甜攀,所以有更豐富的生態(tài)系統(tǒng)
- Vue 提供了CLI 腳手架,能讓你通過交互式的腳手架引導(dǎo)非常容易地構(gòu)建項目琐馆。你甚至可以使用它快速開發(fā)組件的原型规阀。React 在這方面也提供了create-react-app,但是現(xiàn)在還存在一些局限性
- React Native 能使你用相同的組件模型編寫有本地渲染能力的 APP瘦麸,Vue 和Weex會進(jìn)行官方合作谁撼,Weex 是阿里巴巴發(fā)起的跨平臺用戶界面開發(fā)框架,同時也正在 Apache 基金會進(jìn)行項目孵化滋饲,另一個選擇是NativeScript-Vue厉碟,一個用 Vue.js 構(gòu)建完全原生應(yīng)用的NativeScript插件
什么是 mixin ?
- Mixin 使我們能夠為 Vue 組件編寫可插拔和可重用的功能屠缭。
- 如果你希望再多個組件之間重用一組組件選項箍鼓,例如生命周期 hook、 方法等勿她,則可以將其編寫為 mixin袄秩,并在組件中簡單的引用它。
- 然后將 mixin 的內(nèi)容合并到組件中逢并。如果你要在 mixin 中定義生命周期 hook之剧,那么它在執(zhí)行時將優(yōu)化于組件自已的 hook。
在 Vue 實(shí)例中編寫生命周期 hook 或其他 option/properties 時砍聊,為什么不使用箭頭函數(shù) 背稼?
- 箭頭函數(shù)自已沒有定義 this 上下文中。
- 當(dāng)你在 Vue 程序中使用箭頭函數(shù) ( => ) 時玻蝌,this 關(guān)鍵字病不會綁定到 Vue 實(shí)例蟹肘,因此會引發(fā)錯誤。所以強(qiáng)烈建議改用標(biāo)準(zhǔn)函數(shù)聲明俯树。
Vue模版編譯原理知道嗎帘腹,能簡單說一下嗎?
簡單說许饿,Vue的編譯過程就是將template轉(zhuǎn)化為render函數(shù)的過程阳欲。會經(jīng)歷以下階段(生成AST樹/優(yōu)化/codegen):
- 首先解析模版,生成AST語法樹(一種用JavaScript對象的形式來描述整個模板)陋率。 使用大量的正則表達(dá)式對模板進(jìn)行解析球化,遇到標(biāo)簽、文本的時候都會執(zhí)行對應(yīng)的鉤子進(jìn)行相關(guān)處理瓦糟。
- Vue的數(shù)據(jù)是響應(yīng)式的筒愚,但其實(shí)模板中并不是所有的數(shù)據(jù)都是響應(yīng)式的。有一些數(shù)據(jù)首次渲染后就不會再變化菩浙,對應(yīng)的DOM也不會變化巢掺。那么優(yōu)化過程就是深度遍歷AST樹,按照相關(guān)條件對樹節(jié)點(diǎn)進(jìn)行標(biāo)記劲蜻。這些被標(biāo)記的節(jié)點(diǎn)(靜態(tài)節(jié)點(diǎn))我們就可以跳過對它們的比對陆淀,對運(yùn)行時的模板起到很大的優(yōu)化作用。
- 編譯的最后一步是將優(yōu)化后的AST樹轉(zhuǎn)換為可執(zhí)行的代碼斋竞。
diff算法說一下
- 同級比較莉钙,再比較子節(jié)點(diǎn)
- 先判斷一方有子節(jié)點(diǎn)一方?jīng)]有子節(jié)點(diǎn)的情況(如果新的children沒有子節(jié)點(diǎn),將舊的子節(jié)點(diǎn)移除)
- 比較都有子節(jié)點(diǎn)的情況(核心diff)
- 遞歸比較子節(jié)點(diǎn)
說說你對keep-alive組件的了解
-
keep-alive 是 Vue 內(nèi)置的一個組件免绿,可以使被包含的組件保留狀態(tài)可帽,避免重新渲染 ,其有以下特性:
- 一般結(jié)合路由和動態(tài)組件一起使用鳄袍,用于緩存組件绢要;
- 提供 include 和 exclude 屬性,兩者都支持字符串或正則表達(dá)式拗小, include 表示只有名稱匹配的組件會被緩存重罪,exclude 表示任何名稱匹配的組件都不會被緩存 ,其中 exclude 的優(yōu)先級比 include 高;
- 對應(yīng)兩個鉤子函數(shù) activated 和 deactivated 剿配,當(dāng)組件被激活時搅幅,觸發(fā)鉤子函數(shù) activated,當(dāng)組件被移除時呼胚,觸發(fā)鉤子函數(shù) deactivated茄唐。
說說你對SSR的了解
SSR也就是服務(wù)端渲染,也就是將Vue在客戶端把標(biāo)簽渲染成HTML的工作放在服務(wù)端完成蝇更,然后再把html直接返回給客戶端
-
SSR的優(yōu)勢
- 更好的SEO
- 首屏加載速度更快
-
SSR的缺點(diǎn)
- 開發(fā)條件會受到限制沪编,服務(wù)器端渲染只支持beforeCreate和created兩個鉤子
- 當(dāng)我們需要一些外部擴(kuò)展庫時需要特殊處理,服務(wù)端渲染應(yīng)用程序也需要處于Node.js的運(yùn)行環(huán)境
- 更多的服務(wù)端負(fù)載
你都做過哪些Vue的性能優(yōu)化年扩?
-
編碼階段
- 盡量減少data中的數(shù)據(jù)蚁廓,data中的數(shù)據(jù)都會增加getter和setter,會收集對應(yīng)的watcher
- v-if和v-for不能連用
- 如果需要使用v-for給每項元素綁定事件時使用事件代理
- SPA 頁面采用keep-alive緩存組件
- 在更多的情況下厨幻,使用v-if替代v-show
- key保證唯一
- 使用路由懶加載相嵌、異步組件
- 防抖、節(jié)流
- 第三方模塊按需導(dǎo)入
- 長列表滾動到可視區(qū)域動態(tài)加載
- 圖片懶加載
-
SEO優(yōu)化
- 預(yù)渲染
- 服務(wù)端渲染SSR
-
打包優(yōu)化
- 壓縮代碼
- Tree Shaking/Scope Hoisting
- 使用cdn加載第三方模塊
- 多線程打包happypack
- splitChunks抽離公共文件
- sourceMap優(yōu)化
-
用戶體驗
- 骨架屏
- PWA
- 還可以使用緩存(客戶端緩存克胳、服務(wù)端緩存)優(yōu)化平绩、服務(wù)端開啟gzip壓縮等。
vue2.x中如何監(jiān)測數(shù)組變化漠另?
- 使用了函數(shù)劫持的方式捏雌,重寫了數(shù)組的方法,Vue將data中的數(shù)組進(jìn)行了原型鏈重寫笆搓,指向了自己定義的數(shù)組原型方法性湿,當(dāng)調(diào)用數(shù)組api時,可以通知依賴更新满败。
- 如果數(shù)組中包含著引用類型肤频,會對數(shù)組中的引用類型再次遞歸遍歷進(jìn)行監(jiān)控。這樣就實(shí)現(xiàn)了監(jiān)測數(shù)組變化算墨。
說說你對 SPA 單頁面的理解宵荒,它的優(yōu)缺點(diǎn)分別是什么?
SPA( single-page application )僅在 Web 頁面初始化時加載相應(yīng)的 HTML净嘀、JavaScript 和 CSS报咳。一旦頁面加載完成,SPA 不會因為用戶的操作而進(jìn)行頁面的重新加載或跳轉(zhuǎn)挖藏;取而代之的是利用路由機(jī)制實(shí)現(xiàn) HTML 內(nèi)容的變換暑刃,UI 與用戶的交互,避免頁面的重新加載膜眠。
-
優(yōu)點(diǎn):
- 用戶體驗好岩臣、快溜嗜,內(nèi)容的改變不需要重新加載整個頁面,避免了不必要的跳轉(zhuǎn)和重復(fù)渲染架谎;
- 基于上面一點(diǎn)炸宵,SPA 相對對服務(wù)器壓力小狐树;
- 前后端職責(zé)分離焙压,架構(gòu)清晰鸿脓,前端進(jìn)行交互邏輯抑钟,后端負(fù)責(zé)數(shù)據(jù)處理;
-
缺點(diǎn):
- 初次加載耗時多:為實(shí)現(xiàn)單頁 Web 應(yīng)用功能及顯示效果野哭,需要在加載頁面的時候?qū)?JavaScript在塔、CSS 統(tǒng)一加載,部分頁面按需加載拨黔;
- 前進(jìn)后退路由管理:由于單頁應(yīng)用在一個頁面中顯示所有的內(nèi)容蛔溃,所以不能使用瀏覽器的前進(jìn)后退功能,所有的頁面切換需要自己建立堆棧管理篱蝇;
- SEO 難度較大:由于所有的內(nèi)容都在一個頁面中動態(tài)替換顯示贺待,所以在 SEO 上其有著天然的弱勢。
對于即將到來的 vue3.0 特性你有什么了解的嗎零截?
-
監(jiān)測機(jī)制的改變
- 3.0 將帶來基于代理 Proxy的 observer 實(shí)現(xiàn)麸塞,提供全語言覆蓋的反應(yīng)性跟蹤。
- 消除了 Vue 2 當(dāng)中基于 Object.defineProperty 的實(shí)現(xiàn)所存在的很多限制:
-
只能監(jiān)測屬性涧衙,不能監(jiān)測對象
- 檢測屬性的添加和刪除哪工;
- 檢測數(shù)組索引和長度的變更;
- 支持 Map弧哎、Set雁比、WeakMap 和 WeakSet。
-
模板
- 模板方面沒有大的變更撤嫩,只改了作用域插槽偎捎,2.x 的機(jī)制導(dǎo)致作用域插槽變了,父組件會重新渲染序攘,而 3.0 把作用域插槽改成了函數(shù)的方式茴她,這樣只會影響子組件的重新渲染,提升了渲染的性能两踏。
- 同時败京,對于 render 函數(shù)的方面,vue3.0 也會進(jìn)行一系列更改來方便習(xí)慣直接使用 api 來生成 vdom 梦染。
-
對象式的組件聲明方式
- vue2.x 中的組件是通過聲明的方式傳入一系列 option赡麦,和 TypeScript 的結(jié)合需要通過一些裝飾器的方式來做朴皆,雖然能實(shí)現(xiàn)功能,但是比較麻煩泛粹。
- 3.0 修改了組件的聲明方式遂铡,改成了類式的寫法,這樣使得和 TypeScript 的結(jié)合變得很容易
-
其它方面的更改
- 支持自定義渲染器晶姊,從而使得 weex 可以通過自定義渲染器的方式來擴(kuò)展扒接,而不是直接 fork 源碼來改的方式。
- 支持 Fragment(多個根節(jié)點(diǎn))和 Protal(在 dom 其他部分渲染組建內(nèi)容)組件们衙,針對一些特殊的場景做了處理钾怔。
- 基于 tree shaking 優(yōu)化,提供了更多的內(nèi)置功能蒙挑。