1 MVC 和 MVVM 區(qū)別
MVC
MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫赠潦,一種軟件設(shè)計典范
Model(模型):是應(yīng)用程序中用于處理應(yīng)用程序數(shù)據(jù)邏輯的部分麸俘。通常模型對象負(fù)責(zé)在數(shù)據(jù)庫中存取數(shù)據(jù)
View(視圖):是應(yīng)用程序中處理數(shù)據(jù)顯示的部分。通常視圖是依據(jù)模型數(shù)據(jù)創(chuàng)建的
Controller(控制器):是應(yīng)用程序中處理用戶交互的部分消返。通诚呓牛控制器負(fù)責(zé)從視圖讀取數(shù)據(jù)刻像,控制用戶輸入,并向模型發(fā)送數(shù)據(jù)
mvc.png
MVC 的思想:一句話描述就是 Controller 負(fù)責(zé)將 Model 的數(shù)據(jù)用 View 顯示出來同仆,換句話說就是在 Controller 里面把 Model 的數(shù)據(jù)賦值給 View。
MVVM
MVVM 新增了 VM 類
ViewModel 層:做了兩件事達(dá)到了數(shù)據(jù)的雙向綁定 一是將【模型】轉(zhuǎn)化成【視圖】裙品,即將后端傳遞的數(shù)據(jù)轉(zhuǎn)化成所看到的頁面俗批。實(shí)現(xiàn)的方式是:數(shù)據(jù)綁定。二是將【視圖】轉(zhuǎn)化成【模型】市怎,即將所看到的頁面轉(zhuǎn)化成后端的數(shù)據(jù)岁忘。實(shí)現(xiàn)的方式是:DOM 事件監(jiān)聽。
mvvm.png
MVVM 與 MVC 最大的區(qū)別就是:它實(shí)現(xiàn)了 View 和 Model 的自動同步区匠,也就是當(dāng) Model 的屬性改變時干像,我們不用再自己手動操作 Dom 元素,來改變 View 的顯示辱志,而是改變屬性后該屬性對應(yīng) View 層顯示會自動改變(對應(yīng)Vue數(shù)據(jù)驅(qū)動的思想)
整體看來蝠筑,MVVM 比 MVC 精簡很多,不僅簡化了業(yè)務(wù)與界面的依賴揩懒,還解決了數(shù)據(jù)頻繁更新的問題什乙,不用再用選擇器操作 DOM 元素。因?yàn)樵?MVVM 中已球,View 不知道 Model 的存在臣镣,Model 和 ViewModel 也觀察不到 View,這種低耦合模式提高代碼的可重用性
?
注意:Vue 并沒有完全遵循 MVVM 的思想 這一點(diǎn)官網(wǎng)自己也有說明
?
vue-mvvm.png
那么問題來了 為什么官方要說 Vue 沒有完全遵循 MVVM 思想呢智亮?
?
嚴(yán)格的 MVVM 要求 View 不能和 Model 直接通信忆某,而 Vue 提供了 $refs 這個屬性,讓 Model 可以直接操作 View阔蛉,違反了這一規(guī)定弃舒,所以說 Vue 沒有完全遵循 MVVM。
?
2 為什么 data 是一個函數(shù)
組件中的 data 寫成一個函數(shù)状原,數(shù)據(jù)以函數(shù)返回值形式定義聋呢,這樣每復(fù)用一次組件,就會返回一份新的 data颠区,類似于給每個組件實(shí)例創(chuàng)建一個私有的數(shù)據(jù)空間削锰,讓各個組件實(shí)例維護(hù)各自的數(shù)據(jù)。而單純的寫成對象形式毕莱,就使得所有組件實(shí)例共用了一份 data器贩,就會造成一個變了全都會變的結(jié)果
3 Vue 組件通訊有哪幾種方式
props 和父組件向子組件傳遞數(shù)據(jù)是通過傳遞的颅夺,子組件傳遞數(shù)據(jù)給父組件是通過emit 觸發(fā)事件來做到的
children 獲取當(dāng)前組件的父組件和當(dāng)前組件的子組件
和listeners A->B->C。Vue 2.4 開始提供了和listeners 來解決這個問題
父組件中通過 provide 來提供變量蛹稍,然后在子組件中通過 inject 來注入變量吧黄。(官方不推薦在實(shí)際業(yè)務(wù)中使用,但是寫組件庫時很常用)
$refs 獲取組件實(shí)例
envetBus 兄弟組件數(shù)據(jù)傳遞 這種情況下可以使用事件總線的方式
vuex 狀態(tài)管理
4 Vue 的生命周期方法有哪些 一般在哪一步發(fā)請求
「beforeCreate」在實(shí)例初始化之后稳摄,數(shù)據(jù)觀測(data observer) 和 event/watcher 事件配置之前被調(diào)用稚字。在當(dāng)前階段 data、methods厦酬、computed 以及 watch 上的數(shù)據(jù)和方法都不能被訪問
「created」實(shí)例已經(jīng)創(chuàng)建完成之后被調(diào)用胆描。在這一步,實(shí)例已完成以下的配置:數(shù)據(jù)觀測(data observer)仗阅,屬性和方法的運(yùn)算昌讲, watch/event 事件回調(diào)。這里沒有如果非要想與進(jìn)行交互减噪,可以通過nextTick 來訪問 Dom
「beforeMount」在掛載開始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用短绸。
「mounted」在掛載完成后發(fā)生,在當(dāng)前階段筹裕,真實(shí)的 Dom 掛載完畢醋闭,數(shù)據(jù)完成雙向綁定,可以訪問到 Dom 節(jié)點(diǎn)
「beforeUpdate」數(shù)據(jù)更新時調(diào)用朝卒,發(fā)生在虛擬 DOM 重新渲染和打補(bǔ)吨ぢ摺(patch)之前】菇铮可以在這個鉤子中進(jìn)一步地更改狀態(tài)囚企,這不會觸發(fā)附加的重渲染過程
「updated」發(fā)生在更新完成之后,當(dāng)前階段組件 Dom 已完成更新瑞眼。要注意的是避免在此期間更改數(shù)據(jù)龙宏,因?yàn)檫@可能會導(dǎo)致無限循環(huán)的更新,該鉤子在服務(wù)器端渲染期間不被調(diào)用伤疙。
「beforeDestroy」實(shí)例銷毀之前調(diào)用银酗。在這一步,實(shí)例仍然完全可用徒像。我們可以在這時進(jìn)行善后收尾工作黍特,比如清除計時器。
「destroyed」Vue 實(shí)例銷毀后調(diào)用厨姚。調(diào)用后,Vue 實(shí)例指示的所有東西都會解綁定键菱,所有的事件監(jiān)聽器會被移除谬墙,所有的子實(shí)例也會被銷毀今布。該鉤子在服務(wù)器端渲染期間不被調(diào)用。
「activated」keep-alive 專屬拭抬,組件被激活時調(diào)用
「deactivated」keep-alive 專屬部默,組件被銷毀時調(diào)用
?
異步請求在哪一步發(fā)起?
?
可以在鉤子函數(shù) created造虎、beforeMount傅蹂、mounted 中進(jìn)行異步請求,因?yàn)樵谶@三個鉤子函數(shù)中算凿,data 已經(jīng)創(chuàng)建份蝴,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。
如果異步請求不需要依賴 Dom 推薦在 created 鉤子函數(shù)中調(diào)用異步請求氓轰,因?yàn)樵?created 鉤子函數(shù)中調(diào)用異步請求有以下優(yōu)點(diǎn):
能更快獲取到服務(wù)端數(shù)據(jù)婚夫,減少頁面 loading 時間;
ssr ?不支持 beforeMount 署鸡、mounted 鉤子函數(shù)案糙,所以放在 created 中有助于一致性;
5 v-if 和 v-show 的區(qū)別
v-if 在編譯過程中會被轉(zhuǎn)化成三元表達(dá)式,條件不滿足時不渲染此節(jié)點(diǎn)靴庆。
v-show 會被編譯成指令时捌,條件不滿足時控制樣式將對應(yīng)節(jié)點(diǎn)隱藏 (display:none)
「使用場景」
v-if 適用于在運(yùn)行時很少改變條件,不需要頻繁切換條件的場景
v-show 適用于需要非常頻繁切換條件的場景
?
擴(kuò)展補(bǔ)充:display:none炉抒、visibility:hidden 和 opacity:0 之間的區(qū)別奢讨?
?
display.png
6 說說 vue 內(nèi)置指令
內(nèi)置指令.png
7 怎樣理解 Vue 的單向數(shù)據(jù)流
數(shù)據(jù)總是從父組件傳到子組件,子組件沒有權(quán)利修改父組件傳過來的數(shù)據(jù)端礼,只能請求父組件對原始數(shù)據(jù)進(jìn)行修改禽笑。這樣會防止從子組件意外改變父級組件的狀態(tài),從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解蛤奥。
?
注意:在子組件直接用 v-model 綁定父組件傳過來的 prop 這樣是不規(guī)范的寫法 開發(fā)環(huán)境會報警告
?
如果實(shí)在要改變父組件的 prop 值 可以再 data 里面定義一個變量 并用 prop 的值初始化它 之后用 $emit 通知父組件去修改
8 computed 和 watch 的區(qū)別和運(yùn)用的場景
computed 是計算屬性佳镜,依賴其他屬性計算值,并且 computed 的值有緩存凡桥,只有當(dāng)計算值變化才會返回內(nèi)容蟀伸,它可以設(shè)置 getter 和 setter。
watch 監(jiān)聽到值的變化就會執(zhí)行回調(diào)缅刽,在回調(diào)中可以進(jìn)行一些邏輯操作啊掏。
計算屬性一般用在模板渲染中,某個值是依賴了其它的響應(yīng)式對象甚至是計算屬性計算而來衰猛;而偵聽屬性適用于觀測某個值的變化去完成一段復(fù)雜的業(yè)務(wù)邏輯
計算屬性原理詳解傳送門[1]
偵聽屬性原理詳解傳送門[2]
9 v-if 與 v-for 為什么不建議一起使用
v-for 和 v-if 不要在同一個標(biāo)簽中使用,因?yàn)榻馕鰰r先解析 v-for 再解析 v-if迟蜜。如果遇到需要同時使用時可以考慮寫成計算屬性的方式。
中等
10 Vue2.0 響應(yīng)式數(shù)據(jù)的原理
整體思路是數(shù)據(jù)劫持+觀察者模式
對象內(nèi)部通過 defineReactive 方法啡省,使用 Object.defineProperty 將屬性進(jìn)行劫持(只會劫持已經(jīng)存在的屬性)娜睛,數(shù)組則是通過重寫數(shù)組方法來實(shí)現(xiàn)髓霞。當(dāng)頁面使用對應(yīng)屬性時,每個屬性都擁有自己的 dep 屬性畦戒,存放他所依賴的 watcher(依賴收集)方库,當(dāng)屬性變化后會通知自己對應(yīng)的 watcher 去更新(派發(fā)更新)。
相關(guān)代碼如下
classObserver{
//?觀測值
constructor(value)?{
this.walk(value);
}
walk(data)?{
//?對象上的所有屬性依次進(jìn)行觀測
letkeys?=Object.keys(data);
for(leti?=0;?i?<?keys.length;?i++)?{
letkey?=?keys[i];
letvalue?=?data[key];
defineReactive(data,?key,?value);
}
}
}
//?Object.defineProperty數(shù)據(jù)劫持核心?兼容性在ie9以及以上
functiondefineReactive(data,?key,?value){
observe(value);//?遞歸關(guān)鍵
//?--如果value還是一個對象會繼續(xù)走一遍odefineReactive?層層遍歷一直到value不是對象才停止
//???思考障斋?如果Vue數(shù)據(jù)嵌套層級過深?>>性能會受影響
Object.defineProperty(data,?key,?{
get()?{
console.log("獲取值");
//需要做依賴收集過程?這里代碼沒寫出來
returnvalue;
},
set(newValue)?{
if(newValue?===?value)return;
console.log("設(shè)置值");
//需要做派發(fā)更新過程?這里代碼沒寫出來
value?=?newValue;
},
});
}
exportfunctionobserve(value){
//?如果傳過來的是對象或者數(shù)組?進(jìn)行屬性劫持
if(
Object.prototype.toString.call(value)?==="[object?Object]"||
Array.isArray(value)
)?{
returnnewObserver(value);
}
}
復(fù)制代碼
響應(yīng)式數(shù)據(jù)原理詳解傳送門[3]
11 Vue 如何檢測數(shù)組變化
數(shù)組考慮性能原因沒有用 defineProperty 對數(shù)組的每一項(xiàng)進(jìn)行攔截纵潦,而是選擇對 7 種數(shù)組(push,shift,pop,splice,unshift,sort,reverse)方法進(jìn)行重寫(AOP 切片思想)
所以在 Vue 中修改數(shù)組的索引和長度是無法監(jiān)控到的。需要通過以上 7 種變異方法修改數(shù)組才會觸發(fā)數(shù)組對應(yīng)的 watcher 進(jìn)行更新
相關(guān)代碼如下
//?src/obserber/array.js
//?先保留數(shù)組原型
constarrayProto?=Array.prototype;
//?然后將arrayMethods繼承自數(shù)組原型
//?這里是面向切片編程思想(AOP)--不破壞封裝的前提下垃环,動態(tài)的擴(kuò)展功能
exportconstarrayMethods?=Object.create(arrayProto);
letmethodsToPatch?=?[
"push",
"pop",
"shift",
"unshift",
"splice",
"reverse",
"sort",
];
methodsToPatch.forEach((method)?=>{
arrayMethods[method]?=function(...args){
//???這里保留原型方法的執(zhí)行結(jié)果
constresult?=?arrayProto[method].apply(this,?args);
//?這句話是關(guān)鍵
//?this代表的就是數(shù)據(jù)本身?比如數(shù)據(jù)是{a:[1,2,3]}?那么我們使用a.push(4)??this就是a??ob就是a.__ob__?這個屬性就是上段代碼增加的?代表的是該數(shù)據(jù)已經(jīng)被響應(yīng)式觀察過了指向Observer實(shí)例
constob?=this.__ob__;
//?這里的標(biāo)志就是代表數(shù)組有新增操作
letinserted;
switch(method)?{
case"push":
case"unshift":
inserted?=?args;
break;
case"splice":
inserted?=?args.slice(2);
default:
break;
}
//?如果有新增的元素?inserted是一個數(shù)組?調(diào)用Observer實(shí)例的observeArray對數(shù)組每一項(xiàng)進(jìn)行觀測
if(inserted)?ob.observeArray(inserted);
//?之后咱們還可以在這里檢測到數(shù)組改變了之后從而觸發(fā)視圖更新的操作--后續(xù)源碼會揭曉
returnresult;
};
});
復(fù)制代碼
數(shù)組的觀測原理詳解傳送門[4]
12 vue3.0 用過嗎 了解多少
響應(yīng)式原理的改變 Vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
組件選項(xiàng)聲明方式 Vue3.x 使用 Composition API setup 是 Vue3.x 新增的一個選項(xiàng)邀层, 他是組件內(nèi)使用 Composition API 的入口。
模板語法變化 slot 具名插槽語法 自定義指令 v-model 升級
其它方面的更改 Suspense 支持 Fragment(多個根節(jié)點(diǎn))和 Protal(在 dom 其他部分渲染組建內(nèi)容)組件晴裹,針對一些特殊的場景做了處理被济。基于 treeshaking 優(yōu)化涧团,提供了更多的內(nèi)置功能只磷。
Vue3.0 新特性以及使用經(jīng)驗(yàn)總結(jié)傳送門[5]
13 Vue3.0 和 2.0 的響應(yīng)式原理區(qū)別
Vue3.x 改用 Proxy 替代 Object.defineProperty。因?yàn)?Proxy 可以直接監(jiān)聽對象和數(shù)組的變化泌绣,并且有多達(dá) 13 種攔截方法钮追。
相關(guān)代碼如下
import{?mutableHandlers?}from"./baseHandlers";//?代理相關(guān)邏輯
import{?isObject?}from"./util";//?工具方法
exportfunctionreactive(target){
//?根據(jù)不同參數(shù)創(chuàng)建不同響應(yīng)式對象
returncreateReactiveObject(target,?mutableHandlers);
}
functioncreateReactiveObject(target,?baseHandler){
if(!isObject(target))?{
returntarget;
}
constobserved?=newProxy(target,?baseHandler);
returnobserved;
}
constget=?createGetter();
constset=?createSetter();
function?createGetter()?{
returnfunctionget(target,?key,?receiver){
//?對獲取的值進(jìn)行放射
constres?=Reflect.get(target,?key,?receiver);
console.log("屬性獲取",?key);
if(isObject(res))?{
//?如果獲取的值是對象類型,則返回當(dāng)前對象的代理對象
returnreactive(res);
}
returnres;
};
}
functioncreateSetter(){
returnfunctionset(target,?key,?value,?receiver){
constoldValue?=?target[key];
consthadKey?=?hasOwn(target,?key);
constresult?=Reflect.set(target,?key,?value,?receiver);
if(!hadKey)?{
console.log("屬性新增",?key,?value);
}elseif(hasChanged(value,?oldValue))?{
console.log("屬性值被修改",?key,?value);
}
returnresult;
};
}
exportconstmutableHandlers?=?{
get,?//?當(dāng)獲取屬性時調(diào)用此方法
set,?//?當(dāng)修改屬性時調(diào)用此方法
};
復(fù)制代碼
14 Vue 的父子組件生命周期鉤子函數(shù)執(zhí)行順序
加載渲染過程
父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子 created->子 beforeMount->子 mounted->父 mounted
子組件更新過程
父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
父組件更新過程
父 beforeUpdate->父 updated
銷毀過程
父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed
15 虛擬 DOM 是什么 有什么優(yōu)缺點(diǎn)
由于在瀏覽器中操作 DOM 是很昂貴的阿迈。頻繁的操作 DOM元媚,會產(chǎn)生一定的性能問題。這就是虛擬 Dom 的產(chǎn)生原因苗沧。Vue2 的 Virtual DOM 借鑒了開源庫 snabbdom 的實(shí)現(xiàn)刊棕。Virtual DOM 本質(zhì)就是用一個原生的 JS 對象去描述一個 DOM 節(jié)點(diǎn),是對真實(shí) DOM 的一層抽象待逞。
「優(yōu)點(diǎn):」
保證性能下限:框架的虛擬 DOM 需要適配任何上層 API 可能產(chǎn)生的操作甥角,它的一些 DOM 操作的實(shí)現(xiàn)必須是普適的,所以它的性能并不是最優(yōu)的识樱;但是比起粗暴的 DOM 操作性能要好很多嗤无,因此框架的虛擬 DOM 至少可以保證在你不需要手動優(yōu)化的情況下,依然可以提供還不錯的性能怜庸,即保證性能的下限当犯;
無需手動操作 DOM:我們不再需要手動去操作 DOM,只需要寫好 View-Model 的代碼邏輯割疾,框架會根據(jù)虛擬 DOM 和 數(shù)據(jù)雙向綁定嚎卫,幫我們以可預(yù)期的方式更新視圖,極大提高我們的開發(fā)效率宏榕;
跨平臺:虛擬 DOM 本質(zhì)上是 JavaScript 對象,而 DOM 與平臺強(qiáng)相關(guān)拓诸,相比之下虛擬 DOM 可以進(jìn)行更方便地跨平臺操作胸懈,例如服務(wù)器渲染、weex 開發(fā)等等恰响。
「缺點(diǎn):」
無法進(jìn)行極致優(yōu)化:雖然虛擬 DOM + 合理的優(yōu)化,足以應(yīng)對絕大部分應(yīng)用的性能需求涌献,但在一些性能要求極高的應(yīng)用中虛擬 DOM 無法進(jìn)行針對性的極致優(yōu)化胚宦。
首次渲染大量 DOM 時,由于多了一層虛擬 DOM 的計算燕垃,會比 innerHTML 插入慢枢劝。
16 v-model 原理
v-model 只是語法糖而已
v-model 在內(nèi)部為不同的輸入元素使用不同的 property 并拋出不同的事件:
text 和 textarea 元素使用 value property 和 input 事件;
checkbox 和 radio 使用 checked property 和 change 事件卜壕;
select 字段將 value 作為 prop 并將 change 作為事件您旁。
?
注意:對于需要使用輸入法 (如中文、日文轴捎、韓文等) 的語言鹤盒,你會發(fā)現(xiàn) v-model 不會在輸入法組合文字過程中得到更新。
?
在普通標(biāo)簽上
//這一行等于下一行
復(fù)制代碼
在組件上
-->
<!--?子組件定義?-->
Vue.component('currency-input',?{
template:?`
ref="input"
:value="value"
@input="$emit('input',?$event.target.value)"
>
`,
props:?['value'],
})
復(fù)制代碼
17 v-for 為什么要加 key
如果不使用 key侦副,Vue 會使用一種最大限度減少動態(tài)元素并且盡可能的嘗試就地修改/復(fù)用相同類型元素的算法侦锯。key 是為 Vue 中 vnode 的唯一標(biāo)記,通過這個 key秦驯,我們的 diff 操作可以更準(zhǔn)確尺碰、更快速
「更準(zhǔn)確」:因?yàn)閹?key 就不是就地復(fù)用了,在 sameNode 函數(shù) a.key === b.key 對比中可以避免就地復(fù)用的情況译隘。所以會更加準(zhǔn)確亲桥。
「更快速」:利用 key 的唯一性生成 map 對象來獲取對應(yīng)節(jié)點(diǎn),比遍歷方式更快
相關(guān)代碼如下
//?判斷兩個vnode的標(biāo)簽和key是否相同?如果相同?就可以認(rèn)為是同一節(jié)點(diǎn)就地復(fù)用
functionisSameVnode(oldVnode,?newVnode){
returnoldVnode.tag?===?newVnode.tag?&&?oldVnode.key?===?newVnode.key;
}
//?根據(jù)key來創(chuàng)建老的兒子的index映射表??類似?{'a':0,'b':1}?代表key為'a'的節(jié)點(diǎn)在第一個位置?key為'b'的節(jié)點(diǎn)在第二個位置
functionmakeIndexByKey(children){
letmap?=?{};
children.forEach((item,?index)?=>{
map[item.key]?=?index;
});
returnmap;
}
//?生成的映射表
letmap?=?makeIndexByKey(oldCh);
復(fù)制代碼
diff 算法詳解傳送門[6]
18 Vue 事件綁定原理
原生事件綁定是通過 addEventListener 綁定給真實(shí)元素的固耘,組件事件綁定是通過 Vue 自定義的 $on 實(shí)現(xiàn)的题篷。如果要在組件上使用原生事件,需要加.native 修飾符玻驻,這樣就相當(dāng)于在父組件中把子組件當(dāng)做普通 html 標(biāo)簽悼凑,然后加上原生事件。
璧瞬、emit 是基于發(fā)布訂閱模式的户辫,維護(hù)一個事件中心,on 的時候?qū)⑹录疵Q存在事件中心里嗤锉,稱之為訂閱者渔欢,然后 emit 將對應(yīng)的事件進(jìn)行發(fā)布,去執(zhí)行事件中心里的對應(yīng)的監(jiān)聽器
手寫發(fā)布訂閱原理傳送門[7]
19 vue-router 路由鉤子函數(shù)是什么 執(zhí)行順序是什么
路由鉤子的執(zhí)行流程, 鉤子函數(shù)種類有:全局守衛(wèi)瘟忱、路由守衛(wèi)奥额、組件守衛(wèi)
「完整的導(dǎo)航解析流程:」
導(dǎo)航被觸發(fā)苫幢。
在失活的組件里調(diào)用 beforeRouteLeave 守衛(wèi)。
調(diào)用全局的 beforeEach 守衛(wèi)垫挨。
在重用的組件里調(diào)用 beforeRouteUpdate 守衛(wèi) (2.2+)韩肝。
在路由配置里調(diào)用 beforeEnter。
解析異步路由組件九榔。
在被激活的組件里調(diào)用 beforeRouteEnter哀峻。
調(diào)用全局的 beforeResolve 守衛(wèi) (2.5+)。
導(dǎo)航被確認(rèn)哲泊。
調(diào)用全局的 afterEach 鉤子剩蟀。
觸發(fā) DOM 更新。
調(diào)用 beforeRouteEnter 守衛(wèi)中傳給 next 的回調(diào)函數(shù)切威,創(chuàng)建好的組件實(shí)例會作為回調(diào)函數(shù)的參數(shù)傳入育特。
20 vue-router 動態(tài)路由是什么 有什么問題
我們經(jīng)常需要把某種模式匹配到的所有路由,全都映射到同個組件先朦。例如缰冤,我們有一個 User 組件,對于所有 ID 各不相同的用戶喳魏,都要使用這個組件來渲染锋谐。那么,我們可以在 vue-router 的路由路徑中使用“動態(tài)路徑參數(shù)”(dynamic segment) 來達(dá)到這個效果:
constUser?=?{
template:"<div>User</div>",
};
constrouter?=newVueRouter({
routes:?[
//?動態(tài)路徑參數(shù)?以冒號開頭
{path:"/user/:id",component:?User?},
],
});
復(fù)制代碼
?
問題:vue-router 組件復(fù)用導(dǎo)致路由參數(shù)失效怎么辦截酷?
?
解決方法:
1.通過 watch 監(jiān)聽路由參數(shù)再發(fā)請求
watch:?{//通過watch來監(jiān)聽路由變化
"$route":function(){
this.getData(this.$route.params.xxx);
}
}
復(fù)制代碼
2.用 :key 來阻止“復(fù)用”
復(fù)制代碼
21 談一下對 vuex 的個人理解
vuex 是專門為 vue 提供的全局狀態(tài)管理系統(tǒng)涮拗,用于多個組件中數(shù)據(jù)共享、數(shù)據(jù)緩存等迂苛。(無法持久化三热、內(nèi)部核心原理是通過創(chuàng)造一個全局實(shí)例 new Vue)
主要包括以下幾個模塊:
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)樹中。
22 Vuex 頁面刷新數(shù)據(jù)丟失怎么解決
需要做 vuex 數(shù)據(jù)持久化 一般使用本地存儲的方案來保存數(shù)據(jù) 可以自己設(shè)計存儲方案 也可以使用第三方插件
推薦使用 vuex-persist 插件爷恳,它就是為 Vuex 持久化存儲而生的一個插件有缆。不需要你手動存取 storage ,而是直接將狀態(tài)保存至 cookie 或者 localStorage 中
23 Vuex 為什么要分模塊并且加命名空間
「模塊」:由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象棚壁。當(dāng)應(yīng)用變得非常復(fù)雜時杯矩,store 對象就有可能變得相當(dāng)臃腫。為了解決以上問題袖外,Vuex 允許我們將 store 分割成模塊(module)史隆。每個模塊擁有自己的 state、mutation曼验、action逆害、getter、甚至是嵌套子模塊蚣驼。
「命名空間」:默認(rèn)情況下,模塊內(nèi)部的 action相艇、mutation 和 getter 是注冊在全局命名空間的——這樣使得多個模塊能夠?qū)ν?mutation 或 action 作出響應(yīng)颖杏。如果希望你的模塊具有更高的封裝度和復(fù)用性,你可以通過添加 namespaced: true 的方式使其成為帶命名空間的模塊坛芽。當(dāng)模塊被注冊后留储,它的所有 getter、action 及 mutation 都會自動根據(jù)模塊注冊的路徑調(diào)整命名咙轩。
24 使用過 Vue SSR 嗎获讳?說說 SSR
SSR 也就是服務(wù)端渲染,也就是將 Vue 在客戶端把標(biāo)簽渲染成 HTML 的工作放在服務(wù)端完成活喊,然后再把 html 直接返回給客戶端丐膝。
「優(yōu)點(diǎn):」
SSR 有著更好的 SEO、并且首屏加載速度更快
「缺點(diǎn):」開發(fā)條件會受到限制钾菊,服務(wù)器端渲染只支持 beforeCreate 和 created 兩個鉤子帅矗,當(dāng)我們需要一些外部擴(kuò)展庫時需要特殊處理,服務(wù)端渲染應(yīng)用程序也需要處于 Node.js 的運(yùn)行環(huán)境煞烫。
服務(wù)器會有更大的負(fù)載需求
25 vue 中使用了哪些設(shè)計模式
1.工廠模式 - 傳入?yún)?shù)即可創(chuàng)建實(shí)例
虛擬 DOM 根據(jù)參數(shù)的不同返回基礎(chǔ)標(biāo)簽的 Vnode 和組件 Vnode
2.單例模式 - 整個程序有且僅有一個實(shí)例
vuex 和 vue-router 的插件注冊方法 install 判斷如果系統(tǒng)存在實(shí)例就直接返回掉
3.發(fā)布-訂閱模式 (vue 事件機(jī)制)
4.觀察者模式 (響應(yīng)式數(shù)據(jù)原理)
5.裝飾模式: (@裝飾器的用法)
6.策略模式 策略模式指對象有某個行為,但是在不同的場景中,該行為有不同的實(shí)現(xiàn)方案-比如選項(xiàng)的合并策略
...其他模式歡迎補(bǔ)充
26 你都做過哪些 Vue 的性能優(yōu)化
?
這里只列舉針對 Vue 的性能優(yōu)化 整個項(xiàng)目的性能優(yōu)化是一個大工程 可以另寫一篇性能優(yōu)化的文章 哈哈
?
對象層級不要過深浑此,否則性能就會差
不需要響應(yīng)式的數(shù)據(jù)不要放到 data 中(可以用 Object.freeze() 凍結(jié)數(shù)據(jù))
v-if 和 v-show 區(qū)分使用場景
computed 和 watch 區(qū)分使用場景
v-for 遍歷必須加 key,key 最好是 id 值滞详,且避免同時使用 v-if
大數(shù)據(jù)列表和表格性能優(yōu)化-虛擬列表/虛擬表格
防止內(nèi)部泄漏凛俱,組件銷毀后把全局變量和事件銷毀
圖片懶加載
路由懶加載
第三方插件的按需引入
適當(dāng)采用 keep-alive 緩存組件
防抖、節(jié)流運(yùn)用
服務(wù)端渲染 SSR or 預(yù)渲染
困難
27 Vue.mixin 的使用場景和原理
在日常的開發(fā)中料饥,我們經(jīng)常會遇到在不同的組件中經(jīng)常會需要用到一些相同或者相似的代碼蒲犬,這些代碼的功能相對獨(dú)立,可以通過 Vue 的 mixin 功能抽離公共的業(yè)務(wù)邏輯岸啡,原理類似“對象的繼承”暖哨,當(dāng)組件初始化時會調(diào)用 mergeOptions 方法進(jìn)行合并,采用策略模式針對不同的屬性進(jìn)行合并。當(dāng)組件和混入對象含有同名選項(xiàng)時篇裁,這些選項(xiàng)將以恰當(dāng)?shù)姆绞竭M(jìn)行“合并”沛慢。
相關(guān)代碼如下
exportdefaultfunctioninitMixin(Vue){
Vue.mixin?=function(mixin){
//???合并對象
this.options=mergeOptions(this.options,mixin)
};
}
};
//?src/util/index.js
//?定義生命周期
exportconstLIFECYCLE_HOOKS?=?[
"beforeCreate",
"created",
"beforeMount",
"mounted",
"beforeUpdate",
"updated",
"beforeDestroy",
"destroyed",
];
//?合并策略
conststrats?=?{};
//?mixin核心方法
exportfunctionmergeOptions(parent,?child){
constoptions?=?{};
//?遍歷父親
for(letkinparent)?{
mergeFiled(k);
}
//?父親沒有?兒子有
for(letkinchild)?{
if(!parent.hasOwnProperty(k))?{
mergeFiled(k);
}
}
//真正合并字段方法
functionmergeFiled(k){
if(strats[k])?{
options[k]?=?strats[k](parent[k],?child[k]"k]?=?strats[k");
}else{
//?默認(rèn)策略
options[k]?=?child[k]???child[k]?:?parent[k];
}
}
returnoptions;
}
復(fù)制代碼
Vue.mixin 原理詳解傳送門[8]
28 nextTick 使用場景和原理
nextTick 中的回調(diào)是在下次 DOM 更新循環(huán)結(jié)束之后執(zhí)行的延遲回調(diào)。在修改數(shù)據(jù)之后立即使用這個方法达布,獲取更新后的 DOM团甲。主要思路就是采用微任務(wù)優(yōu)先的方式調(diào)用異步方法去執(zhí)行 nextTick 包裝的方法
相關(guān)代碼如下
letcallbacks?=?[];
letpending?=false;
functionflushCallbacks(){
pending?=false;//把標(biāo)志還原為false
//?依次執(zhí)行回調(diào)
for(leti?=0;?i?<?callbacks.length;?i++)?{
callbacks[i]("i");
}
}
lettimerFunc;//定義異步方法??采用優(yōu)雅降級
if(typeofPromise!=="undefined")?{
//?如果支持promise
constp?=Promise.resolve();
timerFunc?=()=>{
p.then(flushCallbacks);
};
}elseif(typeofMutationObserver?!=="undefined")?{
//?MutationObserver?主要是監(jiān)聽dom變化?也是一個異步方法
letcounter?=1;
constobserver?=newMutationObserver(flushCallbacks);
consttextNode?=document.createTextNode(String(counter));
observer.observe(textNode,?{
characterData:true,
});
timerFunc?=()=>{
counter?=?(counter?+1)?%2;
textNode.data?=String(counter);
};
}elseif(typeofsetImmediate?!=="undefined")?{
//?如果前面都不支持?判斷setImmediate
timerFunc?=()=>{
setImmediate(flushCallbacks);
};
}else{
//?最后降級采用setTimeout
timerFunc?=()=>{
setTimeout(flushCallbacks,0);
};
}
exportfunctionnextTick(cb){
//?除了渲染watcher??還有用戶自己手動調(diào)用的nextTick?一起被收集到數(shù)組
callbacks.push(cb);
if(!pending)?{
//?如果多次調(diào)用nextTick??只會執(zhí)行一次異步?等異步隊(duì)列清空之后再把標(biāo)志變?yōu)閒alse
pending?=true;
timerFunc();
}
}
復(fù)制代碼
nextTick 原理詳解傳送門[9]
29 keep-alive 使用場景和原理
keep-alive 是 Vue 內(nèi)置的一個組件,可以實(shí)現(xiàn)組件緩存黍聂,當(dāng)組件切換時不會對當(dāng)前組件進(jìn)行卸載躺苦。
常用的兩個屬性 include/exclude,允許組件有條件的進(jìn)行緩存产还。
兩個生命周期 activated/deactivated匹厘,用來得知當(dāng)前組件是否處于活躍狀態(tài)。
keep-alive 的中還運(yùn)用了 LRU(最近最少使用) 算法脐区,選擇最近最久未使用的組件予以淘汰愈诚。
相關(guān)代碼如下
exportdefault{
name:"keep-alive",
abstract:true,//抽象組件
props:?{
include:?patternTypes,//要緩存的組件
exclude:?patternTypes,//要排除的組件
max:?[String,Number],//最大緩存數(shù)
},
created()?{
this.cache?=Object.create(null);//緩存對象??{a:vNode,b:vNode}
this.keys?=?[];//緩存組件的key集合?[a,b]
},
destroyed()?{
for(constkeyinthis.cache)?{
pruneCacheEntry(this.cache,?key,this.keys);
}
},
mounted()?{
//動態(tài)監(jiān)聽include??exclude
this.$watch("include",?(val)?=>?{
pruneCache(this,?(name)?=>?matches(val,?name));
});
this.$watch("exclude",?(val)?=>?{
pruneCache(this,?(name)?=>?!matches(val,?name));
});
},
render()?{
constslot?=this.$slots.default;//獲取包裹的插槽默認(rèn)值
constvnode:?VNode?=?getFirstComponentChild(slot);//獲取第一個子組件
constcomponentOptions:??VNodeComponentOptions?=
vnode?&&?vnode.componentOptions;
if(componentOptions)?{
//?check?pattern
constname:??string?=?getComponentName(componentOptions);
const{?include,?exclude?}?=this;
//?不走緩存
if(
//?not?included??不包含
(include?&&?(!name?||?!matches(include,?name)))?||
//?excluded??排除里面
(exclude?&&?name?&&?matches(exclude,?name))
)?{
//返回虛擬節(jié)點(diǎn)
returnvnode;
}
const{?cache,?keys?}?=this;
constkey:??string?=
vnode.key?==null
?//?same?constructor?may?get?registered?as?different?local?components
//?so?cid?alone?is?not?enough?(#3269)
componentOptions.Ctor.cid?+
(componentOptions.tag??`::${componentOptions.tag}`:"")
:?vnode.key;
if(cache[key])?{
//通過key?找到緩存?獲取實(shí)例
vnode.componentInstance?=?cache[key].componentInstance;
//?make?current?key?freshest
remove(keys,?key);//通過LRU算法把數(shù)組里面的key刪掉
keys.push(key);//把它放在數(shù)組末尾
}else{
cache[key]?=?vnode;//沒找到就換存下來
keys.push(key);//把它放在數(shù)組末尾
//?prune?oldest?entry??//如果超過最大值就把數(shù)組第0項(xiàng)刪掉
if(this.max?&&?keys.length?>parseInt(this.max))?{
pruneCacheEntry(cache,?keys[0],?keys,this._vnode);
}
}
vnode.data.keepAlive?=true;//標(biāo)記虛擬節(jié)點(diǎn)已經(jīng)被緩存
}
//?返回虛擬節(jié)點(diǎn)
returnvnode?||?(slot?&&?slot[0]);
},
};
復(fù)制代碼
?
擴(kuò)展補(bǔ)充:LRU 算法是什么?
?
lrusuanfa.png
LRU 的核心思想是如果數(shù)據(jù)最近被訪問過牛隅,那么將來被訪問的幾率也更高炕柔,所以我們將命中緩存的組件 key 重新插入到 this.keys 的尾部,這樣一來媒佣,this.keys 中越往頭部的數(shù)據(jù)即將來被訪問幾率越低匕累,所以當(dāng)緩存數(shù)量達(dá)到最大值時,我們就刪除將來被訪問幾率最低的數(shù)據(jù)默伍,即 this.keys 中第一個緩存的組件欢嘿。
30 Vue.set 方法原理
了解 Vue 響應(yīng)式原理的同學(xué)都知道在兩種情況下修改數(shù)據(jù) Vue 是不會觸發(fā)視圖更新的
1.在實(shí)例創(chuàng)建之后添加新的屬性到實(shí)例上(給響應(yīng)式對象新增屬性)
2.直接更改數(shù)組下標(biāo)來修改數(shù)組的值
Vue.set 或者說是 $set 原理如下
因?yàn)轫憫?yīng)式數(shù)據(jù) 我們給對象和數(shù)組本身都增加了__ob__屬性,代表的是 Observer 實(shí)例也糊。當(dāng)給對象新增不存在的屬性 首先會把新的屬性進(jìn)行響應(yīng)式跟蹤 然后會觸發(fā)對象__ob__的 dep 收集到的 watcher 去更新际插,當(dāng)修改數(shù)組索引時我們調(diào)用數(shù)組本身的 splice 方法去更新數(shù)組
相關(guān)代碼如下
exportfunctionset(target:Array|Object,?key:any,?val:any):any{
//?如果是數(shù)組?調(diào)用我們重寫的splice方法?(這樣可以更新視圖)
if(Array.isArray(target)?&&?isValidArrayIndex(key))?{
target.length?=Math.max(target.length,?key);
target.splice(key,1,?val);
returnval;
}
//?如果是對象本身的屬性,則直接添加即可
if(keyintarget?&&?!(keyinObject.prototype))?{
target[key]?=?val;
returnval;
}
constob?=?(target:any).__ob__;
//?如果不是響應(yīng)式的也不需要將其定義成響應(yīng)式屬性
if(!ob)?{
target[key]?=?val;
returnval;
}
//?將屬性定義成響應(yīng)式的
defineReactive(ob.value,?key,?val);
//?通知視圖更新
ob.dep.notify();
returnval;
}
復(fù)制代碼
響應(yīng)式數(shù)據(jù)原理詳解傳送門[10]
31 Vue.extend 作用和原理
官方解釋:Vue.extend 使用基礎(chǔ) Vue 構(gòu)造器显设,創(chuàng)建一個“子類”框弛。參數(shù)是一個包含組件選項(xiàng)的對象。
其實(shí)就是一個子類構(gòu)造器 是 Vue 組件的核心 api 實(shí)現(xiàn)思路就是使用原型繼承的方法返回了 Vue 的子類 并且利用 mergeOptions 把傳入組件的 options 和父類的 options 進(jìn)行了合并
相關(guān)代碼如下
exportdefaultfunctioninitExtend(Vue){
letcid?=0;//組件的唯一標(biāo)識
//?創(chuàng)建子類繼承Vue父類?便于屬性擴(kuò)展
Vue.extend?=function(extendOptions){
//?創(chuàng)建子類的構(gòu)造函數(shù)?并且調(diào)用初始化方法
constSub?=functionVueComponent(options){
this._init(options);//調(diào)用Vue初始化方法
};
Sub.cid?=?cid++;
Sub.prototype?=Object.create(this.prototype);//?子類原型指向父類
Sub.prototype.constructor?=?Sub;//constructor指向自己
Sub.options?=?mergeOptions(this.options,?extendOptions);//合并自己的options和父類的options
returnSub;
};
}
復(fù)制代碼
Vue 組件原理詳解傳送門[11]
32 寫過自定義指令嗎 原理是什么
指令本質(zhì)上是裝飾器捕捂,是 vue 對 HTML 元素的擴(kuò)展瑟枫,給 HTML 元素增加自定義功能。vue 編譯 DOM 時指攒,會找到指令對象慷妙,執(zhí)行指令的相關(guān)方法。
自定義指令有五個生命周期(也叫鉤子函數(shù))允悦,分別是 bind膝擂、inserted、update、componentUpdated架馋、unbind
1.bind:只調(diào)用一次狞山,指令第一次綁定到元素時調(diào)用。在這里可以進(jìn)行一次性的初始化設(shè)置叉寂。
2. inserted:被綁定元素插入父節(jié)點(diǎn)時調(diào)用?(僅保證父節(jié)點(diǎn)存在萍启,但不一定已被插入文檔中)。
3. update:被綁定于元素所在的模板更新時調(diào)用屏鳍,而無論綁定值是否變化勘纯。通過比較更新前后的綁定值,可以忽略不必要的模板更新钓瞭。
4. componentUpdated:被綁定元素所在模板完成一次更新周期時調(diào)用驳遵。
5. unbind:只調(diào)用一次,指令與元素解綁時調(diào)用山涡。
復(fù)制代碼
「原理」
1.在生成 ast 語法樹時堤结,遇到指令會給當(dāng)前元素添加 directives 屬性
2.通過 genDirectives 生成指令代碼
3.在 patch 前將指令的鉤子提取到 cbs 中,在 patch 過程中調(diào)用對應(yīng)的鉤子
4.當(dāng)執(zhí)行指令對應(yīng)鉤子函數(shù)時,調(diào)用對應(yīng)指令定義的方法
33 Vue 修飾符有哪些
「事件修飾符」
.stop 阻止事件繼續(xù)傳播
.prevent 阻止標(biāo)簽?zāi)J(rèn)行為
.capture 使用事件捕獲模式,即元素自身觸發(fā)的事件先在此處處理佳鳖,然后才交由內(nèi)部元素進(jìn)行處理
.self 只當(dāng)在 event.target 是當(dāng)前元素自身時觸發(fā)處理函數(shù)
.once 事件將只會觸發(fā)一次
.passive 告訴瀏覽器你不想阻止事件的默認(rèn)行為
「v-model 的修飾符」
.lazy 通過這個修飾符,轉(zhuǎn)變?yōu)樵?change 事件再同步
.number 自動將用戶的輸入值轉(zhuǎn)化為數(shù)值類型
.trim 自動過濾用戶輸入的首尾空格
「鍵盤事件的修飾符」
.enter
.tab
.delete (捕獲“刪除”和“退格”鍵)
.esc
.space
.up
.down
.left
.right
「系統(tǒng)修飾鍵」
.ctrl
.alt
.shift
.meta
「鼠標(biāo)按鈕修飾符」
.left
.right
.middle
34 Vue 模板編譯原理
Vue 的編譯過程就是將 template 轉(zhuǎn)化為 render 函數(shù)的過程 分為以下三步
第一步是將?模板字符串?轉(zhuǎn)換成?element?ASTs(解析器)
第二步是對?AST?進(jìn)行靜態(tài)節(jié)點(diǎn)標(biāo)記媒惕,主要用來做虛擬DOM的渲染優(yōu)化(優(yōu)化器)
第三步是?使用?element?ASTs?生成?render?函數(shù)代碼字符串(代碼生成器)
復(fù)制代碼
相關(guān)代碼如下
exportfunctioncompileToFunctions(template){
//?我們需要把html字符串變成render函數(shù)
//?1.把html代碼轉(zhuǎn)成ast語法樹??ast用來描述代碼本身形成樹結(jié)構(gòu)?不僅可以描述html?也能描述css以及js語法
//?很多庫都運(yùn)用到了ast?比如?webpack?babel?eslint等等
letast?=?parse(template);
//?2.優(yōu)化靜態(tài)節(jié)點(diǎn)
//?這個有興趣的可以去看源碼??不影響核心功能就不實(shí)現(xiàn)了
//???if?(options.optimize?!==?false)?{
//?????optimize(ast,?options);
//???}
//?3.通過ast?重新生成代碼
//?我們最后生成的代碼需要和render函數(shù)一樣
//?類似_c('div',{id:"app"},_c('div',undefined,_v("hello"+_s(name)),_c('span',undefined,_v("world"))))
//?_c代表創(chuàng)建元素?_v代表創(chuàng)建文本?_s代表文Json.stringify--把對象解析成文本
letcode?=?generate(ast);
//???使用with語法改變作用域?yàn)閠his??之后調(diào)用render函數(shù)可以使用call改變this?方便code里面的變量取值
letrenderFn?=newFunction(`with(this){return${code}}`);
returnrenderFn;
}
復(fù)制代碼
模板編譯原理詳解傳送門[12]
35 生命周期鉤子是如何實(shí)現(xiàn)的
Vue 的生命周期鉤子核心實(shí)現(xiàn)是利用發(fā)布訂閱模式先把用戶傳入的的生命周期鉤子訂閱好(內(nèi)部采用數(shù)組的方式存儲)然后在創(chuàng)建組件實(shí)例的過程中會一次執(zhí)行對應(yīng)的鉤子方法(發(fā)布)
相關(guān)代碼如下
exportfunctioncallHook(vm,?hook){
//?依次執(zhí)行生命周期對應(yīng)的方法
consthandlers?=?vm.$options[hook];
if(handlers)?{
for(leti?=0;?i?<?handlers.length;?i++)?{
handlers[i].call(vm);//生命周期里面的this指向當(dāng)前實(shí)例
}
}
}
//?調(diào)用的時候
Vue.prototype._init?=function(options){
constvm?=this;
vm.$options?=?mergeOptions(vm.constructor.options,?options);
callHook(vm,"beforeCreate");//初始化數(shù)據(jù)之前
//?初始化狀態(tài)
initState(vm);
callHook(vm,"created");//初始化數(shù)據(jù)之后
if(vm.$options.el)?{
vm.$mount(vm.$options.el);
}
};
復(fù)制代碼
生命周期實(shí)現(xiàn)詳解傳送門[13]
36 函數(shù)式組件使用場景和原理
函數(shù)式組件與普通組件的區(qū)別
1.函數(shù)式組件需要在聲明組件是指定?functional:true
2.不需要實(shí)例化系吩,所以沒有this,this通過render函數(shù)的第二個參數(shù)context來代替
3.沒有生命周期鉤子函數(shù),不能使用計算屬性妒蔚,watch
4.不能通過$emit對外暴露事件穿挨,調(diào)用事件只能通過context.listeners.click的方式調(diào)用外部傳入的事件
5.因?yàn)楹瘮?shù)式組件是沒有實(shí)例化的,所以在外部通過ref去引用組件時肴盏,實(shí)際引用的是HTMLElement
6.函數(shù)式組件的props可以不用顯示聲明科盛,所以沒有在props里面聲明的屬性都會被自動隱式解析為prop,而普通組件所有未聲明的屬性都解析到$attrs里面,并自動掛載到組件根元素上面(可以通過inheritAttrs屬性禁止)
復(fù)制代碼
優(yōu)點(diǎn) 1.由于函數(shù)式組件不需要實(shí)例化菜皂,無狀態(tài)贞绵,沒有生命周期,所以渲染性能要好于普通組件 2.函數(shù)式組件結(jié)構(gòu)比較簡單恍飘,代碼結(jié)構(gòu)更清晰
使用場景:
一個簡單的展示組件榨崩,作為容器組件使用 比如 router-view 就是一個函數(shù)式組件
“高階組件”——用于接收一個組件作為參數(shù),返回一個被包裝過的組件
相關(guān)代碼如下
if(isTrue(Ctor.options.functional))?{
//?帶有functional的屬性的就是函數(shù)式組件
returncreateFunctionalComponent(Ctor,?propsData,?data,?context,?children);
}
constlisteners?=?data.on;
data.on?=?data.nativeOn;
installComponentHooks(data);//?安裝組件相關(guān)鉤子?(函數(shù)式組件沒有調(diào)用此方法章母,從而性能高于普通組件)
復(fù)制代碼
37 能說下 vue-router 中常用的路由模式實(shí)現(xiàn)原理嗎
「hash 模式」
location.hash 的值實(shí)際就是 URL 中#后面的東西 它的特點(diǎn)在于:hash 雖然出現(xiàn) URL 中母蛛,但不會被包含在 HTTP 請求中,對后端完全沒有影響乳怎,因此改變 hash 不會重新加載頁面彩郊。
可以為 hash 的改變添加監(jiān)聽事件
window.addEventListener("hashchange",?funcRef,false);
復(fù)制代碼
每一次改變 hash(window.location.hash),都會在瀏覽器的訪問歷史中增加一個記錄利用 hash 的以上特點(diǎn),就可以來實(shí)現(xiàn)前端路由“更新視圖但不重新請求頁面”的功能了
?
特點(diǎn):兼容性好但是不美觀
?
「history 模式」
利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法秫逝。
這兩個方法應(yīng)用于瀏覽器的歷史記錄站恕出,在當(dāng)前已有的 back、forward筷登、go 的基礎(chǔ)之上剃根,它們提供了對歷史記錄進(jìn)行修改的功能。這兩個方法有個共同的特點(diǎn):當(dāng)調(diào)用他們修改瀏覽器歷史記錄棧后前方,雖然當(dāng)前 URL 改變了狈醉,但瀏覽器不會刷新頁面,這就為單頁應(yīng)用前端路由“更新視圖但不重新請求頁面”提供了基礎(chǔ)惠险。
?
特點(diǎn):雖然美觀拟枚,但是刷新會出現(xiàn) 404 需要后端進(jìn)行配置
?
38 diff 算法了解嗎
diff算法.png
建議直接看 diff 算法詳解傳送門[14]
Reference
[1]
傳送門:https://juejin.cn/post/6956407362085191717
[2]
傳送門:https://juejin.cn/post/6954925963226382367
[3]
傳送門:https://juejin.cn/post/6935344605424517128
[4]
傳送門:https://juejin.cn/post/6935344605424517128#heading-4
[5]
傳送門:https://juejin.cn/post/6940454764421316644
[6]
傳送門:https://juejin.cn/post/6953433215218483236
[7]
傳送門:https://juejin.cn/post/6844904153437700103#heading-2
[8]
傳送門:https://juejin.cn/post/6951671158198501383
[9]
傳送門:https://juejin.cn/post/6939704519668432910#heading-4
[10]
傳送門:https://juejin.cn/post/6935344605424517128
[11]
傳送門:https://juejin.cn/post/6954173708344770591
[12]
傳送門:https://juejin.cn/post/6936024530016010276
[13]
傳送門:https://juejin.cn/post/6951671158198501383#heading-4
[14]
傳送門:https://juejin.cn/post/6953433215218483236