【直接收藏】前端 VUE 高階面試題(三)
86.說(shuō)說(shuō)vue生命周期,發(fā)送請(qǐng)求在生命周期的哪個(gè)階段,為什么不可以是beforeMount,mounted中
回答:
?1、vue的生命周期
1)、生命周期是什么? Vue 實(shí)例有一個(gè)完整的生命周期,也就是從開(kāi)始創(chuàng)建、初始化數(shù)據(jù)、編譯模版、掛載 Dom -> 渲染、更新 -> 渲染、卸載等一系列過(guò)程,我們稱這是 Vue 的生命周期。
2)、各個(gè)生命周期階段及其鉤子函數(shù)
vue的生命周期核心經(jīng)歷了四個(gè)階段,在四個(gè)階段的前后分別有兩個(gè)鉤子函數(shù)。
第一階段:數(shù)據(jù)掛載階段:把配置項(xiàng)data中的屬性,賦給了vue對(duì)象本身,并做了數(shù)據(jù)劫持。
該階段前后的兩個(gè)鉤子函數(shù):beforeCreate和created
第二階段:模板渲染階段:把vue對(duì)象的數(shù)據(jù)渲染到模板上。
該階段前后的兩個(gè)鉤子函數(shù):beforeMount和mounted
第三階段:組件更新階段:當(dāng)數(shù)據(jù)發(fā)送變化時(shí),組件會(huì)進(jìn)行重新渲染,所以,準(zhǔn)確的說(shuō)是,組件重新渲染階段。
該階段前后的兩個(gè)鉤子函數(shù):beforeUpdate和updated
第四階段:組件銷毀階段:組件銷毀。
該階段前后的兩個(gè)鉤子函數(shù):beforeDestroy和destroyed
視情況可以補(bǔ)充:
當(dāng)使用keep-alive包裹組件時(shí),會(huì)有組件激活和停用之說(shuō),這兩個(gè)鉤子函數(shù)分別是:activited和deactivated
?2、發(fā)送請(qǐng)求在生命周期的哪個(gè)階段,為什么不可以是beforeMount,mounted中。
(如果組件的初始數(shù)據(jù)來(lái)自后端)發(fā)送請(qǐng)求建議在鉤子函數(shù)created里,這個(gè)鉤子函數(shù)里是最快,也能有助于一致性。
? 為什么?
? 1)、為什么不能放在beforeCreate里?
? 因?yàn)椋?/p>
一般來(lái)說(shuō),數(shù)據(jù)從后端返回回來(lái)后,都會(huì)賦給vue中的屬性(data掛載的),在beforeCreate鉤子函數(shù)里,data的數(shù)據(jù)還沒(méi)有掛載到vue對(duì)象本身上,所以,不能在這個(gè)鉤子函數(shù)里用。而created鉤子函數(shù)里,數(shù)據(jù)已經(jīng)掛載到vue對(duì)象了。
2)、為什么不可以是beforeMount,mounted中
因?yàn)椋?/p>
? 第一,在這兩個(gè)鉤子函數(shù)里,發(fā)送請(qǐng)求有點(diǎn)晚,會(huì)增加頁(yè)面loading的時(shí)間;
? 第二,vue的SSR不支持beforeMount 、mounted 鉤子函數(shù),所以,放在 created 中有助于一致性
87.兄弟和父子組件,在不能使用vuex的情況下有多少種方案,請(qǐng)列舉出來(lái)?
回答:
1、父子之間傳值:
1)、父到子:props,子到父:$emit
2)、$ref、$parent
3)、事件總線(event-bus)
4)、集中管理($root)
2、兄弟之間傳值:
1)、事件總線(event-bus)
2)、集中管理($root)
3)、先 子到父(用事件),再 父到子(用props)
88.v-if和v-for可以同時(shí)使用嗎
回答:
可以使用,但是,在循環(huán)時(shí),通過(guò)v-if只能拿到少部分?jǐn)?shù)據(jù)時(shí),建議不要使用。
原因: v-for比v-if優(yōu)先級(jí)高,如果遍歷的數(shù)組元素個(gè)數(shù)比較多,但是滿足v-if條件比較少的情況下。會(huì)浪費(fèi)性能。而且,每次刷新頁(yè)面時(shí),都會(huì)執(zhí)行這樣性能不高的代碼。
解決:可以在computed里循環(huán)數(shù)組,通過(guò)filter的方式,過(guò)濾出需要的數(shù)據(jù)。v-for直接循環(huán)計(jì)算屬性的結(jié)果(不用v-if)。而computed是有緩存的,所以,在原始數(shù)據(jù)沒(méi)有變化時(shí),不會(huì)多次過(guò)濾數(shù)據(jù),這樣,就提高了效率。
89.vue-cli怎么配置跨域
回答:
使用反向代理,vue-cli3+的項(xiàng)目里,新建(編輯)vue.config.js文件,(增加)配置代碼如下:
90.v-bind是用來(lái)干什么的
回答:
? ? v-bind指令是把標(biāo)簽的屬性處理成動(dòng)態(tài)的。分別可以把屬性名和屬性值處理成vue里的屬性,常間的是屬性值處理成動(dòng)態(tài)的。
格式如下:
1、屬性值動(dòng)態(tài)綁定:v-bind:html屬性="數(shù)據(jù)"
?簡(jiǎn)寫 ? :html屬性="數(shù)據(jù)"` ?
示例:
2、 屬性名動(dòng)態(tài)綁定:v-bind:[屬性名]="數(shù)據(jù)"
此時(shí),屬性值也是動(dòng)態(tài)的
示例:
91.說(shuō)說(shuō)對(duì)插槽的理解
回答:
1、插槽的作用:
? ?插槽是用來(lái)處理組件的內(nèi)容的。插槽決定了組件的內(nèi)容放在組件模板的何處。插槽使用的是vue官方提供的組件<slot>
來(lái)完成的。
2、vue中的插槽分為:
1)、單個(gè)插槽
?在組件中只有一個(gè)插槽時(shí),插槽不用起名字。默認(rèn)的名字是:default
示例:
2)、具名插槽
? 但組件中的插槽多于一個(gè)時(shí),就需要給組件起名字,用名字來(lái)區(qū)分不同的插槽。用官方組件slot的name屬性給插槽起名字
格式:
示例:
92.$nextTick理解 和定時(shí)器有什么區(qū)別 都是延時(shí)執(zhí)行
回答:
1、$nextTick理解:
? ?vue更新DOM時(shí),使用的是異步更新隊(duì)列。目的是提高性能,避免無(wú)效的重復(fù)的DOM更新。即:vue中更新數(shù)據(jù)后,并不會(huì)立即更新DOM,而是把數(shù)據(jù)引起的DOM更新放入到異步更新隊(duì)列里。等待下次事件循環(huán)(tick),并在兩個(gè)tick之間進(jìn)行UI渲染。
? ?按照這個(gè)思路,程序員就不能在更改數(shù)據(jù)后,立即獲取更新后的DOM,也不知道什么時(shí)候DOM能夠更新。基于此,vue提供了$nextTick函數(shù)。程序員只需要把 ?”操作更新后DOM的代碼“ ?放入到$nextTick的回調(diào)函數(shù)里。由$nextTick內(nèi)部,在更新完DOM后,調(diào)用回調(diào)函數(shù)。
示例:
2、$nextTick理解和定時(shí)器有什么區(qū)別
相同:都是延遲加載,都使用事件隊(duì)列
不同:
1)、定時(shí)器是下一個(gè)隊(duì)列的隊(duì)首。
2)、$nextTick()
是放在當(dāng)前隊(duì)列的最后一個(gè)。$nextTick()的回調(diào)函數(shù)執(zhí)行要先于定時(shí)器。
93.event-bus是怎么用?
event-bus是事件總線,是借助一個(gè)全局的vue對(duì)象,來(lái)完成事件的綁定和事件的觸發(fā)。
當(dāng):我們需要把A組件的數(shù)據(jù)傳給B組件時(shí),在A、B兩個(gè)組件里都引入全局的vue對(duì)象。然后,在B組件里綁定事件,在A組件里觸發(fā)事件,就可以把A組件的數(shù)據(jù)傳給B組件了。
示例:
94.mounted與created的區(qū)別
回答:
mounted和created都是vue對(duì)象生命周期的鉤子函數(shù),執(zhí)行時(shí)機(jī)不同。
1、created 是在 data配置項(xiàng)的數(shù)據(jù)掛載到vue對(duì)象本身后,會(huì)調(diào)用的鉤子函數(shù)。
?此時(shí),
1)、用 ?this. ?的方式可以拿到data里的數(shù)據(jù)。
2)、但是數(shù)據(jù)還沒(méi)有渲染到模板上。所以,訪問(wèn)dom時(shí),內(nèi)容還是原始的模板內(nèi)容。
2、mounted是在組件的模板初次渲染完畢后會(huì)調(diào)用的鉤子函數(shù)。
?此時(shí),
? data里的數(shù)據(jù)已經(jīng)渲染到模板上了。所以,訪問(wèn)dom時(shí),已經(jīng)和頁(yè)面上看到的效果一樣了。
95.v-model ? 原理 是什么
回答:
1、v-model指令的作用
?vue中的v-model指令是完成雙向綁定的,用在表單元素上。雙向綁定就是 M會(huì)影響V。V也會(huì)影響M。即:能將頁(yè)面上輸入的值同步更新到相關(guān)綁定的data屬性,也會(huì)在更新data綁定屬性時(shí)候,更新頁(yè)面上輸入控件的值。
2、v-model的原理 ? ? ?
v-model指令是一個(gè)語(yǔ)法糖,是屬性綁定和事件的語(yǔ)法糖。vue會(huì)根據(jù)不同的表單元素使用不同的屬性和事件。
如下:
text 和 textarea 元素使用
value
property 和input
事件;checkbox 和 radio 使用
checked
property 和change
事件;select 字段將
value
作為 prop 并將change
作為事件。
以文本框?yàn)槔饰鲈恚韵率谴a:
而,使用v-model來(lái)完成以上功能的代碼如下:
96.vue 的組件中的 data 配置為什么必須是函數(shù)(每個(gè)組件都是 vue 的實(shí)例)
vue 為了保證每個(gè)實(shí)例上的 data 數(shù)據(jù)的獨(dú)立性,規(guī)定了必須使用函數(shù),而不是對(duì)象。因?yàn)槭褂脤?duì)象的話,每個(gè)實(shí)例(組件)上使用的 data 數(shù)據(jù)是相互影響的,這當(dāng)然就不是我們想要的了。對(duì)象是對(duì)于內(nèi)存地址的引用,直接定義個(gè)對(duì)象的話組件之間都會(huì)使用這個(gè)對(duì)象,這樣會(huì)造成組件之間數(shù)據(jù)相互影響。而函數(shù)具有內(nèi)部作用域,可以解決這個(gè)問(wèn)題。
97.vue 中 computed 和 watch 的區(qū)別
watch 和 computed 都是以 Vue 的依賴追蹤機(jī)制為基礎(chǔ)的,當(dāng)某一個(gè)依賴型數(shù)據(jù)(依賴型數(shù)據(jù):簡(jiǎn)單理解即放在 data 等對(duì)象下的實(shí)例數(shù)據(jù))發(fā)生變化的時(shí)候,所有依賴這個(gè)數(shù)據(jù)的相關(guān)數(shù)據(jù)會(huì)自動(dòng)發(fā)生變化,即自動(dòng)調(diào)用相關(guān)的函數(shù),來(lái)實(shí)現(xiàn)數(shù)據(jù)的變動(dòng)。當(dāng)依賴的值變化時(shí),在 watch 中,是可以做一些復(fù)雜的操作的,而 computed 中的依賴,僅僅是一個(gè)值依賴于另一個(gè)值,是值上的依賴。
應(yīng)用場(chǎng)景:computed:用于處理復(fù)雜的邏輯運(yùn)算;一個(gè)數(shù)據(jù)受一個(gè)或多個(gè)數(shù)據(jù)影響;用來(lái)處理 watch 和 methods 無(wú)法處理的,或處理起來(lái)不方便的情況。例如處理模板中的復(fù)雜表達(dá)式、購(gòu)物車?yán)锩娴纳唐窋?shù)量和總金額之間的變化關(guān)系等。 ? watch:用來(lái)處理當(dāng)一個(gè)屬性發(fā)生變化時(shí),需要執(zhí)行某些具體的業(yè)務(wù)邏輯操作,或要在數(shù)據(jù)變化時(shí)執(zhí)行異步或開(kāi)銷較大的操作;一個(gè)數(shù)據(jù)改變影響多個(gè)數(shù)據(jù)。例如用來(lái)監(jiān)控路由、inpurt 輸入框值的特殊處理等。
區(qū)別:
computed
初始化顯示或者相關(guān)的 data、props 等屬性數(shù)據(jù)發(fā)生變化的時(shí)候調(diào)用;
計(jì)算屬性不在 data 中,它是基于 data 或 props 中的數(shù)據(jù)通過(guò)計(jì)算得到的一個(gè)新值,這個(gè)新值根據(jù)已知值的變化而變化;
在 computed 屬性對(duì)象中定義計(jì)算屬性的方法,和取 data 對(duì)象里的數(shù)據(jù)屬性一樣,以屬性訪問(wèn)的形式調(diào)用;
如果 computed 屬性值是函數(shù),那么默認(rèn)會(huì)走 get 方法,必須要有一個(gè)返回值,函數(shù)的返回值就是屬性的屬性值;
computed 屬性值默認(rèn)會(huì)緩存計(jì)算結(jié)果,在重復(fù)的調(diào)用中,只要依賴數(shù)據(jù)不變,直接取緩存中的計(jì)算結(jié)果,只有依賴型數(shù)據(jù)發(fā)生改變,computed 才會(huì)重新計(jì)算;
在 computed 中的,屬性都有一個(gè) get 和一個(gè) set 方法,當(dāng)數(shù)據(jù)變化時(shí),調(diào)用 set 方法。
watch
主要用來(lái)監(jiān)聽(tīng)某些特定數(shù)據(jù)的變化,從而進(jìn)行某些具體的業(yè)務(wù)邏輯操作,可以看作是 computed 和 methods 的結(jié)合體;
可以監(jiān)聽(tīng)的數(shù)據(jù)來(lái)源:data,props,computed 內(nèi)的數(shù)據(jù);
watch 支持異步;
不支持緩存,監(jiān)聽(tīng)的數(shù)據(jù)改變,直接會(huì)觸發(fā)相應(yīng)的操作;
監(jiān)聽(tīng)函數(shù)有兩個(gè)參數(shù),第一個(gè)參數(shù)是最新的值,第二個(gè)參數(shù)是輸入之前的值,順序一定是新值,舊值。
98.vue 的生命周期?網(wǎng)絡(luò)請(qǐng)求為什么要掛載在 mounted 中?
vue2 的生命周期
初始化階段:
beforeCreate
created
掛載階段
beforeMount
mounted
更新階段
beforeUpdate
updated
卸載階段
beforeDestroy
destroyed
緩存組件相關(guān)
activated
deactivated
處理錯(cuò)誤相關(guān)
errorCaptured
vue3 的生命周期在 vue2 的基礎(chǔ)上新增了:
renderTracked:跟蹤虛擬 DOM 重新渲染時(shí)調(diào)用。鉤子接收 debugger event 作為參數(shù)。此事件告訴你哪個(gè)操作跟蹤了組件以及該操作的目標(biāo)對(duì)象和鍵。
renderTriggered:當(dāng)虛擬 DOM 重新渲染被觸發(fā)時(shí)調(diào)用。和 renderTracked 類似,接收 debugger event 作為參數(shù)。此事件告訴你是什么操作觸發(fā)了重新渲染,以及該操作的目標(biāo)對(duì)象和鍵。一共 13 個(gè)
vue3 的組合 api 的生命周期移除了 beforeCreate 和 created,因?yàn)閯?chuàng)建時(shí)的事件可以在 setup 里面直接調(diào)用。其他的 11 個(gè)生命周期前面全部加上 on比如:mounted -> onMounted, beforeDestroy -> onDeforeDestroy
網(wǎng)絡(luò)請(qǐng)求為什么要掛載在 mounted 中?在 Created 生命周期里 Data 才生成,而請(qǐng)求返回的數(shù)據(jù)需要掛載在 data 上,所以 Created 里是可以初始化請(qǐng)求的,但是 Created 的這時(shí)候 DOM 還沒(méi)有初始化;Mounted 生命周期里 DOM 才初始化渲染完成。然而,請(qǐng)求是異步的,所以不會(huì)堵塞頁(yè)面渲染的主線程。所以請(qǐng)求放在 created 和 mounted 里面都是可行的。如果我們的請(qǐng)求不需要獲取/借助/依賴/改變 DOM,這時(shí)請(qǐng)求可以放在 Created。反之則可以放在 Mounted 里。這樣做會(huì)更加的安全,也能保證頁(yè)面不會(huì)閃爍。
99.vue 的指令,在項(xiàng)目中封裝了那些常用指令?
在 vue 中我們可以使用 Vue.directive()方法注冊(cè)全局指令。也可以只用 directives 選項(xiàng)注冊(cè)局部指令。
輸入框防抖指令 v-debounce
復(fù)制粘貼指令 v-copy
長(zhǎng)按指令 v-longpress
禁止表情及特殊字符 v-emoji
圖片懶加載 v-LazyLoad
權(quán)限校驗(yàn)指令 v-premission
實(shí)現(xiàn)頁(yè)面水印 v-waterMarker
拖拽指令 v-draggable
100.vue 的移動(dòng)端適配怎么做的,rem 怎么用的
vue 的移動(dòng)端適配我們可以參考 vant-ui 組件庫(kù)給我們提供的方案。使用 amfe-flexible(用于自動(dòng)定義跟字體大小)插件和 postcss-pxtorem(用于將 px 自動(dòng)轉(zhuǎn)成 rem)插件在 main.ts 里面 import "amfe-flexible"在根目錄新建 .postcssrc.js 文件
rem 是相對(duì)于跟字體的倍數(shù),如果我們整個(gè)項(xiàng)目都是用 rem 作為單位,那么當(dāng)我們做移動(dòng)端的響應(yīng)式的時(shí)候只需要去改變跟字體的大小就能做到適配。
101.后臺(tái)管理系統(tǒng)用戶驗(yàn)證權(quán)限
登錄用戶填寫完賬號(hào)和密碼后向服務(wù)端驗(yàn)證是否正確,登錄成功后,服務(wù)端會(huì)返回一個(gè) token(該 token 的是一個(gè)能唯一標(biāo)示用戶身份的一個(gè) key),之后我們將 token 存儲(chǔ)在本地 localstorage 之中,這樣下次打開(kāi)頁(yè)面或者刷新頁(yè)面的時(shí)候能記住用戶的登錄狀態(tài),不用再去登錄頁(yè)面重新登錄了。為了保證安全性,后臺(tái)所有 token 有效期(Expires/Max-Age)都是 Session,就是當(dāng)瀏覽器關(guān)閉了就丟失了。重新打開(kāi)瀏覽器都需要重新登錄驗(yàn)證,后端也會(huì)在每周固定一個(gè)時(shí)間點(diǎn)重新刷新 token,讓后臺(tái)用戶全部重新登錄一次,確保后臺(tái)用戶不會(huì)因?yàn)殡娔X遺失或者其它原因被人隨意使用賬號(hào)。
攔截路由進(jìn)行判斷頁(yè)面會(huì)先從 localstorage 中查看是否存有 token,沒(méi)有,就走一遍上一部分的流程重新登錄,如果有 token,就會(huì)把這個(gè) token 返給后端去拉取 user_info,保證用戶信息是最新的。當(dāng)然如果是做了單點(diǎn)登錄得的的話,用戶信息存儲(chǔ)在本地也是可以的。當(dāng)你一臺(tái)電腦登錄時(shí),另一臺(tái)會(huì)被提下線,所以總會(huì)重新登錄獲取最新的內(nèi)容。
權(quán)限控制前端會(huì)有一份路由表,它表示了每一個(gè)路由可訪問(wèn)的權(quán)限。當(dāng)用戶登錄之后,通過(guò) token 獲取用戶的 role ,動(dòng)態(tài)根據(jù)用戶的 role 算出其對(duì)應(yīng)有權(quán)限的路由,再通過(guò) router.addRoutes 動(dòng)態(tài)掛載路由。但這些控制都只是頁(yè)面級(jí)的,說(shuō)白了前端再怎么做權(quán)限控制都不是絕對(duì)安全的,后端的權(quán)限驗(yàn)證是逃不掉的。前端控制頁(yè)面級(jí)的權(quán)限,不同權(quán)限的用戶顯示不同的側(cè)邊欄和限制其所能進(jìn)入的頁(yè)面(也做了少許按鈕級(jí)別的權(quán)限控制),后端則會(huì)驗(yàn)證每一個(gè)涉及請(qǐng)求的操作,驗(yàn)證其是否有該操作的權(quán)限,每一個(gè)后臺(tái)的請(qǐng)求不管是 get 還是 post 都會(huì)讓前端在請(qǐng)求 header 里面攜帶用戶的 token,后端會(huì)根據(jù)該 token 來(lái)驗(yàn)證用戶是否有權(quán)限執(zhí)行該操作。若沒(méi)有權(quán)限則拋出一個(gè)對(duì)應(yīng)的狀態(tài)碼,前端檢測(cè)到該狀態(tài)碼,做出相對(duì)應(yīng)的操作。
利用 vuex 管理路由表,根據(jù) vuex 中可訪問(wèn)的路由渲染側(cè)邊欄組件。
創(chuàng)建 vue 實(shí)例的時(shí)候?qū)?vue-router 掛載,但這個(gè)時(shí)候 vue-router 掛載一些登錄或者不用權(quán)限的公用的頁(yè)面。
當(dāng)用戶登錄后,獲取用 role,將 role 和路由表每個(gè)頁(yè)面的需要的權(quán)限作比較,生成最終用戶可訪問(wèn)的路由表。
調(diào)用 router.addRoutes(store.getters.addRouters)添加用戶可訪問(wèn)的路由。
使用 vuex 管理路由表,根據(jù) vuex 中可訪問(wèn)的路由渲染側(cè)邊欄組件。
102.vuex 做數(shù)據(jù)請(qǐng)求刷新頁(yè)面,數(shù)據(jù)可能會(huì)發(fā)生丟失這個(gè)問(wèn)題怎么解決
因?yàn)?store 里的數(shù)據(jù)是保存在運(yùn)行內(nèi)存中的,當(dāng)頁(yè)面刷新時(shí),頁(yè)面會(huì)重新加載 vue 實(shí)例,store 里面的數(shù)據(jù)就會(huì)被重新賦值初始化。所以我們可以在修改 store 的數(shù)據(jù)同時(shí)將數(shù)據(jù)再存一份到本地存儲(chǔ)(localStorage 或者 sessionStorage),本地存儲(chǔ)的內(nèi)容是存在瀏覽器里面的,不會(huì)因?yàn)樗⑿露鴣G失。我們也可以用過(guò)比如 vuex-persistedstate 這樣的第三方包來(lái)幫助我們做 vuex 的持久化數(shù)據(jù)。
103.vue2 和 vue3 兩者的具體的區(qū)別有哪些,請(qǐng)一一例舉出來(lái)
源碼使用 ts 重寫現(xiàn)如今 typescript 異?;鸨?,它的崛起是有原因的,因?yàn)閷?duì)于規(guī)模很大的項(xiàng)目,沒(méi)有類型聲明,后期維護(hù)和代碼的閱讀都是頭疼的事情,所以廣大碼農(nóng)迫切的需要 vue 能完美支持 ts。vue2 用的是 Facebook 的 Flow 做類型檢查,但是因?yàn)槟承┣闆r下推斷有問(wèn)題,所以 vue3 使用 typescript 進(jìn)行了源碼的重寫。一個(gè)是為了更好的類型檢查,另一個(gè)是擁抱 ts。
使用 proxy 代替 defineProperty我們知道 vue2.x 雙向綁定的核心是 Object.defineProperty(),所以導(dǎo)致 vue 對(duì)數(shù)組對(duì)象的深層監(jiān)聽(tīng)無(wú)法實(shí)現(xiàn)。所以 vue3 使用 proxy 對(duì)雙向綁定進(jìn)行了重寫。Proxy 可以對(duì)整體進(jìn)行監(jiān)聽(tīng),不需要關(guān)心里面有什么屬性,而且 Proxy 的配置項(xiàng)有 13 種,可以做更細(xì)致的事情,這是之前的 defineProperty 無(wú)法達(dá)到的。
Diff 算法的提升vue3 在 vue2 的 diff 算法的基礎(chǔ)上增加了靜態(tài)標(biāo)記,元素提升和事件緩存等優(yōu)化。使得速度更快。
打包體積變化vue2 官方說(shuō)的運(yùn)行時(shí)打包師 23k,但這只是沒(méi)安裝依賴的時(shí)候,隨著依賴包和框架特性的增多,有時(shí)候不必要的,未使用的代碼文件都被打包了進(jìn)去,所以后期項(xiàng)目大了,打包文件會(huì)特別多還很大。在 Vue 3 中,我們通過(guò)將大多數(shù)全局 API 和內(nèi)部幫助程序移動(dòng)到 Javascript 的 module.exports 屬性上實(shí)現(xiàn)這一點(diǎn)。這允許現(xiàn)代模式下的 module bundler 能夠靜態(tài)地分析模塊依賴關(guān)系,并刪除與未使用的 module.exports 屬性相關(guān)的代碼。模板編譯器還生成了對(duì)樹抖動(dòng)友好的代碼,只有在模板中實(shí)際使用某個(gè)特性時(shí),該代碼才導(dǎo)入該特性的幫助程序。盡管增加了許多新特性,但 Vue 3 被壓縮后的基線大小約為 10 KB,不到 Vue 2 的一半。
其他 Api 和功能的改動(dòng)
Global API
模板指令
組件
渲染函數(shù)
vue-cli 從 v4.5.0 開(kāi)始提供 Vue 3 預(yù)設(shè)
Vue Router 4.0 提供了 Vue 3 支持,并有許多突破性的變化
Vuex 4.0 提供了 Vue 3 支持,其 API 與 2.x 基本相同
組件基本結(jié)構(gòu)
在創(chuàng)建組件的時(shí)候我們不必在寫唯一的根元素。移除了 vue2 中的 filters。
生命周期的區(qū)別
新增了 renderTracked 和 renderTriggered 兩個(gè)生命周期。
增加了組合 api
我們可以使用 setup 函數(shù)來(lái)使用類似 react 的自定義 hooks 的功能,主要解決邏輯關(guān)注點(diǎn)分離的問(wèn)題。
104.vue 操作虛擬 DOM 有什么優(yōu)異的地方?他不是還多做了一層虛擬 DOM,為什么比原生操作 DOM 還快
我們有必要先了解下模板轉(zhuǎn)換成視圖的過(guò)程整個(gè)過(guò)程:
Vue.js 通過(guò)編譯將 template 模板轉(zhuǎn)換成渲染函數(shù)(render ) ,執(zhí)行渲染函數(shù)就可以得到一個(gè)虛擬節(jié)點(diǎn)樹。
在對(duì) Model 進(jìn)行操作的時(shí)候,會(huì)觸發(fā)對(duì)應(yīng) Dep 中的 Watcher 對(duì)象。Watcher 對(duì)象會(huì)調(diào)用對(duì)應(yīng)的 update 來(lái)修改視圖。這個(gè)過(guò)程主要是將新舊虛擬節(jié)點(diǎn)進(jìn)行差異對(duì)比,然后根據(jù)對(duì)比結(jié)果進(jìn)行 DOM 操作來(lái)更新視圖。
簡(jiǎn)單點(diǎn)講,在 Vue 的底層實(shí)現(xiàn)上,Vue 將模板編譯成虛擬 DOM 渲染函數(shù)。結(jié)合 Vue 自帶的響應(yīng)系統(tǒng),在狀態(tài)改變時(shí),Vue 能夠智能地計(jì)算出重新渲染組件的最小代價(jià)并應(yīng)到 DOM 操作上。

那么 vue 操作虛擬 DOM 有什么優(yōu)異的地方呢?
具備跨平臺(tái)的優(yōu)勢(shì)由于 Virtual DOM 是以 JavaScript 對(duì)象為基礎(chǔ)而不依賴真實(shí)平臺(tái)環(huán)境,所以使它具有了跨平臺(tái)的能力,比如說(shuō)瀏覽器平臺(tái)、Weex、Node 等。
操作 DOM 慢,js 運(yùn)行效率高。我們可以將 DOM 對(duì)比操作放在 JS 層,提高效率。因?yàn)?DOM 操作的執(zhí)行速度遠(yuǎn)不如 Javascript 的運(yùn)算速度快,因此,把大量的 DOM 操作搬運(yùn)到 Javascript 中,運(yùn)用 patching 算法來(lái)計(jì)算出真正需要更新的節(jié)點(diǎn),最大限度地減少 DOM 操作,從而顯著提高性能。Virtual DOM 本質(zhì)上就是在 JS 和 DOM 之間做了一個(gè)緩存??梢灶惐?CPU 和硬盤,既然硬盤這么慢,我們就在它們之間加個(gè)緩存:既然 DOM 這么慢,我們就在它們 JS 和 DOM 之間加個(gè)緩存。CPU(JS)只操作內(nèi)存(Virtual DOM),最后的時(shí)候再把變更寫入硬盤(DOM)
提升渲染性能Virtual DOM 的優(yōu)勢(shì)不在于單次的操作,而是在大量、頻繁的數(shù)據(jù)更新下,能夠?qū)σ晥D進(jìn)行合理、高效的更新。為了實(shí)現(xiàn)高效的 DOM 操作,一套高效的虛擬 DOM diff 算法顯得很有必要。我們通過(guò) patch 的核心—-diff 算法,找出本次 DOM 需要更新的節(jié)點(diǎn)來(lái)更新,其他的不更新。比如修改某個(gè) model 100 次,從 1 加到 100,那么有了 Virtual DOM 的緩存之后,只會(huì)把最后一次修改 patch 到 view 上。那 diff 算法的實(shí)現(xiàn)過(guò)程是怎樣的?
那么為什么比原生操作 DOM 還快呢?首先我們每次操作 dom 的時(shí)候,都會(huì)去執(zhí)行瀏覽器的那 5 個(gè)步驟,尤其是當(dāng)大量循環(huán)的時(shí)候,每次循環(huán)完都不知道后面還要不要修改,所以每次都要去重復(fù)這個(gè)過(guò)程,引發(fā)不必要的渲染。但是在實(shí)際開(kāi)發(fā)過(guò)程中,我們會(huì)發(fā)現(xiàn)虛擬 dom 并沒(méi)有比真實(shí) dom 更快。這個(gè)問(wèn)題尤雨溪在知乎上面有過(guò)回答:這是一個(gè)性能 vs. 可維護(hù)性的取舍??蚣艿囊饬x在于為你掩蓋底層的 DOM 操作,讓你用更聲明式的方式來(lái)描述你的目的,從而讓你的代碼更容易維護(hù)。沒(méi)有任何框架可以比純手動(dòng)的優(yōu)化 DOM 操作更快,因?yàn)榭蚣艿?DOM 操作層需要應(yīng)對(duì)任何上層 API 可能產(chǎn)生的操作,它的實(shí)現(xiàn)必須是普適的。針對(duì)任何一個(gè) benchmark,我都可以寫出比任何框架更快的手動(dòng)優(yōu)化,但是那有什么意義呢?在構(gòu)建一個(gè)實(shí)際應(yīng)用的時(shí)候,你難道為每一個(gè)地方都去做手動(dòng)優(yōu)化嗎?出于可維護(hù)性的考慮,這顯然不可能??蚣芙o你的保證是,你在不需要手動(dòng)優(yōu)化的情況下,我依然可以給你提供過(guò)得去的性能。
105.token 過(guò)期你是如何來(lái)進(jìn)行處理,有沒(méi)有弄過(guò) token 續(xù)期
在開(kāi)發(fā)中,我們經(jīng)常會(huì)遇到使用 token,token 的作用是要驗(yàn)證用戶是否處于登錄狀態(tài),所以要請(qǐng)求一些只有登錄狀態(tài)才能查看的資源的時(shí)候,我們需要攜帶 token。
一般的后端接口設(shè)置的 token 是有時(shí)效的,超時(shí)后就會(huì)失效,失效之后的處理策略一般會(huì)做兩種處理:
一種是直接跳轉(zhuǎn)到登錄頁(yè)面,重新登錄。
另外一種如果返回 token 失效的信息,自動(dòng)去刷新 token,然后繼續(xù)完成未完成的請(qǐng)求操作。
106.vue底層實(shí)現(xiàn)原理
使用 Object.defineProperty 劫持 data上的數(shù)據(jù)。
Vue2.0通過(guò)設(shè)定對(duì)象屬性的 setter/getter 方法來(lái)監(jiān)聽(tīng)數(shù)據(jù)的變化,通過(guò)getter進(jìn)行依賴收集,而每個(gè)setter方法就是一個(gè)觀察者,在數(shù)據(jù)變更的時(shí)候通知訂閱者更新視圖。
107.Vue的生命周期,created與mounted的區(qū)別
1、created
表示組件實(shí)例已經(jīng)完全創(chuàng)建,data數(shù)據(jù)已經(jīng)被 Object.defineProperty 劫持完成,屬性也綁定成功,但真實(shí)dom還沒(méi)有生成,$el還不可用。
2、mounted
el選項(xiàng)所對(duì)應(yīng)的視圖節(jié)點(diǎn)已經(jīng)被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去了。此時(shí)響應(yīng)式數(shù)據(jù)都已經(jīng)完成了渲染。
108.用vue寫了商城,從列表頁(yè)點(diǎn)進(jìn)詳情頁(yè),從詳情頁(yè)退出來(lái)的時(shí)候,怎么保持進(jìn)入詳情頁(yè)之前的頁(yè)面卷動(dòng)值。
使用 <keep-alive>對(duì)列表頁(yè)面進(jìn)行包裹,被包裹的列表頁(yè)面就有了activated、deactivated這兩個(gè)生命周期。在離開(kāi)列表頁(yè)面時(shí),在deactivated中記錄頁(yè)面的滾動(dòng)條位置。再次進(jìn)入列表頁(yè)面時(shí),在activated中使用 this.$el.scrollTop 把頁(yè)面滾動(dòng)到離開(kāi)時(shí)所記錄的位置。
109.說(shuō)說(shuō)你對(duì)vue的理解
vue是數(shù)據(jù)驅(qū)動(dòng)的MVVM框架,相比傳統(tǒng)的DOM庫(kù),vue有一層虛擬DOM。每當(dāng)數(shù)據(jù)發(fā)生更新時(shí),會(huì)觸發(fā)虛擬DOM進(jìn)行diff運(yùn)算,找出最小的變化節(jié)點(diǎn),大大地節(jié)省了DOM操作性能。
vue是組件化的,在單頁(yè)面應(yīng)用程序中,每一個(gè)組件相當(dāng)于是一塊積木,搭建起龐大的應(yīng)用系統(tǒng)。組件,可以理解成更大粒度的“HTML元素”,有利于快速開(kāi)發(fā)、組件的高效復(fù)用。
vue有一整套指令,大大地降低了開(kāi)發(fā)者的開(kāi)發(fā)難度,提升了開(kāi)發(fā)效率。
雖然vue有諸多優(yōu)點(diǎn),但仍然有一些缺陷,比如復(fù)雜的業(yè)務(wù)頁(yè)面通常過(guò)長(zhǎng),data、methods、computed、watch對(duì)應(yīng)的數(shù)據(jù)和邏輯交織在一起,難以維護(hù)。
110.說(shuō)說(shuō)對(duì)虛擬DOM的理解
在vue中,虛擬DOM本質(zhì)上就是一個(gè)固定格式的JSON數(shù)據(jù),它用于描述真實(shí)的DOM結(jié)構(gòu),并使用各種不同flag標(biāo)記出動(dòng)態(tài)的DOM節(jié)點(diǎn)。
虛擬DOM數(shù)據(jù)保存在應(yīng)用程序?qū)?yīng)的內(nèi)存結(jié)構(gòu)中,擁有更快的數(shù)據(jù)交換速度。
每當(dāng)有數(shù)據(jù)發(fā)生變化時(shí),就會(huì)生成新的虛擬DOM,進(jìn)一步發(fā)生diff運(yùn)算,找出最小臟節(jié)點(diǎn),減少不必要的DOM開(kāi)銷,這也是vue擁有更好的性能的根本原因。
111.說(shuō)說(shuō)provide的用法
在父級(jí)組件中,使用provide選項(xiàng)向vue組件樹中“提供數(shù)據(jù)”,其語(yǔ)法是:provide:{a: 1}
在后代子級(jí)組件中,使用 inject選項(xiàng)從vue組件中“取出數(shù)據(jù)”,其語(yǔ)法是:inject: ['a']
112.說(shuō)一下element ui遇到過(guò)的坑
表單設(shè)置觸發(fā)事件為blur,但是ctrl+A全選以后再刪除時(shí)又觸發(fā)了change事件,并提示一個(gè)原始報(bào)錯(cuò)
解決方案:trigger設(shè)置成
trigger: ['blur', 'change']
使用el-dialog 遮罩層把顯示內(nèi)容遮住了
原因:Dialog 的外層布局的 position 值為 fixed, absolute, relative 三者之一時(shí),就會(huì)出現(xiàn)被蒙板遮住的情況。
解決方法:
v-bind:modal-append-to-body="false"
使用el-select 不能繼承父元素的寬度
原因:el-select 本身是 ?inline-block
解決辦法:手動(dòng)設(shè)置el-select的寬度
113.怎么修改element ui動(dòng)態(tài)組件的樣式
要修改elementUI組件的樣式,可以采用以下兩種方式
1.全局樣式
通過(guò)選擇權(quán)重覆蓋elementUI組件的樣式,如修改復(fù)選框?yàn)閳A角:
但這種方式為全局樣式,會(huì)影響頁(yè)面中所有復(fù)選框,如果不希望影響其它頁(yè)面的樣式,可以采用第二中方式
2.局部樣式
但如果僅僅是設(shè)置了scoped屬性,樣式無(wú)法生效,原因是以上樣式會(huì)被編譯成屬性選擇器,而elementUI組件內(nèi)部的結(jié)構(gòu)卻無(wú)法添加該html屬性,以上樣式被編譯成如下代碼:
解決方案也很簡(jiǎn)單,只需在選擇器中要添加>>>
即可
如果是sass或less編寫的樣式,還可以使用/deep/
以上寫法樣式都會(huì)編譯成以下樣式:
所以elementUI中的樣式就能成功覆蓋
114.vue和react中的key值主要用來(lái)干什么
key是虛擬DOM對(duì)象的標(biāo)識(shí),在更新顯示時(shí)key起著極其重要的作用,vue和react都是采用diff算法來(lái)對(duì)比新舊虛擬節(jié)點(diǎn),而key的作用是為了在執(zhí)行 diff算法 的時(shí)候,更快更準(zhǔn)確地找到對(duì)應(yīng)的虛擬節(jié)點(diǎn),從而提高diff速度。
115.route和router區(qū)別
route
和 router
是vue-router中經(jīng)常會(huì)操作的兩個(gè)對(duì)象, route
表示當(dāng)前的路由信息對(duì)象,包含了當(dāng)前 URL 解析得到的信息,包含當(dāng)前的路徑、參數(shù)、query對(duì)象等,一般用于獲取跳轉(zhuǎn)時(shí)傳入的參數(shù)。router
對(duì)象是全局路由的實(shí)例,是router構(gòu)造方法的實(shí)例,一般用戶路由跳轉(zhuǎn),如router.push()
、router.replace()
等方法
116.vue和react相對(duì)于傳統(tǒng)的有什么好處,性能優(yōu)點(diǎn)
組件化開(kāi)發(fā),開(kāi)發(fā)效率更高
React與Vue都鼓勵(lì)使用組件化開(kāi)發(fā)。這本質(zhì)上是建議你將你的應(yīng)用分拆成一個(gè)個(gè)功能明確的模塊,每個(gè)模塊之間可以通過(guò)特定的方式進(jìn)行關(guān)聯(lián)。這樣可以更好的管理功能模塊與復(fù)用,在團(tuán)隊(duì)項(xiàng)目中也能更好的分工協(xié)作
VirtualDOM,性能更高
對(duì)真實(shí)DOM的操作很慢,所以Vue和React都實(shí)現(xiàn)了虛擬DOM,用戶對(duì)數(shù)據(jù)的修改首先反映到虛擬DOM上,而不是直接操作真實(shí)DOM,然后在虛擬DOM環(huán)節(jié)進(jìn)行優(yōu)化,比如只修改差異項(xiàng),對(duì)虛擬 DOM 進(jìn)行頻繁修改時(shí)進(jìn)行合并,然后一次性修改真實(shí) DOM 中需要改的部分,最后在真實(shí) DOM 中進(jìn)行排版與重繪,減少過(guò)多DOM節(jié)點(diǎn)排版與重繪損耗等
數(shù)據(jù)和結(jié)構(gòu)的分離
雙向數(shù)據(jù)綁定
Vue可以通過(guò)v-model指令實(shí)現(xiàn),react可通過(guò)單向綁定+事件來(lái)實(shí)現(xiàn)
強(qiáng)大的周邊生態(tài)
Vue和React都有著強(qiáng)大的生態(tài),比如路由、狀態(tài)管理、腳手架等,用戶可以根據(jù)需求自行選擇,不需要重復(fù)造輪子
117.虛擬DOM實(shí)現(xiàn)原理
我們先來(lái)看看瀏覽器渲染一個(gè)頁(yè)面的過(guò)和,大概需要以下5個(gè)步驟
解析HTML元素,構(gòu)建DOM樹
解析CSS,生成頁(yè)面CSS規(guī)則樹(Style Rules)
將DOM樹和CSS規(guī)則樹進(jìn)行關(guān)聯(lián),生成render樹
布局(layout/reflow):瀏覽器設(shè)定Render樹中的每個(gè)節(jié)點(diǎn)在屏幕上的位置與尺寸
繪制Render樹:繪制頁(yè)面像素信息到屏幕上
眾所周知,一個(gè)頁(yè)面在瀏覽器中最大的開(kāi)銷就是DOM節(jié)點(diǎn)操作,頁(yè)面的性能問(wèn)題大多數(shù)是DOM操作引起的,當(dāng)我們用原生js 或jquery這樣的庫(kù)去操作DOM時(shí),瀏覽器會(huì)從構(gòu)建DOM樹開(kāi)始執(zhí)行完以上整個(gè)流程,所以頻繁操作DOM會(huì)引起不需要的計(jì)算、重排與重繪,從而導(dǎo)致頁(yè)面卡頓,影響用戶體驗(yàn)
所以減少DOM的操作能達(dá)到性能優(yōu)化的目的,事實(shí)上,虛擬DOM就是這么做的,虛擬DOM(VirtualDOM) 的實(shí)現(xiàn)原理主要包括以下 3 部分:
用 JavaScript 對(duì)象模擬真實(shí) DOM 樹,對(duì)真實(shí) DOM 進(jìn)行抽象
diff 算法 — 比較新舊兩個(gè)虛擬DOM,得到差異對(duì)象
pach 算法 — 將差異對(duì)象應(yīng)用到真實(shí) DOM 樹
Virtual DOM 本質(zhì)上就是一個(gè)javascript對(duì)象,數(shù)據(jù)的修改會(huì)生成一個(gè)新的虛擬DOM(一個(gè)新的javascript對(duì)象),然后與舊的虛擬DOM進(jìn)行對(duì)比,得到兩個(gè)對(duì)象的差異項(xiàng),最后只更新差異對(duì)象中的內(nèi)容到真實(shí)DOM,這樣能做到最少限度地修改真實(shí)DOM,從而實(shí)現(xiàn)性能優(yōu)化
118.如何實(shí)現(xiàn)角色權(quán)限分配
在開(kāi)發(fā)中后臺(tái)應(yīng)用過(guò)程中或多或少都會(huì)涉及到一個(gè)問(wèn)題:權(quán)限,簡(jiǎn)單地說(shuō)就是讓不同的用戶在系統(tǒng)中擁有不同的操作能力。
但在實(shí)際應(yīng)用中我們一般不直接將權(quán)限賦予在用戶身上,因?yàn)檫@樣操作對(duì)有大量用戶的系統(tǒng)來(lái)說(shuō)過(guò)于繁瑣,所以我們一般基于RBAC(Role-Based Access Control)權(quán)限模型,引入角色的概念,通過(guò)角色的媒介過(guò)渡,先將權(quán)限賦予在角色上,再關(guān)聯(lián)相應(yīng)的用戶,對(duì)應(yīng)的用戶就繼承了角色的權(quán)限
用戶與角色,角色與權(quán)限都是多對(duì)多的關(guān)系
引入角色媒介的優(yōu)點(diǎn):
實(shí)現(xiàn)了用戶與權(quán)限的解耦
提高了權(quán)限配置的效率
降低了后期維護(hù)的成本
119.雙向數(shù)據(jù)綁定和單向數(shù)據(jù)流的優(yōu)缺點(diǎn)
所謂數(shù)據(jù)綁定,就是指View
層和Model
層之間的映射關(guān)系。
單向數(shù)據(jù)綁定:
Model
的更新會(huì)觸發(fā)View
的更新,而View
的更新不會(huì)觸發(fā)Model
的更新,它們的作用是單向的。優(yōu)點(diǎn):所有狀態(tài)變化都可以被記錄、跟蹤,狀態(tài)變化通過(guò)手動(dòng)調(diào)用觸發(fā),源頭易追溯。
缺點(diǎn):會(huì)有很多類似的樣板代碼,代碼量會(huì)相應(yīng)的上升。
雙向數(shù)據(jù)綁定:
Model
的更新會(huì)觸發(fā)View
的更新,View
的更新也會(huì)觸發(fā)Model
的更新,它們的作用是相互的。優(yōu)點(diǎn):在操作表單時(shí)使用
v-model
方便簡(jiǎn)單,可以省略繁瑣或重復(fù)的onChange
事件去處理每個(gè)表單數(shù)據(jù)的變化(減少代碼量)。缺點(diǎn):屬于暗箱操作,無(wú)法很好的追蹤雙向綁定的數(shù)據(jù)的變化。
120.Vue是如何實(shí)現(xiàn)雙向綁定的?
Vue的雙向數(shù)據(jù)綁定是通過(guò)數(shù)據(jù)劫持結(jié)合發(fā)布者訂閱者模式來(lái)實(shí)現(xiàn)的 要實(shí)現(xiàn)這種雙向數(shù)據(jù)綁定,必要的條件有:
實(shí)現(xiàn)一個(gè)數(shù)據(jù)監(jiān)聽(tīng)器Observer,能夠?qū)?shù)據(jù)對(duì)象的所有屬性進(jìn)行監(jiān)聽(tīng),如有變動(dòng)可拿到最新值并通知訂閱者
實(shí)現(xiàn)一個(gè)指令解析器Compile,對(duì)每個(gè)元素節(jié)點(diǎn)的指令進(jìn)行掃描和解析,根據(jù)指令模板替換數(shù)據(jù),以及綁定相應(yīng)的更新函數(shù)
實(shí)現(xiàn)一個(gè)Watcher,作為連接Observer和Compile的橋梁,能夠訂閱并收到每個(gè)屬性變動(dòng)的通知,執(zhí)行指令綁定的相應(yīng)回調(diào)函數(shù),從而更新視圖
MVVM入口函數(shù),整合以上三者
補(bǔ)充回答
在new Vue的時(shí)候,在 Observer 中通過(guò) Object.defineProperty()
達(dá)到數(shù)據(jù)劫持,代理所有數(shù)據(jù)的 getter 和 setter 屬性,在每次觸發(fā) setter 的時(shí)候,都會(huì)通過(guò) Dep 來(lái)通知 Watcher,Watcher 作為Observer
數(shù)據(jù)監(jiān)聽(tīng)器與Compile
模板解析器之間的橋梁,當(dāng) Observer 監(jiān)聽(tīng)到數(shù)據(jù)發(fā)生改變的時(shí)候,通過(guò) Updater 來(lái)通知 Compile 更新視圖,而 Compile 通過(guò) Watcher 訂閱對(duì)應(yīng)數(shù)據(jù),綁定更新函數(shù),通過(guò) Dep 來(lái)添加訂閱者,達(dá)到雙向綁定。
121.Proxy與Object.defineProperty的優(yōu)劣對(duì)比?
Proxy的優(yōu)勢(shì)如下
Proxy可以直接監(jiān)聽(tīng)整個(gè)對(duì)象而非屬性。
Proxy可以直接監(jiān)聽(tīng)數(shù)組的變化。
Proxy有13中攔截方法,如
ownKeys、deleteProperty、has
等是Object.defineProperty
不具備的。Proxy返回的是一個(gè)新對(duì)象,我們可以只操作新的對(duì)象達(dá)到目的,而
Object.defineProperty
只能遍歷對(duì)象屬性直接修改;Proxy做為新標(biāo)準(zhǔn)將受到瀏覽器產(chǎn)商重點(diǎn)持續(xù)的性能優(yōu)化,也就是傳說(shuō)中的新標(biāo)準(zhǔn)的性能紅利。
Object.defineProperty 的優(yōu)勢(shì)如下
兼容性好,支持 IE9,而 Proxy 的存在瀏覽器兼容性問(wèn)題,而且無(wú)法用 polyfill 磨平。
Object.defineProperty 不足在于:
Object.defineProperty
只能劫持對(duì)象的屬性,因此我們需要對(duì)每個(gè)對(duì)象的每個(gè)屬性進(jìn)行遍歷。Object.defineProperty
不能監(jiān)聽(tīng)數(shù)組。是通過(guò)重寫數(shù)據(jù)的那7個(gè)可以改變數(shù)據(jù)的方法來(lái)對(duì)數(shù)組進(jìn)行監(jiān)聽(tīng)的。Object.defineProperty
也不能對(duì)es6
新產(chǎn)生的Map
,Set
這些數(shù)據(jù)結(jié)構(gòu)做出監(jiān)聽(tīng)。Object.defineProperty
也不能監(jiān)聽(tīng)新增和刪除操作,通過(guò)Vue.set()
和Vue.delete
來(lái)實(shí)現(xiàn)響應(yīng)式的。
122.你是如何理解Vue的響應(yīng)式系統(tǒng)的?
就是能夠自動(dòng)追蹤數(shù)據(jù)的變化,而不必手動(dòng)觸發(fā)視圖更新。Vue2.X通過(guò)Object.defineProperty()做數(shù)據(jù)劫持,而Vue3通過(guò)Proxy做數(shù)據(jù)代理,從而捕捉到對(duì)數(shù)據(jù)的get和set。
什么叫數(shù)據(jù)響應(yīng)式?
簡(jiǎn)單說(shuō)就是用戶更改數(shù)據(jù)(Data)時(shí),視圖可以自動(dòng)刷新,頁(yè)面UI能夠響應(yīng)數(shù)據(jù)變化。
為什么要了解Vue數(shù)據(jù)響應(yīng)式?
因?yàn)檫@是Vue的立身之本呀,連尤雨溪都在給放文檔上這樣說(shuō),“Vue 最獨(dú)特的特性之一,是其非侵入性的響應(yīng)式系統(tǒng)?!?/p>
Vue就是通過(guò)getter 和setter來(lái)對(duì)數(shù)據(jù)進(jìn)行操作,通過(guò)get()和set()函數(shù)可以生成一個(gè)虛擬屬性,來(lái)直接調(diào)用函數(shù)操作數(shù)據(jù)。這一步是被封裝起來(lái)的,我們是看不到的。
?此外,還需要一個(gè)重要的媒介API,那就是Object.defineProperty,因?yàn)閷?duì)象被定義完成后,想要更改或添加屬性(像是get和set這樣的屬性),只能通過(guò)這個(gè)Object.defineProperty來(lái)添加。接著需要實(shí)現(xiàn)對(duì)數(shù)據(jù)屬性的讀寫進(jìn)行監(jiān)控。能夠監(jiān)控就意味著能讓vm(一般生成的實(shí)例)能夠知道數(shù)據(jù)有變化,從而觸發(fā)render(data)函數(shù),頁(yè)面UI就會(huì)做出響應(yīng)。
123.既然Vue通過(guò)數(shù)據(jù)劫持可以精準(zhǔn)探測(cè)數(shù)據(jù)變化,為什么還需要虛擬DOM進(jìn)行diff檢測(cè)差異?
1.減少對(duì)真實(shí)DOM的操作
Diff 算法探討的就是虛擬 DOM 樹發(fā)生變化后,生成 DOM 樹更新補(bǔ)丁的方式。對(duì)比新舊兩株虛擬 DOM 樹的變更差異,將更新補(bǔ)丁作用于真實(shí) DOM,以最小成本完成視圖更新。
具體流程:
真實(shí) DOM 與虛擬 DOM 之間存在一個(gè)映射關(guān)系。這個(gè)映射關(guān)系依靠初始化時(shí)的 JSX 建立完成;
當(dāng)虛擬 DOM 發(fā)生變化后,就會(huì)根據(jù)差距計(jì)算生成 patch,這個(gè) patch 是一個(gè)結(jié)構(gòu)化的數(shù)據(jù),內(nèi)容包含了增加、更新、移除等;
最后再根據(jù) patch 去更新真實(shí)的 DOM,反饋到用戶的界面上。
這樣一個(gè)生成補(bǔ)丁、更新差異的過(guò)程統(tǒng)稱為 diff 算法。
這里涉及3個(gè)要點(diǎn):
更新時(shí)機(jī):更新發(fā)生在setState、Hooks 調(diào)用等操作以后
遍歷算法:采用深度優(yōu)先遍歷,從根節(jié)點(diǎn)出發(fā),沿著左子樹方向進(jìn)行縱向遍歷,直到找到葉子節(jié)點(diǎn)為止。然后回溯到前一個(gè)節(jié)點(diǎn),進(jìn)行右子樹節(jié)點(diǎn)的遍歷,直到遍歷完所有可達(dá)節(jié)點(diǎn)
優(yōu)化策略:樹、組件及元素三個(gè)層面進(jìn)行復(fù)雜度的優(yōu)化
元素比對(duì)主要發(fā)生在同層級(jí)中,通過(guò)標(biāo)記節(jié)點(diǎn)操作生成補(bǔ)丁。節(jié)點(diǎn)操作包含了插入、移動(dòng)、刪除等。其中節(jié)點(diǎn)重新排序同時(shí)涉及插入、移動(dòng)、刪除三個(gè)操作,所以效率消耗最大,此時(shí)策略三起到了至關(guān)重要的作用。通過(guò)標(biāo)記 key 的方式,React 可以直接移動(dòng) DOM 節(jié)點(diǎn),降低內(nèi)耗
在組件比對(duì)的過(guò)程中:如果組件是同一類型則進(jìn)行樹比對(duì);如果不是則直接放入補(bǔ)丁中。只要父組件類型不同,就會(huì)被重新渲染。這也就是為什么shouldComponentUpdate、PureComponent 及 React.memo 可以提高性能的原因
這一策略需要進(jìn)行樹比對(duì),即對(duì)樹進(jìn)行分層比較。樹比對(duì)的處理手法是非?!氨┝Α钡?,即兩棵樹只對(duì)同一層次的節(jié)點(diǎn)進(jìn)行比較,如果發(fā)現(xiàn)節(jié)點(diǎn)已經(jīng)不存在了,則該節(jié)點(diǎn)及其子節(jié)點(diǎn)會(huì)被完全刪除掉,不會(huì)用于進(jìn)一步的比較,這就提升了比對(duì)效率
忽略節(jié)點(diǎn)跨層級(jí)操作場(chǎng)景,提升比對(duì)效率。
如果組件的 class 一致,則默認(rèn)為相似的樹結(jié)構(gòu),否則默認(rèn)為不同的樹結(jié)構(gòu)
同一層級(jí)的子節(jié)點(diǎn),可以通過(guò)標(biāo)記 key 的方式進(jìn)行列表對(duì)比
通過(guò)diff算法能準(zhǔn)確的獲取到具體的節(jié)點(diǎn) 進(jìn)行針對(duì)操作?。?!
124.Vue為什么沒(méi)有類似于React中shouldComponentUpdate的生命周期?
因?yàn)?Vue 的響應(yīng)式系統(tǒng)已經(jīng)在初次渲染時(shí)收集了渲染依賴的數(shù)據(jù)項(xiàng),通過(guò)自動(dòng)的方式就能夠得到相當(dāng)不錯(cuò)的性能。不過(guò),在一些場(chǎng)景下,手動(dòng)控制刷新檢查還是能夠進(jìn)一步提升渲染性能的
vue 的機(jī)制,其實(shí)就是個(gè)依賴關(guān)系管理機(jī)制。不管是計(jì)算屬性,watcher,以及 renderer,站在外部看,模型都是一樣的,即初始化的時(shí)候通過(guò)一些方法分析出來(lái)這個(gè)模塊依賴誰(shuí),等被依賴的數(shù)據(jù)更新了,這些模塊就重新運(yùn)算一遍產(chǎn)出物(實(shí)際上不盡然,比如計(jì)算屬性有惰性機(jī)制)。
具體到 renderer,哪些數(shù)據(jù)的變更會(huì)引起 dom 變更,在模板編譯的時(shí)候已經(jīng)確定了,并且寫死在生成的代碼里了。
而 react 是沒(méi)有這種自動(dòng)機(jī)制的,它去執(zhí)行 render 唯一的原因就是你主動(dòng)讓他 render。那你什么時(shí)候讓它 render 呢?工程上一般是使用一個(gè)數(shù)據(jù)流工具,數(shù)據(jù)有變化的時(shí)候發(fā)出一個(gè)事件,一股腦把數(shù)據(jù)推過(guò)來(lái),不區(qū)分哪個(gè)字段有變更(你區(qū)分了也沒(méi)用,renderer 根本不認(rèn))。如果這個(gè)數(shù)據(jù)流模型是多個(gè)組件共用的,那必然是在 render 之前有個(gè) hook 給我們個(gè)機(jī)會(huì)告訴組件“這次沒(méi)你的事兒”,有利于性能優(yōu)化。
那么,我們有沒(méi)有可能不增加代碼靜態(tài)分析環(huán)節(jié),搞清楚 react renderer 到底依賴哪些數(shù)據(jù),繼而把這個(gè)判斷自動(dòng)做掉呢?依我看不太可能,因?yàn)槲覀儾荒鼙WC運(yùn)行期跑一遍 render,它就會(huì)一次性訪問(wèn)它所有可能訪問(wèn)的數(shù)據(jù)。
125.Vue中的key到底有什么用?
1、key的作用主要是為了搞笑的更新虛擬dom,其原理是vue在patch過(guò)程中通過(guò)key可以精準(zhǔn)判斷兩個(gè)節(jié)點(diǎn)是否是同一個(gè),從而避免頻繁更新不同元素,使得整個(gè)patch過(guò)程更加高效,減少dom操作量,提高性能。2、另外,若不設(shè)置key還可能在列表更新時(shí)候引發(fā)一些隱藏的bug。3、vue中在使用相同標(biāo)簽名元素的過(guò)渡切換時(shí),也會(huì)使用到key屬性,其目的也是為了讓vue可以區(qū)分它們,否則vue只會(huì)替換其內(nèi)部屬性而不會(huì)觸發(fā)過(guò)渡效果。
126.不能用index做key
看了上面的介紹其實(shí)也很容易明白為什么不能用index作為key。
1、影響性能:當(dāng)用index作為key的時(shí)候,刪除節(jié)點(diǎn)后面的所有節(jié)點(diǎn)都會(huì)導(dǎo)致重新渲染,因?yàn)閕ndex變化了,可以也就變化了
有人說(shuō),當(dāng)你的列表數(shù)據(jù)沒(méi)有變化的時(shí)候可以用index作為key。也就是說(shuō)列表不會(huì)觸發(fā)更新元素,只有靜態(tài)展示。這種說(shuō)法你怎么看呢?之所以說(shuō)到這個(gè)問(wèn)題,是在vue官方群里面一群人應(yīng)為這個(gè)問(wèn)題討論半天。我弱弱回復(fù)一句,任何情況下都不要用index作為key。結(jié)果遭到炮轟,哎?。ǔ乔岸藢懰赖膌ist,且無(wú)操作不會(huì)引起key變化,只要是后端數(shù)據(jù),前端怎么能保證數(shù)據(jù)不變呢)。關(guān)于這個(gè)問(wèn)題,我有這樣三點(diǎn)想法:
1、代碼的規(guī)范性2、類比typescript,為什么變量要加類型,除了規(guī)范,也方便定位錯(cuò)誤3、列表的順序穩(wěn)定其實(shí)是難以保證的
127.vue項(xiàng)目中用jsx語(yǔ)法
JSX就是Javascript和XML結(jié)合的一種格式。React發(fā)明了JSX,利用HTML語(yǔ)法來(lái)創(chuàng)建虛擬DOM。當(dāng)遇到<,JSX就當(dāng)HTML解析,遇到{就當(dāng)JavaScript解析.
我為什么要在vue中用JSX?
是使用的的模板語(yǔ)法,Vue的模板實(shí)際上就是編譯成了 render 函數(shù),同樣支持 JSX 語(yǔ)法。在 Vue 官網(wǎng)中,提供 createElement 函數(shù)中使用模板中的功能。
但是極少數(shù)的 VUE項(xiàng)目用JSX語(yǔ)法 JSX語(yǔ)法 根植在react上面 ?在vue上還是 ?template舒服!