1锈玉、說(shuō)說(shuō)你對(duì) SPA 單頁(yè)面的理解爪飘,它的優(yōu)缺點(diǎn)分別是什么?
SPA( single-page application )僅在 Web 頁(yè)面初始化時(shí)加載相應(yīng)的 HTML拉背、JavaScript 和 CSS师崎。一旦頁(yè)面加載完成,SPA 不會(huì)因?yàn)橛脩?hù)的操作而進(jìn)行頁(yè)面的重新加載或跳轉(zhuǎn)椅棺;取而代之的是利用路由機(jī)制實(shí)現(xiàn) HTML 內(nèi)容的變換犁罩,UI 與用戶(hù)的交互,避免頁(yè)面的重新加載两疚。
優(yōu)點(diǎn):
- 用戶(hù)體驗(yàn)好床估、快,內(nèi)容的改變不需要重新加載整個(gè)頁(yè)面诱渤,避免了不必要的跳轉(zhuǎn)和重復(fù)渲染丐巫;
- 基于上面一點(diǎn),SPA 相對(duì)對(duì)服務(wù)器壓力猩酌馈递胧;
- 前后端職責(zé)分離,架構(gòu)清晰赡茸,前端進(jìn)行交互邏輯缎脾,后端負(fù)責(zé)數(shù)據(jù)處理;
缺點(diǎn):
- 初次加載耗時(shí)多:為實(shí)現(xiàn)單頁(yè) Web 應(yīng)用功能及顯示效果占卧,需要在加載頁(yè)面的時(shí)候?qū)?JavaScript遗菠、CSS 統(tǒng)一加載,部分頁(yè)面按需加載华蜒;
- 前進(jìn)后退路由管理:由于單頁(yè)應(yīng)用在一個(gè)頁(yè)面中顯示所有的內(nèi)容舷蒲,所以不能使用瀏覽器的前進(jìn)后退功能,所有的頁(yè)面切換需要自己建立堆棧管理友多;
- SEO 難度較大:由于所有的內(nèi)容都在一個(gè)頁(yè)面中動(dòng)態(tài)替換顯示牲平,所以在 SEO 上其有著天然的弱勢(shì)。
2域滥、v-show 與 v-if 有什么區(qū)別纵柿?
v-if 是真正的條件渲染蜈抓,因?yàn)樗鼤?huì)確保在切換過(guò)程中條件塊內(nèi)的事件監(jiān)聽(tīng)器和子組件適當(dāng)?shù)乇讳N(xiāo)毀和重建;也是惰性的:如果在初始渲染時(shí)條件為假昂儒,則什么也不做——直到條件第一次變?yōu)檎鏁r(shí)沟使,才會(huì)開(kāi)始渲染條件塊。
v-show 就簡(jiǎn)單得多——不管初始條件是什么渊跋,元素總是會(huì)被渲染腊嗡,并且只是簡(jiǎn)單地基于 CSS 的 “display” 屬性進(jìn)行切換。
所以拾酝,v-if 適用于在運(yùn)行時(shí)很少改變條件燕少,不需要頻繁切換條件的場(chǎng)景;v-show 則適用于需要非常頻繁切換條件的場(chǎng)景蒿囤。
3客们、Class 與 Style 如何動(dòng)態(tài)綁定?
Class 可以通過(guò)對(duì)象語(yǔ)法和數(shù)組語(yǔ)法進(jìn)行動(dòng)態(tài)綁定:
- 對(duì)象語(yǔ)法:
<div v-bind:class="{ active: isActive, 'text-danger': hasError }"></div>
data: {
isActive: true,
hasError: false
}
- 數(shù)組語(yǔ)法:
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
Style 也可以通過(guò)對(duì)象語(yǔ)法和數(shù)組語(yǔ)法進(jìn)行動(dòng)態(tài)綁定:
- 對(duì)象語(yǔ)法:
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
- 數(shù)組語(yǔ)法:
<div v-bind:style="[styleColor, styleSize]"></div>
data: {
styleColor: {
color: 'red'
},
styleSize:{
fontSize:'23px'
}
}
4材诽、怎樣理解 Vue 的單向數(shù)據(jù)流底挫?
所有的 prop 都使得其父子 prop 之間形成了一個(gè)單向下行綁定:父級(jí) prop 的更新會(huì)向下流動(dòng)到子組件中,但是反過(guò)來(lái)則不行脸侥。這樣會(huì)防止從子組件意外改變父級(jí)組件的狀態(tài)建邓,從而導(dǎo)致你的應(yīng)用的數(shù)據(jù)流向難以理解。
額外的睁枕,每次父級(jí)組件發(fā)生更新時(shí)官边,子組件中所有的 prop 都將會(huì)刷新為最新的值。這意味著你不應(yīng)該在一個(gè)子組件內(nèi)部改變 prop譬重。如果你這樣做了,Vue 會(huì)在瀏覽器的控制臺(tái)中發(fā)出警告罐氨。子組件想修改時(shí)臀规,只能通過(guò) $emit 派發(fā)一個(gè)自定義事件,父組件接收到后栅隐,由父組件修改塔嬉。
有兩種常見(jiàn)的試圖改變一個(gè) prop 的情形 :
- 這個(gè) prop 用來(lái)傳遞一個(gè)初始值;這個(gè)子組件接下來(lái)希望將其作為一個(gè)本地的 prop 數(shù)據(jù)來(lái)使用租悄。 在這種情況下谨究,最好定義一個(gè)本地的 data 屬性并將這個(gè) prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
- 這個(gè) prop 以一種原始的值傳入且需要進(jìn)行轉(zhuǎn)換。 在這種情況下泣棋,最好使用這個(gè) prop 的值來(lái)定義一個(gè)計(jì)算屬性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
5胶哲、computed 和 watch 的區(qū)別和運(yùn)用的場(chǎng)景?
computed: 是計(jì)算屬性潭辈,依賴(lài)其它屬性值鸯屿,并且 computed 的值有緩存澈吨,只有它依賴(lài)的屬性值發(fā)生改變,下一次獲取 computed 的值時(shí)才會(huì)重新計(jì)算 computed 的值寄摆;
watch: 更多的是「觀察」的作用谅辣,類(lèi)似于某些數(shù)據(jù)的監(jiān)聽(tīng)回調(diào) ,每當(dāng)監(jiān)聽(tīng)的數(shù)據(jù)變化時(shí)都會(huì)執(zhí)行回調(diào)進(jìn)行后續(xù)操作婶恼;
運(yùn)用場(chǎng)景:
當(dāng)我們需要進(jìn)行數(shù)值計(jì)算桑阶,并且依賴(lài)于其它數(shù)據(jù)時(shí),應(yīng)該使用 computed勾邦,因?yàn)榭梢岳?computed 的緩存特性蚣录,避免每次獲取值時(shí),都要重新計(jì)算检痰;
當(dāng)我們需要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷(xiāo)較大的操作時(shí)包归,應(yīng)該使用 watch,使用 watch 選項(xiàng)允許我們執(zhí)行異步操作 ( 訪問(wèn)一個(gè) API )铅歼,限制我們執(zhí)行該操作的頻率公壤,并在我們得到最終結(jié)果前,設(shè)置中間狀態(tài)椎椰。這些都是計(jì)算屬性無(wú)法做到的厦幅。
6、直接給一個(gè)數(shù)組項(xiàng)賦值慨飘,Vue 能檢測(cè)到變化嗎确憨?
由于 JavaScript 的限制,Vue 不能檢測(cè)到以下數(shù)組的變動(dòng):
- 當(dāng)你利用索引直接設(shè)置一個(gè)數(shù)組項(xiàng)時(shí)瓤的,例如:
vm.items[indexOfItem] = newValue
- 當(dāng)你修改數(shù)組的長(zhǎng)度時(shí)休弃,例如:
vm.items.length = newLength
為了解決第一個(gè)問(wèn)題,Vue 提供了以下操作方法:
// Vue.set
Vue.set(vm.items, indexOfItem, newValue)
// vm.$set圈膏,Vue.set的一個(gè)別名
vm.$set(vm.items, indexOfItem, newValue)
// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)
為了解決第二個(gè)問(wèn)題塔猾,Vue 提供了以下操作方法:
// Array.prototype.splice
vm.items.splice(newLength)
7、談?wù)勀銓?duì) Vue 生命周期的理解稽坤?
(1)生命周期是什么丈甸?
Vue 實(shí)例有一個(gè)完整的生命周期,也就是從開(kāi)始創(chuàng)建尿褪、初始化數(shù)據(jù)睦擂、編譯模版、掛載 Dom -> 渲染杖玲、更新 -> 渲染顿仇、卸載等一系列過(guò)程,我們稱(chēng)這是 Vue 的生命周期。
(2)各個(gè)生命周期的作用
生命周期 | 描述 |
---|---|
beforeCreate | 組件實(shí)例被創(chuàng)建之初夺欲,組件的屬性生效之前 |
created | 組件實(shí)例已經(jīng)完全創(chuàng)建跪帝,屬性也綁定,但真實(shí) dom 還沒(méi)有生成些阅,$el 還不可用 |
beforeMount | 在掛載開(kāi)始之前被調(diào)用:相關(guān)的 render 函數(shù)首次被調(diào)用 |
mounted | el 被新創(chuàng)建的 vm.$el 替換伞剑,并掛載到實(shí)例上去之后調(diào)用該鉤子 |
beforeUpdate | 組件數(shù)據(jù)更新之前調(diào)用,發(fā)生在虛擬 DOM 打補(bǔ)丁之前 |
update | 組件數(shù)據(jù)更新之后 |
activited | keep-alive 專(zhuān)屬市埋,組件被激活時(shí)調(diào)用 |
deadctivated | keep-alive 專(zhuān)屬黎泣,組件被銷(xiāo)毀時(shí)調(diào)用 |
beforeDestory | 組件銷(xiāo)毀前調(diào)用 |
destoryed | 組件銷(xiāo)毀后調(diào)用 |
(3)生命周期示意圖
8、Vue 的父組件和子組件生命周期鉤子函數(shù)執(zhí)行順序缤谎?
Vue 的父組件和子組件生命周期鉤子函數(shù)執(zhí)行順序可以歸類(lèi)為以下 4 部分:
-
加載渲染過(guò)程
父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子 created -> 子 beforeMount -> 子 mounted -> 父 mounted
-
子組件更新過(guò)程
父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated
-
父組件更新過(guò)程
父 beforeUpdate -> 父 updated
-
銷(xiāo)毀過(guò)程
父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed
9抒倚、在哪個(gè)生命周期內(nèi)調(diào)用異步請(qǐng)求?
可以在鉤子函數(shù) created坷澡、beforeMount托呕、mounted 中進(jìn)行調(diào)用,因?yàn)樵谶@三個(gè)鉤子函數(shù)中频敛,data 已經(jīng)創(chuàng)建项郊,可以將服務(wù)端端返回的數(shù)據(jù)進(jìn)行賦值。但是本人推薦在 created 鉤子函數(shù)中調(diào)用異步請(qǐng)求斟赚,因?yàn)樵?created 鉤子函數(shù)中調(diào)用異步請(qǐng)求有以下優(yōu)點(diǎn):
- 能更快獲取到服務(wù)端數(shù)據(jù)着降,減少頁(yè)面 loading 時(shí)間;
- ssr 不支持 beforeMount 拗军、mounted 鉤子函數(shù)任洞,所以放在 created 中有助于一致性;
10发侵、在什么階段才能訪問(wèn)操作DOM交掏?
在鉤子函數(shù) mounted 被調(diào)用前,Vue 已經(jīng)將編譯好的模板掛載到頁(yè)面上刃鳄,所以在 mounted 中可以訪問(wèn)操作 DOM盅弛。vue 具體的生命周期示意圖可以參見(jiàn)如下,理解了整個(gè)生命周期各個(gè)階段的操作铲汪,關(guān)于生命周期相關(guān)的面試題就難不倒你了熊尉。
11罐柳、父組件可以監(jiān)聽(tīng)到子組件的生命周期嗎掌腰?
比如有父組件 Parent 和子組件 Child,如果父組件監(jiān)聽(tīng)到子組件掛載 mounted 就做一些邏輯處理张吉,可以通過(guò)以下寫(xiě)法實(shí)現(xiàn):
// Parent.vue
<Child @mounted="doSomething"/>
// Child.vue
mounted() {
this.$emit("mounted");
}
以上需要手動(dòng)通過(guò) $emit 觸發(fā)父組件的事件齿梁,更簡(jiǎn)單的方式可以在父組件引用子組件時(shí)通過(guò) @hook 來(lái)監(jiān)聽(tīng)即可,如下所示:
// Parent.vue
<Child @hook:mounted="doSomething" ></Child>
doSomething() {
console.log('父組件監(jiān)聽(tīng)到 mounted 鉤子函數(shù) ...');
},
// Child.vue
mounted(){
console.log('子組件觸發(fā) mounted 鉤子函數(shù) ...');
},
// 以上輸出順序?yàn)椋?// 子組件觸發(fā) mounted 鉤子函數(shù) ...
// 父組件監(jiān)聽(tīng)到 mounted 鉤子函數(shù) ...
當(dāng)然 @hook 方法不僅僅是可以監(jiān)聽(tīng) mounted,其它的生命周期事件勺择,例如:created创南,updated 等都可以監(jiān)聽(tīng)。
12省核、談?wù)勀銓?duì) keep-alive 的了解稿辙?
keep-alive 是 Vue 內(nèi)置的一個(gè)組件,可以使被包含的組件保留狀態(tài)气忠,避免重新渲染 邻储,其有以下特性:
- 一般結(jié)合路由和動(dòng)態(tài)組件一起使用,用于緩存組件旧噪;
- 提供 include 和 exclude 屬性吨娜,兩者都支持字符串或正則表達(dá)式, include 表示只有名稱(chēng)匹配的組件會(huì)被緩存淘钟,exclude 表示任何名稱(chēng)匹配的組件都不會(huì)被緩存 宦赠,其中 exclude 的優(yōu)先級(jí)比 include 高;
- 對(duì)應(yīng)兩個(gè)鉤子函數(shù) activated 和 deactivated 米母,當(dāng)組件被激活時(shí)勾扭,觸發(fā)鉤子函數(shù) activated,當(dāng)組件被移除時(shí)爱咬,觸發(fā)鉤子函數(shù) deactivated尺借。
13、組件中 data 為什么是一個(gè)函數(shù)精拟?
為什么組件中的 data 必須是一個(gè)函數(shù)燎斩,然后 return 一個(gè)對(duì)象,而 new Vue 實(shí)例里蜂绎,data 可以直接是一個(gè)對(duì)象栅表?
// data
data() {
return {
message: "子組件",
childName:this.name
}
}
// new Vue
new Vue({
el: '#app',
router,
template: '<App/>',
components: {App}
})
因?yàn)榻M件是用來(lái)復(fù)用的,且 JS 里對(duì)象是引用關(guān)系师枣,如果組件中 data 是一個(gè)對(duì)象怪瓶,那么這樣作用域沒(méi)有隔離,子組件中的 data 屬性值會(huì)相互影響践美,如果組件中 data 選項(xiàng)是一個(gè)函數(shù)洗贰,那么每個(gè)實(shí)例可以維護(hù)一份被返回對(duì)象的獨(dú)立的拷貝,組件實(shí)例之間的 data 屬性值不會(huì)互相影響陨倡;而 new Vue 的實(shí)例敛滋,是不會(huì)被復(fù)用的,因此不存在引用對(duì)象的問(wèn)題兴革。
14绎晃、v-model 的原理蜜唾?
我們?cè)?vue 項(xiàng)目中主要使用 v-model 指令在表單 input、textarea庶艾、select 等元素上創(chuàng)建雙向數(shù)據(jù)綁定袁余,我們知道 v-model 本質(zhì)上不過(guò)是語(yǔ)法糖,v-model 在內(nèi)部為不同的輸入元素使用不同的屬性并拋出不同的事件:
- text 和 textarea 元素使用 value 屬性和 input 事件咱揍;
- checkbox 和 radio 使用 checked 屬性和 change 事件颖榜;
- select 字段將 value 作為 prop 并將 change 作為事件。
以 input 表單元素為例:
<input v-model='something'>
相當(dāng)于
<input v-bind:value="something" v-on:input="something = $event.target.value">
如果在自定義組件中煤裙,v-model 默認(rèn)會(huì)利用名為 value 的 prop 和名為 input 的事件朱转,如下所示:
父組件:
<ModelChild v-model="message"></ModelChild>
子組件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小紅')
},
},
15、Vue 組件間通信有哪幾種方式积暖?
Vue 組件間通信是面試程傥考的知識(shí)點(diǎn)之一,這題有點(diǎn)類(lèi)似于開(kāi)放題夺刑,你回答出越多方法當(dāng)然越加分缅疟,表明你對(duì) Vue 掌握的越熟練。Vue 組件間通信只要指以下 3 類(lèi)通信:父子組件通信遍愿、隔代組件通信存淫、兄弟組件通信,下面我們分別介紹每種通信方式且會(huì)說(shuō)明此種方法可適用于哪類(lèi)組件間通信沼填。
(1)props / $emit
適用 父子組件通信
這種方法是 Vue 組件的基礎(chǔ)桅咆,相信大部分同學(xué)耳聞能詳,所以此處就不舉例展開(kāi)介紹坞笙。
(2)ref
與 $parent / $children
適用 父子組件通信
-
ref
:如果在普通的 DOM 元素上使用岩饼,引用指向的就是 DOM 元素;如果用在子組件上薛夜,引用就指向組件實(shí)例 -
$parent
/$children
:訪問(wèn)父 / 子實(shí)例
(3)EventBus ($emit / $on)
適用于 父子籍茧、隔代、兄弟組件通信
這種方法通過(guò)一個(gè)空的 Vue 實(shí)例作為中央事件總線(事件中心)梯澜,用它來(lái)觸發(fā)事件和監(jiān)聽(tīng)事件寞冯,從而實(shí)現(xiàn)任何組件間的通信,包括父子晚伙、隔代吮龄、兄弟組件。
(4)$attrs
/$listeners
適用于 隔代組件通信
-
$attrs
:包含了父作用域中不被 prop 所識(shí)別 (且獲取) 的特性綁定 ( class 和 style 除外 )咆疗。當(dāng)一個(gè)組件沒(méi)有聲明任何 prop 時(shí)漓帚,這里會(huì)包含所有父作用域的綁定 ( class 和 style 除外 ),并且可以通過(guò)v-bind="$attrs"
傳入內(nèi)部組件民傻。通常配合 inheritAttrs 選項(xiàng)一起使用胰默。 -
$listeners
:包含了父作用域中的 (不含 .native 修飾器的) v-on 事件監(jiān)聽(tīng)器。它可以通過(guò)v-on="$listeners"
傳入內(nèi)部組件
(5)provide / inject
適用于 隔代組件通信
祖先組件中通過(guò) provider 來(lái)提供變量漓踢,然后在子孫組件中通過(guò) inject 來(lái)注入變量牵署。 provide / inject API 主要解決了跨級(jí)組件間的通信問(wèn)題,不過(guò)它的使用場(chǎng)景喧半,主要是子組件獲取上級(jí)組件的狀態(tài)奴迅,跨級(jí)組件間建立了一種主動(dòng)提供與依賴(lài)注入的關(guān)系。
(6)Vuex 適用于 父子挺据、隔代取具、兄弟組件通信
Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉(cāng)庫(kù))扁耐∠炯欤“store” 基本上就是一個(gè)容器,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )婉称。
- Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的块仆。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候,若 store 中的狀態(tài)發(fā)生變化王暗,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新悔据。
- 改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化俗壹。
16科汗、你使用過(guò) Vuex 嗎?
Vuex 是一個(gè)專(zhuān)為 Vue.js 應(yīng)用程序開(kāi)發(fā)的狀態(tài)管理模式绷雏。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉(cāng)庫(kù))头滔。“store” 基本上就是一個(gè)容器涎显,它包含著你的應(yīng)用中大部分的狀態(tài) ( state )拙毫。
(1)Vuex 的狀態(tài)存儲(chǔ)是響應(yīng)式的。當(dāng) Vue 組件從 store 中讀取狀態(tài)的時(shí)候棺禾,若 store 中的狀態(tài)發(fā)生變化缀蹄,那么相應(yīng)的組件也會(huì)相應(yīng)地得到高效更新。
(2)改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation膘婶。這樣使得我們可以方便地跟蹤每一個(gè)狀態(tài)的變化缺前。
主要包括以下幾個(gè)模塊:
- State:定義了應(yīng)用狀態(tài)的數(shù)據(jù)結(jié)構(gòu),可以在這里設(shè)置默認(rèn)的初始狀態(tài)悬襟。
- Getter:允許組件從 Store 中獲取數(shù)據(jù)衅码,mapGetters 輔助函數(shù)僅僅是將 store 中的 getter 映射到局部計(jì)算屬性。
- Mutation:是唯一更改 store 中狀態(tài)的方法脊岳,且必須是同步函數(shù)逝段。
- Action:用于提交 mutation垛玻,而不是直接變更狀態(tài),可以包含任意異步操作奶躯。
- Module:允許將單一的 Store 拆分為多個(gè) store 且同時(shí)保存在單一的狀態(tài)樹(shù)中帚桩。
17、使用過(guò) Vue SSR 嗎嘹黔?說(shuō)說(shuō) SSR账嚎?
Vue.js 是構(gòu)建客戶(hù)端應(yīng)用程序的框架。默認(rèn)情況下儡蔓,可以在瀏覽器中輸出 Vue 組件郭蕉,進(jìn)行生成 DOM 和操作 DOM。然而喂江,也可以將同一個(gè)組件渲染為服務(wù)端的 HTML 字符串召锈,將它們直接發(fā)送到瀏覽器,最后將這些靜態(tài)標(biāo)記"激活"為客戶(hù)端上完全可交互的應(yīng)用程序获询。
即:SSR大致的意思就是vue在客戶(hù)端將標(biāo)簽渲染成的整個(gè) html 片段的工作在服務(wù)端完成烟勋,服務(wù)端形成的html 片段直接返回給客戶(hù)端這個(gè)過(guò)程就叫做服務(wù)端渲染。
服務(wù)端渲染 SSR 的優(yōu)缺點(diǎn)如下:
(1)服務(wù)端渲染的優(yōu)點(diǎn):
- 更好的 SEO: 因?yàn)?SPA 頁(yè)面的內(nèi)容是通過(guò) Ajax 獲取筐付,而搜索引擎爬取工具并不會(huì)等待 Ajax 異步完成后再抓取頁(yè)面內(nèi)容卵惦,所以在 SPA 中是抓取不到頁(yè)面通過(guò) Ajax 獲取到的內(nèi)容;而 SSR 是直接由服務(wù)端返回已經(jīng)渲染好的頁(yè)面(數(shù)據(jù)已經(jīng)包含在頁(yè)面中)瓦戚,所以搜索引擎爬取工具可以抓取渲染好的頁(yè)面沮尿;
- 更快的內(nèi)容到達(dá)時(shí)間(首屏加載更快): SPA 會(huì)等待所有 Vue 編譯后的 js 文件都下載完成后,才開(kāi)始進(jìn)行頁(yè)面的渲染较解,文件下載等需要一定的時(shí)間等畜疾,所以首屏渲染需要一定的時(shí)間;SSR 直接由服務(wù)端渲染好頁(yè)面直接返回顯示印衔,無(wú)需等待下載 js 文件及再去渲染等啡捶,所以 SSR 有更快的內(nèi)容到達(dá)時(shí)間;
(2) 服務(wù)端渲染的缺點(diǎn):
- 更多的開(kāi)發(fā)條件限制: 例如服務(wù)端渲染只支持 beforCreate 和 created 兩個(gè)鉤子函數(shù)奸焙,這會(huì)導(dǎo)致一些外部擴(kuò)展庫(kù)需要特殊處理瞎暑,才能在服務(wù)端渲染應(yīng)用程序中運(yùn)行;并且與可以部署在任何靜態(tài)文件服務(wù)器上的完全靜態(tài)單頁(yè)面應(yīng)用程序 SPA 不同与帆,服務(wù)端渲染應(yīng)用程序了赌,需要處于 Node.js server 運(yùn)行環(huán)境;
- 更多的服務(wù)器負(fù)載:在 Node.js 中渲染完整的應(yīng)用程序玄糟,顯然會(huì)比僅僅提供靜態(tài)文件的 server 更加大量占用CPU 資源 (CPU-intensive - CPU 密集)勿她,因此如果你預(yù)料在高流量環(huán)境 ( high traffic ) 下使用,請(qǐng)準(zhǔn)備相應(yīng)的服務(wù)器負(fù)載阵翎,并明智地采用緩存策略逢并。
如果沒(méi)有 SSR 開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)之剧,可以參考本文作者的另一篇 SSR 的實(shí)踐文章《Vue SSR 踩坑之旅》,里面 SSR 項(xiàng)目搭建以及附有項(xiàng)目源碼砍聊。
18背稼、vue-router 路由模式有幾種?
vue-router 有 3 種路由模式:hash辩恼、history、abstract谓形,對(duì)應(yīng)的源碼如下所示:
switch (mode) {
case 'history':
this.history = new HTML5History(this, options.base)
break
case 'hash':
this.history = new HashHistory(this, options.base, this.fallback)
break
case 'abstract':
this.history = new AbstractHistory(this, options.base)
break
default:
if (process.env.NODE_ENV !== 'production') {
assert(false, `invalid mode: ${mode}`)
}
}
其中灶伊,3 種路由模式的說(shuō)明如下:
hash: 使用 URL hash 值來(lái)作路由。支持所有瀏覽器寒跳,包括不支持 HTML5 History Api 的瀏覽器聘萨;
history : 依賴(lài) HTML5 History API 和服務(wù)器配置。具體可以查看 HTML5 History 模式童太;
abstract : 支持所有 JavaScript 運(yùn)行環(huán)境米辐,如 Node.js 服務(wù)器端。如果發(fā)現(xiàn)沒(méi)有瀏覽器的 API书释,路由會(huì)自動(dòng)強(qiáng)制進(jìn)入這個(gè)模式.
19翘贮、能說(shuō)下 vue-router 中常用的 hash 和 history 路由模式實(shí)現(xiàn)原理嗎?
(1)hash 模式的實(shí)現(xiàn)原理
早期的前端路由的實(shí)現(xiàn)就是基于 location.hash 來(lái)實(shí)現(xiàn)的爆惧。其實(shí)現(xiàn)原理很簡(jiǎn)單狸页,location.hash 的值就是 URL 中 # 后面的內(nèi)容。比如下面這個(gè)網(wǎng)站扯再,它的 location.hash 的值為 '#search':
https://www.word.com#search
hash 路由模式的實(shí)現(xiàn)主要是基于下面幾個(gè)特性:
- URL 中 hash 值只是客戶(hù)端的一種狀態(tài)芍耘,也就是說(shuō)當(dāng)向服務(wù)器端發(fā)出請(qǐng)求時(shí),hash 部分不會(huì)被發(fā)送熄阻;
- hash 值的改變斋竞,都會(huì)在瀏覽器的訪問(wèn)歷史中增加一個(gè)記錄。因此我們能通過(guò)瀏覽器的回退秃殉、前進(jìn)按鈕控制hash 的切換坝初;
- 可以通過(guò) a 標(biāo)簽,并設(shè)置 href 屬性钾军,當(dāng)用戶(hù)點(diǎn)擊這個(gè)標(biāo)簽后脖卖,URL 的 hash 值會(huì)發(fā)生改變;或者使用 JavaScript 來(lái)對(duì) loaction.hash 進(jìn)行賦值巧颈,改變 URL 的 hash 值畦木;
- 我們可以使用 hashchange 事件來(lái)監(jiān)聽(tīng) hash 值的變化,從而對(duì)頁(yè)面進(jìn)行跳轉(zhuǎn)(渲染)砸泛。
(2)history 模式的實(shí)現(xiàn)原理
HTML5 提供了 History API 來(lái)實(shí)現(xiàn) URL 的變化十籍。其中做最主要的 API 有以下兩個(gè):history.pushState() 和 history.repalceState()蛆封。這兩個(gè) API 可以在不進(jìn)行刷新的情況下,操作瀏覽器的歷史紀(jì)錄勾栗。唯一不同的是惨篱,前者是新增一個(gè)歷史記錄,后者是直接替換當(dāng)前的歷史記錄围俘,如下所示:
window.history.pushState(null, null, path);
window.history.replaceState(null, null, path);
history 路由模式的實(shí)現(xiàn)主要基于存在下面幾個(gè)特性:
- pushState 和 repalceState 兩個(gè) API 來(lái)操作實(shí)現(xiàn) URL 的變化 砸讳;
- 我們可以使用 popstate 事件來(lái)監(jiān)聽(tīng) url 的變化,從而對(duì)頁(yè)面進(jìn)行跳轉(zhuǎn)(渲染)界牡;
- history.pushState() 或 history.replaceState() 不會(huì)觸發(fā) popstate 事件簿寂,這時(shí)我們需要手動(dòng)觸發(fā)頁(yè)面跳轉(zhuǎn)(渲染)。
20宿亡、什么是 MVVM常遂?
Model–View–ViewModel (MVVM) 是一個(gè)軟件架構(gòu)設(shè)計(jì)模式,由微軟 WPF 和 Silverlight 的架構(gòu)師 Ken Cooper 和 Ted Peters 開(kāi)發(fā)挽荠,是一種簡(jiǎn)化用戶(hù)界面的事件驅(qū)動(dòng)編程方式克胳。由 John Gossman(同樣也是 WPF 和 Silverlight 的架構(gòu)師)于2005年在他的博客上發(fā)表
MVVM 源自于經(jīng)典的 Model–View–Controller(MVC)模式 ,MVVM 的出現(xiàn)促進(jìn)了前端開(kāi)發(fā)與后端業(yè)務(wù)邏輯的分離圈匆,極大地提高了前端開(kāi)發(fā)效率漠另,MVVM 的核心是 ViewModel 層,它就像是一個(gè)中轉(zhuǎn)站(value converter)跃赚,負(fù)責(zé)轉(zhuǎn)換 Model 中的數(shù)據(jù)對(duì)象來(lái)讓數(shù)據(jù)變得更容易管理和使用涤姊,該層向上與視圖層進(jìn)行雙向數(shù)據(jù)綁定偎行,向下與 Model 層通過(guò)接口請(qǐng)求進(jìn)行數(shù)據(jù)交互旷偿,起呈上啟下作用懂扼。如下圖所示:
(1)View 層
View 是視圖層,也就是用戶(hù)界面嘹锁。前端主要由 HTML 和 CSS 來(lái)構(gòu)建 葫录。
(2)Model 層
Model 是指數(shù)據(jù)模型,泛指后端進(jìn)行的各種業(yè)務(wù)邏輯處理和數(shù)據(jù)操控领猾,對(duì)于前端來(lái)說(shuō)就是后端提供的 api 接口米同。
(3)ViewModel 層
ViewModel 是由前端開(kāi)發(fā)人員組織生成和維護(hù)的視圖數(shù)據(jù)層。在這一層摔竿,前端開(kāi)發(fā)者對(duì)從后端獲取的 Model 數(shù)據(jù)進(jìn)行轉(zhuǎn)換處理面粮,做二次封裝,以生成符合 View 層使用預(yù)期的視圖數(shù)據(jù)模型继低。需要注意的是 ViewModel 所封裝出來(lái)的數(shù)據(jù)模型包括視圖的狀態(tài)和行為兩部分熬苍,而 Model 層的數(shù)據(jù)模型是只包含狀態(tài)的,比如頁(yè)面的這一塊展示什么,而頁(yè)面加載進(jìn)來(lái)時(shí)發(fā)生什么柴底,點(diǎn)擊這一塊發(fā)生什么婿脸,這一塊滾動(dòng)時(shí)發(fā)生什么這些都屬于視圖行為(交互),視圖狀態(tài)和行為都封裝在了 ViewModel 里柄驻。這樣的封裝使得 ViewModel 可以完整地去描述 View 層狐树。
MVVM 框架實(shí)現(xiàn)了雙向綁定,這樣 ViewModel 的內(nèi)容會(huì)實(shí)時(shí)展現(xiàn)在 View 層鸿脓,前端開(kāi)發(fā)者再也不必低效又麻煩地通過(guò)操縱 DOM 去更新視圖抑钟,MVVM 框架已經(jīng)把最臟最累的一塊做好了,我們開(kāi)發(fā)者只需要處理和維護(hù) ViewModel野哭,更新數(shù)據(jù)視圖就會(huì)自動(dòng)得到相應(yīng)更新在塔。這樣 View 層展現(xiàn)的不是 Model 層的數(shù)據(jù),而是 ViewModel 的數(shù)據(jù)虐拓,由 ViewModel 負(fù)責(zé)與 Model 層交互心俗,這就完全解耦了 View 層和 Model 層傲武,這個(gè)解耦是至關(guān)重要的蓉驹,它是前后端分離方案實(shí)施的重要一環(huán)。
我們以下通過(guò)一個(gè) Vue 實(shí)例來(lái)說(shuō)明 MVVM 的具體實(shí)現(xiàn)揪利,有 Vue 開(kāi)發(fā)經(jīng)驗(yàn)的同學(xué)應(yīng)該一目了然:
(1)View 層
<div id="app">
<p>{{message}}</p>
<button v-on:click="showMessage()">Click me</button>
</div>
(2)ViewModel 層
var app = new Vue({
el: '#app',
data: { // 用于描述視圖狀態(tài)
message: 'Hello Vue!',
},
methods: { // 用于描述視圖行為
showMessage(){
let vm = this;
alert(vm.message);
}
},
created(){
let vm = this;
// Ajax 獲取 Model 層的數(shù)據(jù)
ajax({
url: '/your/server/data/api',
success(res){
vm.message = res;
}
});
}
})
(3) Model 層
{
"url": "/your/server/data/api",
"res": {
"success": true,
"name": "IoveC",
"domain": "www.cnblogs.com"
}
}
21态兴、Vue 是如何實(shí)現(xiàn)數(shù)據(jù)雙向綁定的?
Vue 數(shù)據(jù)雙向綁定主要是指:數(shù)據(jù)變化更新視圖疟位,視圖變化更新數(shù)據(jù)瞻润,如下圖所示:
即:
- 輸入框內(nèi)容變化時(shí),Data 中的數(shù)據(jù)同步變化甜刻。即 View => Data 的變化绍撞。
- Data 中的數(shù)據(jù)變化時(shí),文本節(jié)點(diǎn)的內(nèi)容同步變化得院。即 Data => View 的變化傻铣。
其中,View 變化更新 Data 祥绞,可以通過(guò)事件監(jiān)聽(tīng)的方式來(lái)實(shí)現(xiàn)非洲,所以 Vue 的數(shù)據(jù)雙向綁定的工作主要是如何根據(jù) Data 變化更新 View。
Vue 主要通過(guò)以下 4 個(gè)步驟來(lái)實(shí)現(xiàn)數(shù)據(jù)雙向綁定的:
實(shí)現(xiàn)一個(gè)監(jiān)聽(tīng)器 Observer:對(duì)數(shù)據(jù)對(duì)象進(jìn)行遍歷蜕径,包括子屬性對(duì)象的屬性两踏,利用 Object.defineProperty() 對(duì)屬性都加上 setter 和 getter。這樣的話(huà)兜喻,給這個(gè)對(duì)象的某個(gè)值賦值梦染,就會(huì)觸發(fā) setter,那么就能監(jiān)聽(tīng)到了數(shù)據(jù)變化朴皆。
實(shí)現(xiàn)一個(gè)解析器 Compile:解析 Vue 模板指令弓坞,將模板中的變量都替換成數(shù)據(jù)隧甚,然后初始化渲染頁(yè)面視圖,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù)渡冻,添加監(jiān)聽(tīng)數(shù)據(jù)的訂閱者戚扳,一旦數(shù)據(jù)有變動(dòng),收到通知族吻,調(diào)用更新函數(shù)進(jìn)行數(shù)據(jù)更新帽借。
實(shí)現(xiàn)一個(gè)訂閱者 Watcher:Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁 ,主要的任務(wù)是訂閱 Observer 中的屬性值變化的消息超歌,當(dāng)收到屬性值變化的消息時(shí)砍艾,觸發(fā)解析器 Compile 中對(duì)應(yīng)的更新函數(shù)。
實(shí)現(xiàn)一個(gè)訂閱器 Dep:訂閱器采用 發(fā)布-訂閱 設(shè)計(jì)模式巍举,用來(lái)收集訂閱者 Watcher脆荷,對(duì)監(jiān)聽(tīng)器 Observer 和 訂閱者 Watcher 進(jìn)行統(tǒng)一管理。
以上四個(gè)步驟的流程圖表示如下懊悯,如果有同學(xué)理解不大清晰的蜓谋,可以查看作者專(zhuān)門(mén)介紹數(shù)據(jù)雙向綁定的文章《0 到 1 掌握:Vue 核心之?dāng)?shù)據(jù)雙向綁定》,有進(jìn)行詳細(xì)的講解炭分、以及代碼 demo 示例桃焕。
22、Vue 框架怎么實(shí)現(xiàn)對(duì)象和數(shù)組的監(jiān)聽(tīng)捧毛?
如果被問(wèn)到 Vue 怎么實(shí)現(xiàn)數(shù)據(jù)雙向綁定观堂,大家肯定都會(huì)回答 通過(guò) Object.defineProperty() 對(duì)數(shù)據(jù)進(jìn)行劫持,但是 Object.defineProperty() 只能對(duì)屬性進(jìn)行數(shù)據(jù)劫持呀忧,不能對(duì)整個(gè)對(duì)象進(jìn)行劫持师痕,同理無(wú)法對(duì)數(shù)組進(jìn)行劫持,但是我們?cè)谑褂?Vue 框架中都知道而账,Vue 能檢測(cè)到對(duì)象和數(shù)組(部分方法的操作)的變化胰坟,那它是怎么實(shí)現(xiàn)的呢?我們查看相關(guān)代碼如下:
/**
* Observe a list of Array items.
*/
observeArray (items: Array<any>) {
for (let i = 0, l = items.length; i < l; i++) {
observe(items[i]) // observe 功能為監(jiān)測(cè)數(shù)據(jù)的變化
}
}
/**
* 對(duì)屬性進(jìn)行遞歸遍歷
*/
let childOb = !shallow && observe(val) // observe 功能為監(jiān)測(cè)數(shù)據(jù)的變化
通過(guò)以上 Vue 源碼部分查看福扬,我們就能知道 Vue 框架是通過(guò)遍歷數(shù)組 和遞歸遍歷對(duì)象腕铸,從而達(dá)到利用 Object.defineProperty() 也能對(duì)對(duì)象和數(shù)組(部分方法的操作)進(jìn)行監(jiān)聽(tīng)。
23铛碑、Proxy 與 Object.defineProperty 優(yōu)劣對(duì)比
Proxy 的優(yōu)勢(shì)如下:
- Proxy 可以直接監(jiān)聽(tīng)對(duì)象而非屬性狠裹;
- Proxy 可以直接監(jiān)聽(tīng)數(shù)組的變化;
- Proxy 有多達(dá) 13 種攔截方法,不限于 apply汽烦、ownKeys涛菠、deleteProperty、has 等等是 Object.defineProperty 不具備的;
- Proxy 返回的是一個(gè)新對(duì)象,我們可以只操作新的對(duì)象達(dá)到目的,而 Object.defineProperty 只能遍歷對(duì)象屬性直接修改俗冻;
- Proxy 作為新標(biāo)準(zhǔn)將受到瀏覽器廠商重點(diǎn)持續(xù)的性能優(yōu)化礁叔,也就是傳說(shuō)中的新標(biāo)準(zhǔn)的性能紅利;
Object.defineProperty 的優(yōu)勢(shì)如下:
- 兼容性好迄薄,支持 IE9琅关,而 Proxy 的存在瀏覽器兼容性問(wèn)題,而且無(wú)法用 polyfill 磨平,因此 Vue 的作者才聲明需要等到下個(gè)大版本( 3.0 )才能用 Proxy 重寫(xiě)讥蔽。
24涣易、Vue 怎么用 vm.$set() 解決對(duì)象新增屬性不能響應(yīng)的問(wèn)題 ?
受現(xiàn)代 JavaScript 的限制 冶伞,Vue 無(wú)法檢測(cè)到對(duì)象屬性的添加或刪除新症。由于 Vue 會(huì)在初始化實(shí)例時(shí)對(duì)屬性執(zhí)行 getter/setter 轉(zhuǎn)化,所以屬性必須在 data 對(duì)象上存在才能讓 Vue 將它轉(zhuǎn)換為響應(yīng)式的响禽。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)
來(lái)實(shí)現(xiàn)為對(duì)象添加響應(yīng)式屬性徒爹,那框架本身是如何實(shí)現(xiàn)的呢?
我們查看對(duì)應(yīng)的 Vue 源碼:vue/src/core/instance/index.js
export function set (target: Array<any> | Object, key: any, val: any): any {
// target 為數(shù)組
if (Array.isArray(target) && isValidArrayIndex(key)) {
// 修改數(shù)組的長(zhǎng)度, 避免索引>數(shù)組長(zhǎng)度導(dǎo)致splcie()執(zhí)行有誤
target.length = Math.max(target.length, key)
// 利用數(shù)組的splice變異方法觸發(fā)響應(yīng)式
target.splice(key, 1, val)
return val
}
// key 已經(jīng)存在芋类,直接修改屬性值
if (key in target && !(key in Object.prototype)) {
target[key] = val
return val
}
const ob = (target: any).__ob__
// target 本身就不是響應(yīng)式數(shù)據(jù), 直接賦值
if (!ob) {
target[key] = val
return val
}
// 對(duì)屬性進(jìn)行響應(yīng)式處理
defineReactive(ob.value, key, val)
ob.dep.notify()
return val
}
我們閱讀以上源碼可知隆嗅,vm.$set 的實(shí)現(xiàn)原理是:
如果目標(biāo)是數(shù)組,直接使用數(shù)組的 splice 方法觸發(fā)相應(yīng)式梗肝;
如果目標(biāo)是對(duì)象榛瓮,會(huì)先判讀屬性是否存在铺董、對(duì)象是否是響應(yīng)式巫击,最終如果要對(duì)屬性進(jìn)行響應(yīng)式處理,則是通過(guò)調(diào)用 defineReactive 方法進(jìn)行響應(yīng)式處理( defineReactive 方法就是 Vue 在初始化對(duì)象時(shí)精续,給對(duì)象屬性采用 Object.defineProperty 動(dòng)態(tài)添加 getter 和 setter 的功能所調(diào)用的方法)
25坝锰、虛擬 DOM 的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
- 保證性能下限: 框架的虛擬 DOM 需要適配任何上層 API 可能產(chǎn)生的操作重付,它的一些 DOM 操作的實(shí)現(xiàn)必須是普適的顷级,所以它的性能并不是最優(yōu)的;但是比起粗暴的 DOM 操作性能要好很多确垫,因此框架的虛擬 DOM 至少可以保證在你不需要手動(dòng)優(yōu)化的情況下弓颈,依然可以提供還不錯(cuò)的性能,即保證性能的下限删掀;
- 無(wú)需手動(dòng)操作 DOM: 我們不再需要手動(dòng)去操作 DOM翔冀,只需要寫(xiě)好 View-Model 的代碼邏輯,框架會(huì)根據(jù)虛擬 DOM 和 數(shù)據(jù)雙向綁定披泪,幫我們以可預(yù)期的方式更新視圖纤子,極大提高我們的開(kāi)發(fā)效率;
- 跨平臺(tái): 虛擬 DOM 本質(zhì)上是 JavaScript 對(duì)象,而 DOM 與平臺(tái)強(qiáng)相關(guān),相比之下虛擬 DOM 可以進(jìn)行更方便地跨平臺(tái)操作控硼,例如服務(wù)器渲染泽论、weex 開(kāi)發(fā)等等。
缺點(diǎn):
- 無(wú)法進(jìn)行極致優(yōu)化: 雖然虛擬 DOM + 合理的優(yōu)化卡乾,足以應(yīng)對(duì)絕大部分應(yīng)用的性能需求翼悴,但在一些性能要求極高的應(yīng)用中虛擬 DOM 無(wú)法進(jìn)行針對(duì)性的極致優(yōu)化。
26幔妨、虛擬 DOM 實(shí)現(xiàn)原理抄瓦?
虛擬 DOM 的實(shí)現(xiàn)原理主要包括以下 3 部分:
- 用 JavaScript 對(duì)象模擬真實(shí) DOM 樹(shù),對(duì)真實(shí) DOM 進(jìn)行抽象陶冷;
- diff 算法 — 比較兩棵虛擬 DOM 樹(shù)的差異钙姊;
- pach 算法 — 將兩個(gè)虛擬 DOM 對(duì)象的差異應(yīng)用到真正的 DOM 樹(shù)。
如果對(duì)以上 3 個(gè)部分還不是很了解的同學(xué)埂伦,可以查看本文作者寫(xiě)的另一篇詳解虛擬 DOM 的文章《深入剖析:Vue核心之虛擬DOM》
27煞额、Vue 中的 key 有什么作用?
key 是為 Vue 中 vnode 的唯一標(biāo)記沾谜,通過(guò)這個(gè) key膊毁,我們的 diff 操作可以更準(zhǔn)確、更快速基跑。Vue 的 diff 過(guò)程可以概括為:oldCh 和 newCh 各有兩個(gè)頭尾的變量 oldStartIndex婚温、oldEndIndex 和 newStartIndex、newEndIndex媳否,它們會(huì)新節(jié)點(diǎn)和舊節(jié)點(diǎn)會(huì)進(jìn)行兩兩對(duì)比栅螟,即一共有4種比較方式:newStartIndex 和oldStartIndex 、newEndIndex 和 oldEndIndex 篱竭、newStartIndex 和 oldEndIndex 力图、newEndIndex 和 oldStartIndex,如果以上 4 種比較都沒(méi)匹配掺逼,如果設(shè)置了key吃媒,就會(huì)用 key 再進(jìn)行比較,在比較的過(guò)程中吕喘,遍歷會(huì)往中間靠赘那,一旦 StartIdx > EndIdx 表明 oldCh 和 newCh 至少有一個(gè)已經(jīng)遍歷完了,就會(huì)結(jié)束比較氯质。具體有無(wú) key 的 diff 過(guò)程募舟,可以查看作者寫(xiě)的另一篇詳解虛擬 DOM 的文章《深入剖析:Vue核心之虛擬DOM》
所以 Vue 中 key 的作用是:key 是為 Vue 中 vnode 的唯一標(biāo)記,通過(guò)這個(gè) key病梢,我們的 diff 操作可以更準(zhǔn)確胃珍、更快速
更準(zhǔn)確:因?yàn)閹?key 就不是就地復(fù)用了梁肿,在 sameNode 函數(shù) a.key === b.key
對(duì)比中可以避免就地復(fù)用的情況。所以會(huì)更加準(zhǔn)確觅彰。
更快速:利用 key 的唯一性生成 map 對(duì)象來(lái)獲取對(duì)應(yīng)節(jié)點(diǎn)吩蔑,比遍歷方式更快,源碼如下:
function createKeyToOldIdx (children, beginIdx, endIdx) {
let i, key
const map = {}
for (i = beginIdx; i <= endIdx; ++i) {
key = children[i].key
if (isDef(key)) map[key] = i
}
return map
}
28填抬、你有對(duì) Vue 項(xiàng)目進(jìn)行哪些優(yōu)化烛芬?
如果沒(méi)有對(duì) Vue 項(xiàng)目沒(méi)有進(jìn)行過(guò)優(yōu)化總結(jié)的同學(xué),可以參考本文作者的另一篇文章《 Vue 項(xiàng)目性能優(yōu)化 — 實(shí)踐指南 》飒责,文章主要介紹從 3 個(gè)大方面赘娄,22 個(gè)小方面詳細(xì)講解如何進(jìn)行 Vue 項(xiàng)目的優(yōu)化。
(1)代碼層面的優(yōu)化
- v-if 和 v-show 區(qū)分使用場(chǎng)景
- computed 和 watch 區(qū)分使用場(chǎng)景
- v-for 遍歷必須為 item 添加 key宏蛉,且避免同時(shí)使用 v-if
- 長(zhǎng)列表性能優(yōu)化
- 事件的銷(xiāo)毀
- 圖片資源懶加載
- 路由懶加載
- 第三方插件的按需引入
- 優(yōu)化無(wú)限列表性能
- 服務(wù)端渲染 SSR or 預(yù)渲染
(2)Webpack 層面的優(yōu)化
- Webpack 對(duì)圖片進(jìn)行壓縮
- 減少 ES6 轉(zhuǎn)為 ES5 的冗余代碼
- 提取公共代碼
- 模板預(yù)編譯
- 提取組件的 CSS
- 優(yōu)化 SourceMap
- 構(gòu)建結(jié)果輸出分析
- Vue 項(xiàng)目的編譯優(yōu)化
(3)基礎(chǔ)的 Web 技術(shù)的優(yōu)化
開(kāi)啟 gzip 壓縮
瀏覽器緩存
CDN 的使用
使用 Chrome Performance 查找性能瓶頸
29遣臼、對(duì)于即將到來(lái)的 vue3.0 特性你有什么了解的嗎?
Vue 3.0 正走在發(fā)布的路上拾并,Vue 3.0 的目標(biāo)是讓 Vue 核心變得更小揍堰、更快、更強(qiáng)大嗅义,因此 Vue 3.0 增加以下這些新特性:
(1)監(jiān)測(cè)機(jī)制的改變
3.0 將帶來(lái)基于代理 Proxy 的 observer 實(shí)現(xiàn)屏歹,提供全語(yǔ)言覆蓋的反應(yīng)性跟蹤。這消除了 Vue 2 當(dāng)中基于 Object.defineProperty 的實(shí)現(xiàn)所存在的很多限制:
只能監(jiān)測(cè)屬性之碗,不能監(jiān)測(cè)對(duì)象
檢測(cè)屬性的添加和刪除蝙眶;
檢測(cè)數(shù)組索引和長(zhǎng)度的變更;
支持 Map褪那、Set幽纷、WeakMap 和 WeakSet。
新的 observer 還提供了以下特性:
- 用于創(chuàng)建 observable 的公開(kāi) API武通。這為中小規(guī)模場(chǎng)景提供了簡(jiǎn)單輕量級(jí)的跨組件狀態(tài)管理解決方案霹崎。
- 默認(rèn)采用惰性觀察珊搀。在 2.x 中冶忱,不管反應(yīng)式數(shù)據(jù)有多大,都會(huì)在啟動(dòng)時(shí)被觀察到境析。如果你的數(shù)據(jù)集很大囚枪,這可能會(huì)在應(yīng)用啟動(dòng)時(shí)帶來(lái)明顯的開(kāi)銷(xiāo)。在 3.x 中劳淆,只觀察用于渲染應(yīng)用程序最初可見(jiàn)部分的數(shù)據(jù)链沼。
- 更精確的變更通知。在 2.x 中沛鸵,通過(guò) Vue.set 強(qiáng)制添加新屬性將導(dǎo)致依賴(lài)于該對(duì)象的 watcher 收到變更通知括勺。在 3.x 中缆八,只有依賴(lài)于特定屬性的 watcher 才會(huì)收到通知。
- 不可變的 observable:我們可以創(chuàng)建值的“不可變”版本(即使是嵌套屬性)疾捍,除非系統(tǒng)在內(nèi)部暫時(shí)將其“解禁”奈辰。這個(gè)機(jī)制可用于凍結(jié) prop 傳遞或 Vuex 狀態(tài)樹(shù)以外的變化。
- 更好的調(diào)試功能:我們可以使用新的 renderTracked 和 renderTriggered 鉤子精確地跟蹤組件在什么時(shí)候以及為什么重新渲染乱豆。
(2)模板
模板方面沒(méi)有大的變更奖恰,只改了作用域插槽,2.x 的機(jī)制導(dǎo)致作用域插槽變了宛裕,父組件會(huì)重新渲染瑟啃,而 3.0 把作用域插槽改成了函數(shù)的方式,這樣只會(huì)影響子組件的重新渲染揩尸,提升了渲染的性能蛹屿。
同時(shí),對(duì)于 render 函數(shù)的方面岩榆,vue3.0 也會(huì)進(jìn)行一系列更改來(lái)方便習(xí)慣直接使用 api 來(lái)生成 vdom 蜡峰。
(3)對(duì)象式的組件聲明方式
vue2.x 中的組件是通過(guò)聲明的方式傳入一系列 option,和 TypeScript 的結(jié)合需要通過(guò)一些裝飾器的方式來(lái)做朗恳,雖然能實(shí)現(xiàn)功能湿颅,但是比較麻煩。3.0 修改了組件的聲明方式粥诫,改成了類(lèi)式的寫(xiě)法油航,這樣使得和 TypeScript 的結(jié)合變得很容易。
此外怀浆,vue 的源碼也改用了 TypeScript 來(lái)寫(xiě)谊囚。其實(shí)當(dāng)代碼的功能復(fù)雜之后,必須有一個(gè)靜態(tài)類(lèi)型系統(tǒng)來(lái)做一些輔助管理≈瓷模現(xiàn)在 vue3.0 也全面改用 TypeScript 來(lái)重寫(xiě)了镰踏,更是使得對(duì)外暴露的 api 更容易結(jié)合 TypeScript。靜態(tài)類(lèi)型系統(tǒng)對(duì)于復(fù)雜代碼的維護(hù)確實(shí)很有必要沙合。
(4)其它方面的更改
vue3.0 的改變是全面的奠伪,上面只涉及到主要的 3 個(gè)方面,還有一些其他的更改:
- 支持自定義渲染器首懈,從而使得 weex 可以通過(guò)自定義渲染器的方式來(lái)擴(kuò)展绊率,而不是直接 fork 源碼來(lái)改的方式。
- 支持 Fragment(多個(gè)根節(jié)點(diǎn))和 Protal(在 dom 其他部分渲染組建內(nèi)容)組件究履,針對(duì)一些特殊的場(chǎng)景做了處理滤否。
- 基于 treeshaking 優(yōu)化,提供了更多的內(nèi)置功能最仑。
30藐俺、說(shuō)說(shuō)你使用 Vue 框架踩過(guò)最大的坑是什么炊甲?怎么解決的?
本題為開(kāi)放題目欲芹,歡迎大家在評(píng)論區(qū)暢所欲言蜜葱,分享自己的踩坑、填坑經(jīng)歷耀石,提供前車(chē)之鑒牵囤,避免大伙再次踩坑 ~~~
作者:Vicky丶Amor
鏈接:http://www.reibang.com/p/b1564296a78b
來(lái)源:簡(jiǎn)書(shū)
著作權(quán)歸作者所有。商業(yè)轉(zhuǎn)載請(qǐng)聯(lián)系作者獲得授權(quán)滞伟,非商業(yè)轉(zhuǎn)載請(qǐng)注明出處揭鳞。