Vue 的基本原理
當(dāng) 一 個(gè) Vue 實(shí) 例 創(chuàng) 建 時(shí) , Vue 會(huì) 遍 歷 data 中 的 屬 性 , 用 Object.defineProperty ( vue3.0 使 用 proxy ) 將 它 們 轉(zhuǎn) 為 getter/setter,并且在內(nèi)部追蹤相關(guān)依賴,在屬性被訪問(wèn)和修改時(shí)通知變化。 每個(gè)組件實(shí)例都有相應(yīng)的 watcher 程序?qū)嵗鼤?huì)在組
件渲染的過(guò)程中把屬性記錄為依賴,之后當(dāng)依賴項(xiàng)的 setter 被調(diào)用時(shí),會(huì)通知 watcher 重新計(jì)算,從而致使它關(guān)聯(lián)的組件得以更新。
雙向數(shù)據(jù)綁定的原理
Vue.js 是采用數(shù)據(jù)劫持結(jié)合發(fā)布者- 訂閱者模式的方式, 通過(guò) Object.defineProperty()來(lái)劫持各個(gè)屬性的setter,getter,在數(shù)據(jù)變動(dòng)時(shí)發(fā)布消息給訂閱者,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)。主要分為以下幾個(gè)步驟:
-
需要observe 的數(shù)據(jù)對(duì)象進(jìn)行遞歸遍歷,包括子屬性對(duì)象的屬性,都加上setter 和getter 這樣的話,給這個(gè)對(duì)象的某個(gè)值賦值,就會(huì)觸發(fā)setter,那么就能監(jiān)聽到了數(shù)據(jù)變化
-
compile 解析模板指令,將模板中的變量替換成數(shù)據(jù),然后初始化渲染頁(yè)面視圖,并將每個(gè)指令對(duì)應(yīng)的節(jié)點(diǎn)綁定更新函數(shù),添加監(jiān)聽數(shù)據(jù)的訂閱者,一旦數(shù)據(jù)有變動(dòng),收到通知,更新視圖
-
Watcher 訂閱者是 Observer 和 Compile 之間通信的橋梁,主要做的事情是: ①在自身實(shí)例化時(shí)往屬性訂閱器(dep)里面添加自己 ②自身必須有一個(gè)update()方法 ③待屬性變動(dòng)dep.notice()通知時(shí),能調(diào)用自身的 update()方法,并觸發(fā) Compile 中綁定的回調(diào),則功成身退。
-
MVVM 作為數(shù)據(jù)綁定的入口,整合 Observer、Compile 和 Watcher三者,通過(guò) Observer 來(lái)監(jiān)聽自己的 model 數(shù)據(jù)變化,通過(guò) Compile來(lái)解析編譯模板指令,最終利用 Watcher 搭起 Observer 和 Compile之間的通信橋梁,達(dá)到數(shù)據(jù)變化 -> 視圖更新;視圖交互變化(input)-> 數(shù)據(jù)model 變更的雙向綁定效果。
MVVM、MVC、MVP 的區(qū)別
MVC、MVP 和 MVVM 是三種常見的軟件架構(gòu)設(shè)計(jì)模式,主要通過(guò)分離關(guān)注點(diǎn)的方式來(lái)組織代碼結(jié)構(gòu),優(yōu)化開發(fā)效率。
在開發(fā)單頁(yè)面應(yīng)用時(shí),往往一個(gè)路由頁(yè)面對(duì)應(yīng)了一個(gè)腳本文件,所有的頁(yè)面邏輯都在一個(gè)腳本文件里。頁(yè)面的渲染、數(shù)據(jù)的獲取,對(duì)用戶事件的響應(yīng)所有的應(yīng)用邏輯都混合在一起,這樣在開發(fā)簡(jiǎn)單項(xiàng)目時(shí),可能看不出什么問(wèn)題,如果項(xiàng)目變得復(fù)雜,那么整個(gè)文件就會(huì)變得冗長(zhǎng)、混亂,這樣對(duì)項(xiàng)目開發(fā)和后期的項(xiàng)目維護(hù)是非常不利的。
- MVC
MVC 通過(guò)分離 Model、View 和 Controller 的方式來(lái)組織代碼結(jié)構(gòu)。其中 View 負(fù)責(zé)頁(yè)面的顯示邏輯,Model 負(fù)責(zé)存儲(chǔ)頁(yè)面的業(yè)務(wù)數(shù)據(jù),以及對(duì)相應(yīng)數(shù)據(jù)的操作。并且 View 和 Model 應(yīng)用了觀察者模式,當(dāng) Model 層發(fā)生改變的時(shí)候它會(huì)通知有關(guān) View 層更新頁(yè)面。
Controller 層是 View 層和 Model 層的紐帶,它主要負(fù)責(zé)用戶與應(yīng)用的響應(yīng)操作,當(dāng)用戶與頁(yè)面產(chǎn)生交互的時(shí)候,Controller 中的事件觸發(fā)器就開始工作了,通過(guò)調(diào)用 Model 層,來(lái)完成對(duì) Model 的修改,然后 Model 層再去通知 View 層更新。
- MVVM
MVVM 分為 Model
、View
、ViewModel
:
Model 代表數(shù)據(jù)模型,數(shù)據(jù)和業(yè)務(wù)邏輯都在Model 層中定義; View 代表UI 視圖,負(fù)責(zé)數(shù)據(jù)的展示;
ViewModel 負(fù)責(zé)監(jiān)聽Model 中數(shù)據(jù)的改變并且控制視圖的更新,處理用戶交互操作;
Model 和 View 并無(wú)直接關(guān)聯(lián),而是通過(guò) ViewModel 來(lái)進(jìn)行聯(lián)系的, Model 和ViewModel 之間有著雙向數(shù)據(jù)綁定的聯(lián)系。因此當(dāng) Model 中的數(shù)據(jù)改變時(shí)會(huì)觸發(fā)View 層的刷新,View 中由于用戶交互操作而改變的數(shù)據(jù)也會(huì)在Model 中同步。
這種模式實(shí)現(xiàn)了 Model 和View 的數(shù)據(jù)自動(dòng)同步,因此開發(fā)者只需要專注于數(shù)據(jù)的維護(hù)操作即可,而不需要自己操作DOM。
- MVP
MVP 模式與 MVC 唯一不同的在于 Presenter 和 Controller。在 MVC 模式中使用觀察者模式,來(lái)實(shí)現(xiàn)當(dāng) Model 層數(shù)據(jù)發(fā)生變化的時(shí)候,通知 View 層的更新。這樣 View 層和 Model 層耦合在一起,當(dāng)項(xiàng)目邏輯變得復(fù)雜的時(shí)候,可能會(huì)造成代碼的混亂,并且可能會(huì)對(duì)代碼的復(fù)用性造成一些問(wèn)題。MVP 的模式通過(guò)使用 Presenter 來(lái)實(shí)現(xiàn)對(duì) View 層和 Model 層的解耦。MVC 中的 Controller 只知道 Model 的接口,因此它沒(méi)有辦法控制 View 層的更新,MVP 模式中, View 層的接口暴露給了 Presenter 因此可以在 Presenter 中將 Model 的變化和 View 的變化綁定在一起,以此來(lái)實(shí)現(xiàn) View 和 Model 的同步更新。這樣就實(shí)現(xiàn)了對(duì) View 和 Model 的解耦, Presenter 還包含了其他的響應(yīng)邏輯。
slot 是什么?有什么作用?原理是什么?
slot 又名插槽,是 Vue 的內(nèi)容分發(fā)機(jī)制,組件內(nèi)部的模板引擎使用 slot 元素作為承載分發(fā)內(nèi)容的出口。插槽 slot 是子組件的一個(gè)模板標(biāo)簽元素,而這一個(gè)標(biāo)簽元素是否顯示,以及怎么顯示是由父組件決定的。slot 又分三類,默認(rèn)插槽,具名插槽和作用域插槽。
默認(rèn)插槽:又名匿名插槽,當(dāng) slot 沒(méi)有指定name 屬性值的時(shí)候一個(gè)默認(rèn)顯示插槽,一個(gè)組件內(nèi)只有有一個(gè)匿名插槽。
具名插槽:帶有具體名字的插槽,也就是帶有name 屬性的slot,一個(gè)組件可以出現(xiàn)多個(gè)具名插槽。
作用域插槽:默認(rèn)插槽、具名插槽的一個(gè)變體,可以是匿名插槽,也可以是具名插槽,該插槽的不同點(diǎn)是在子組件渲染作用域插槽時(shí),可以將子組件內(nèi)部的數(shù)據(jù)傳遞給父組件,讓父組件根據(jù)子組件的傳遞過(guò)來(lái)的數(shù)據(jù)決定如何渲染該插槽。
實(shí)現(xiàn)原理:當(dāng)子組件 vm 實(shí)例化時(shí),獲取到父組件傳入的 slot 標(biāo)簽的內(nèi)容,存放在vm.$slot 中,默認(rèn)插槽為vm.$slot.default,具名插槽為vm.$slot.xxx,xxx 為插槽名,當(dāng)組件執(zhí)行渲染函數(shù)時(shí)候,遇到slot 標(biāo)簽,使用$slot 中的內(nèi)容進(jìn)行替換,此時(shí)可以為插槽傳遞數(shù)據(jù),若存在數(shù)據(jù),則可稱該插槽為作用域插槽。
$nextTick 原理及作用
Vue 的 nextTick 其本質(zhì)是對(duì) JavaScript 執(zhí)行原理 EventLoop 的一種應(yīng)用。
nextTick 的核心是利用了如 Promise 、 MutationObserver 、 setImmediate、setTimeout 的原生 JavaScript 方法來(lái)模擬對(duì)應(yīng)的微/宏任務(wù)的實(shí)現(xiàn),本質(zhì)是為了利用 JavaScript 的這些異步回調(diào)任務(wù)隊(duì)列來(lái)實(shí)現(xiàn) Vue 框架中自己的異步回調(diào)隊(duì)列。
nextTick 不僅是 Vue 內(nèi)部的異步隊(duì)列的調(diào)用方法,同時(shí)也允許開發(fā)者在實(shí)際項(xiàng)目中使用這個(gè)方法來(lái)滿足實(shí)際應(yīng)用中對(duì) DOM 更新數(shù)據(jù)時(shí)機(jī)的后續(xù)邏輯處理nextTick 是典型的將底層 JavaScript 執(zhí)行原理應(yīng)用到具體案例中的示例,引入異步更新隊(duì)列機(jī)制的原因∶
如果是同步更新,則多次對(duì)一個(gè)或多個(gè)屬性賦值,會(huì)頻繁觸發(fā) UI/DOM的渲染,可以減少一些無(wú)用渲染
同時(shí)由于 VirtualDOM 的引入,每一次狀態(tài)發(fā)生變化后,狀態(tài)變化的信號(hào)會(huì)發(fā)送給組件,組件內(nèi)部使用 VirtualDOM 進(jìn)行計(jì)算得出需要更新的具體的 DOM 節(jié)點(diǎn),然后對(duì) DOM 進(jìn)行更新操作,每次更新狀態(tài)后的渲染過(guò)程需要更多的計(jì)算,而這種無(wú)用功也將浪費(fèi)更多的性能,所以異步渲染變得更加至關(guān)重要
Vue 采用了數(shù)據(jù)驅(qū)動(dòng)視圖的思想,但是在一些情況下,仍然需要操作 DOM。有時(shí)候,可能遇到這樣的情況,DOM1 的數(shù)據(jù)發(fā)生了變化,而 DOM2需要從DOM1 中獲取數(shù)據(jù),那這時(shí)就會(huì)發(fā)現(xiàn)DOM2 的視圖并沒(méi)有更新,這時(shí)就需要用到了nextTick 了。
由于Vue 的DOM 操作是異步的,所以,在上面的情況中,就要將 DOM2獲取數(shù)據(jù)的操作寫在$nextTick 中。
所以,在以下情況下,會(huì)用到nextTick:
在數(shù)據(jù)變化后執(zhí)行的某個(gè)操作,而這個(gè)操作需要使用隨數(shù)據(jù)變化而變化的 DOM 結(jié)構(gòu)的時(shí)候,這個(gè)操作就需要方法在 nextTick()的回調(diào)函數(shù)中。
在vue 生命周期中,如果在created()鉤子進(jìn)行DOM 操作,也一定要放在nextTick()的回調(diào)函數(shù)中。
因?yàn)樵赾reated()鉤子函數(shù)中,頁(yè)面的DOM 還未渲染,這時(shí)候也沒(méi)辦法操作DOM,所以,此時(shí)如果想要操作DOM,必須將操作的代碼放在 nextTick()的回調(diào)函數(shù)中。
Vue 單頁(yè)應(yīng)用與多頁(yè)應(yīng)用的區(qū)別
概念:
SPA 單頁(yè)面應(yīng)用(SinglePage Web Application),指只有一個(gè)主頁(yè)面的應(yīng)用,一開始只需要加載一次 js、css 等相關(guān)資源。所有內(nèi)容都包含在主頁(yè)面,對(duì)每一個(gè)功能模塊組件化。單頁(yè)應(yīng)用跳轉(zhuǎn),就是切換相關(guān)組件,僅僅刷新局部資源。
MPA 多頁(yè)面應(yīng)用 (MultiPage Application),指有多個(gè)獨(dú)立頁(yè)面的應(yīng)用,每個(gè)頁(yè)面必須重復(fù)加載js、css 等相關(guān)資源。多頁(yè)應(yīng)用跳轉(zhuǎn),需要整頁(yè)資源刷新。
區(qū)別:
Vue 中封裝的數(shù)組方法有哪些,其如何實(shí)現(xiàn)頁(yè)面更新
在Vue 中,對(duì)響應(yīng)式處理利用的是 Object.defineProperty 對(duì)數(shù)據(jù)進(jìn)行攔截,而這個(gè)方法并不能監(jiān)聽到數(shù)組內(nèi)部變化,數(shù)組長(zhǎng)度變化,數(shù)組的截取變化等,所以需要對(duì)這些操作進(jìn)行 hack,讓 Vue 能監(jiān)聽到其中的變化。
那Vue 是如何實(shí)現(xiàn)讓這些數(shù)組方法實(shí)現(xiàn)元素的實(shí)時(shí)更新的呢,下面是 Vue 中對(duì)這些方法的封裝:
簡(jiǎn)單來(lái)說(shuō)就是,重寫了數(shù)組中的那些原生方法,首先獲取到這個(gè)數(shù)組的 ob , 也就是它的 Observer 對(duì)象, 如果有新的值, 就調(diào)用 observeArray 繼續(xù)對(duì)新的值觀察變化(也就是通過(guò) target_proto_== arrayMethods 來(lái)改變了數(shù)組實(shí)例的型),然后手動(dòng)調(diào)用notify,通知渲染watcher,執(zhí)行update。
Vue data 中某一個(gè)屬性的值發(fā)生改變后,視圖會(huì)立即同步執(zhí)行重新渲染嗎?
不會(huì)立即同步執(zhí)行重新渲染。Vue 實(shí)現(xiàn)響應(yīng)式并不是數(shù)據(jù)發(fā)生變化之后 DOM 立即變化,而是按一定的策略進(jìn)行 DOM 的更新。Vue 在更新 DOM 時(shí)是異步執(zhí)行的。只要偵聽到數(shù)據(jù)變化, Vue 將開啟一個(gè)隊(duì)列,并緩沖在同一事件循環(huán)中發(fā)生的所有數(shù)據(jù)變更。
如果同一個(gè)watcher 被多次觸發(fā),只會(huì)被推入到隊(duì)列中一次。這種在緩沖時(shí)去除重復(fù)數(shù)據(jù)對(duì)于避免不必要的計(jì)算和 DOM 操作是非常重要的。然后,在下一個(gè)的事件循環(huán)tick 中,Vue 刷新隊(duì)列并執(zhí)行實(shí)際
(已去重的)工作。
簡(jiǎn)述 mixin、extends 的覆蓋邏輯
- mixin 和 extends
mixin 和 extends 均是用于合并、拓展組件的, 兩者均通過(guò) mergeOptions 方法實(shí)現(xiàn)合并。
mixins 接收一個(gè)混入對(duì)象的數(shù)組,其中混入對(duì)象可以像正常的實(shí)例對(duì)象一樣包含實(shí)例選項(xiàng),這些選項(xiàng)會(huì)被合并到最終的選項(xiàng)中。Mixin鉤子按照傳入順序依次調(diào)用,并在調(diào)用組件自身的鉤子之前被調(diào)用。
extends 主要是為了便于擴(kuò)展單文件組件,接收一個(gè)對(duì)象或構(gòu)造函數(shù)。
- mergeOptions 的執(zhí)行過(guò)程
規(guī) 范 化 選 項(xiàng) ( normalizeProps 、 normalizelnject 、 normalizeDirectives)
對(duì)未合并的選項(xiàng),進(jìn)行判斷
子組件可以直接改變父組件的數(shù)據(jù)嗎?
子組件不可以直接改變父組件的數(shù)據(jù)。這樣做主要是為了維護(hù)父子組件的單向數(shù)據(jù)流。每次父級(jí)組件發(fā)生更新時(shí),子組件中所有的 prop都將會(huì)刷新為最新的值。如果這樣做了,Vue 會(huì)在瀏覽器的控制臺(tái)中發(fā)出警告。
子組件不可以直接改變父組件的數(shù)據(jù)。這樣做主要是為了維護(hù)父子組
件的單向數(shù)據(jù)流。每次父級(jí)組件發(fā)生更新時(shí),子組件中所有的 prop都將會(huì)刷新為最新的值。如果這樣做了,Vue 會(huì)在瀏覽器的控制臺(tái)中發(fā)出警告。
Vue 提倡單向數(shù)據(jù)流,即父級(jí) props 的更新會(huì)流向子組件,但是反過(guò)來(lái)則不行。這是為了防止意外的改變父組件狀態(tài),使得應(yīng)用的數(shù)據(jù)流變得難以理解,導(dǎo)致數(shù)據(jù)流混亂。如果破壞了單向數(shù)據(jù)流,當(dāng)應(yīng)用復(fù)雜時(shí),debug 的成本會(huì)非常高。
只能通過(guò) $emit 派發(fā)一個(gè)自定義事件,父組件接收到后,由父組件修改。
對(duì) React 和 Vue 的理解,它們的異同
相似之處:
-
都將注意力集中保持在核心庫(kù),而將其他功能如路由和全局狀態(tài)管理交給相關(guān)的庫(kù);
-
都有自己的構(gòu)建工具,能讓你得到一個(gè)根據(jù)最佳實(shí)踐設(shè)置的項(xiàng)目模板;都使用了Virtual DOM(虛擬 DOM)提高重繪性能;
-
都有props 的概念,允許組件間的數(shù)據(jù)傳遞;
-
都鼓勵(lì)組件化應(yīng)用,將應(yīng)用分拆成一個(gè)個(gè)功能明確的模塊,提高復(fù)用性。
不同之處 :
-
數(shù)據(jù)流
Vue 默認(rèn)支持?jǐn)?shù)據(jù)雙向綁定,而React 一直提倡單向數(shù)據(jù)流 -
虛擬DOM
Vue2.x 開始引入"Virtual DOM",消除了和React 在這方面的差異,但是在具體的細(xì)節(jié)還是有各自的特點(diǎn)。
Vue 宣稱可以更快地計(jì)算出Virtual DOM 的差異,這是由于它在渲染過(guò)程中,會(huì)跟蹤每一個(gè)組件的依賴關(guān)系,不需要重新渲染整個(gè)組件樹。
對(duì)于React 而言,每當(dāng)應(yīng)用的狀態(tài)被改變時(shí),全部子組件都會(huì)重新渲染。當(dāng)然,這可以通過(guò) PureComponent/shouldComponentUpdate 這個(gè)生命周期方法來(lái)進(jìn)行控制,但Vue 將此視為默認(rèn)的優(yōu)化。
- 組件化
React 與Vue 最大的不同是模板的編寫。
Vue 鼓勵(lì)寫近似常規(guī)HTML 的模板。寫起來(lái)很接近標(biāo)準(zhǔn) HTML 元素,只是多了一些屬性。
React 推薦你所有的模板通用JavaScript 的語(yǔ)法擴(kuò)展——JSX 書寫。
具體來(lái)講:React 中render 函數(shù)是支持閉包特性的,所以import 的組件在render 中可以直接調(diào)用。但是在 Vue 中,由于模板中使用的數(shù)據(jù)都必須掛在 this 上進(jìn)行一次中轉(zhuǎn),所以 import 一個(gè)組件完了之后,還需要在 components 中再聲明下。
- 監(jiān)聽數(shù)據(jù)變化的實(shí)現(xiàn)原理不同
Vue 通過(guò) getter/setter 以及一些函數(shù)的劫持,能精確知道數(shù)據(jù)變化,不需要特別的優(yōu)化就能達(dá)到很好的性能
React 默 認(rèn) 是 通 過(guò) 比 較 引 用 的 方 式 進(jìn) 行 的 , 如 果 不 優(yōu) 化
(PureComponent/shouldComponentUpdate)可能導(dǎo)致大量不必要的 vDOM 的重新渲染。這是因?yàn)?Vue 使用的是可變數(shù)據(jù),而React 更強(qiáng)調(diào)數(shù)據(jù)的不可變。
- 高階組件
react 可以通過(guò)高階組件(HOC)來(lái)擴(kuò)展,而Vue 需要通過(guò)mixins 來(lái)擴(kuò)展。
高階組件就是高階函數(shù),而 React 的組件本身就是純粹的函數(shù),所以高階函數(shù)對(duì)React 來(lái)說(shuō)易如反掌。相反 Vue.js 使用HTML 模板創(chuàng)建視圖組件,這時(shí)模板無(wú)法有效的編譯,因此Vue 不能采用HOC 來(lái)實(shí)現(xiàn)。
- 構(gòu)建工具
兩者都有自己的構(gòu)建工具:
React ==> Create React APP
Vue ==> vue-cli
- 跨平臺(tái)
React ==> React Native
Vue ==> Weex
Vue 的優(yōu)點(diǎn)
- 輕量級(jí)框架:只關(guān)注視圖層,是一個(gè)構(gòu)建數(shù)據(jù)的視圖集合,大小只有幾十 kb ;
- 簡(jiǎn)單易學(xué):國(guó)人開發(fā),中文文檔,不存在語(yǔ)言障礙 ,易于理解和學(xué)習(xí);
- 雙向數(shù)據(jù)綁定:保留了 angular 的特點(diǎn),在數(shù)據(jù)操作方面更為簡(jiǎn)單;
- 組件化:保留了 react 的優(yōu)點(diǎn),實(shí)現(xiàn)了 html 的封裝和重用,在構(gòu)建單頁(yè)面應(yīng)用方面有著獨(dú)特的優(yōu)勢(shì);
- 視圖,數(shù)據(jù),結(jié)構(gòu)分離:使數(shù)據(jù)的更改更為簡(jiǎn)單,不需要進(jìn)行邏輯代碼的修改,只需要操作數(shù)據(jù)就能完成相關(guān)操作;
- 虛擬 DOM:dom 操作是非常耗費(fèi)性能的,不再使用原生的 dom 操作節(jié)點(diǎn),極大解放 dom 操作,但具體操作的還是 dom 不過(guò)是換了另一種方式;
- 運(yùn)行速度更快:相比較于 react 而言,同樣是操作虛擬 dom,就性能而言, vue 存在很大的優(yōu)勢(shì)。
assets 和static 的區(qū)別
相同點(diǎn): assets 和 static 兩個(gè)都是存放靜態(tài)資源文件。項(xiàng)目中所需要的資源文件圖片,字體圖標(biāo),樣式文件等都可以放在這兩個(gè)文件下,這是相同點(diǎn)
不同點(diǎn):assets 中存放的靜態(tài)資源文件在項(xiàng)目打包時(shí),也就是運(yùn)行 npm run build 時(shí)會(huì)將 assets 中放置的靜態(tài)資源文件進(jìn)行打包上傳,所謂打包簡(jiǎn)單點(diǎn)可以理解為壓縮體積,代碼格式化。而壓縮后的靜態(tài)資源文件最終也都會(huì)放置在 static 文件中跟著 index.html一同上傳至服務(wù)器。static 中放置的靜態(tài)資源文件就不會(huì)要走打包壓縮格式化等流程,而是直接進(jìn)入打包好的目錄,直接上傳至服務(wù)器。因?yàn)楸苊饬藟嚎s直接進(jìn)行上傳,在打包時(shí)會(huì)提高一定的效率,但是 static 中的資源文件由于沒(méi)有進(jìn)行壓縮等操作,所以文件的體積也就相對(duì)于 assets 中打包后的文件提交較大點(diǎn)。在服務(wù)器中就會(huì)占據(jù)更大的空間。
建議: 將項(xiàng)目中 template 需要的樣式文件js 文件等都可以放置在 assets 中,走打包這一流程。減少體積。而項(xiàng)目中引入的第三方的資源文件如iconfoont.css 等文件可以放置在 static 中,因?yàn)檫@些引入的第三方文件已經(jīng)經(jīng)過(guò)處理,不再需要處理,直接上傳。
delete 和Vue.delete 刪除數(shù)組的區(qū)別
delete 只是被刪除的元素變成了 empty/undefined 其他的元素的鍵值還是不變。
Vue.delete 直接刪除了數(shù)組 改變了數(shù)組的鍵值。
Vue 模版編譯原理
vue 中的模板template 無(wú)法被瀏覽器解析并渲染,因?yàn)檫@不屬于瀏覽器的標(biāo)準(zhǔn),不是正確的 HTML 語(yǔ)法,所有需要將 template 轉(zhuǎn)化成一個(gè)JavaScript 函數(shù),這樣瀏覽器就可以執(zhí)行這一個(gè)函數(shù)并渲染出對(duì)應(yīng)的HTML 元素,就可以讓視圖跑起來(lái)了,這一個(gè)轉(zhuǎn)化的過(guò)程,就成為模板編譯。模板編譯又分三個(gè)階段,解析parse,優(yōu)化 optimize,生成generate,最終生成可執(zhí)行函數(shù)render。
解析階段:使用大量的正則表達(dá)式對(duì)template 字符串進(jìn)行解析,將標(biāo)簽、指令、屬性等轉(zhuǎn)化為抽象語(yǔ)法樹AST。
優(yōu)化階段:遍歷AST,找到其中的一些靜態(tài)節(jié)點(diǎn)并進(jìn)行標(biāo)記,方便在頁(yè)面重渲染的時(shí)候進(jìn)行diff 比較時(shí),直接跳過(guò)這一些靜態(tài)節(jié)點(diǎn),優(yōu)化runtime 的性能。
生成階段:將最終的AST 轉(zhuǎn)化為render 函數(shù)字符串。
vue 初始化頁(yè)面閃動(dòng)問(wèn)題
使用vue 開發(fā)時(shí),在 vue 初始化之前,由于 div 是不歸 vue 管的,所
以我們寫的代碼在還沒(méi)有解析的情況下會(huì)容易出現(xiàn)花屏現(xiàn)象,看到類似于{{message}}的字樣,雖然一般情況下這個(gè)時(shí)間很短暫,但是還是有必要讓解決這個(gè)問(wèn)題的。
首先:在css 里加上以下代碼:
如果沒(méi)有徹底解決問(wèn)題, 則在根元素加上 style=“display: none;” :style="{display: ‘block’}"
MVVM 的優(yōu)缺點(diǎn)?
優(yōu)點(diǎn):
分離視圖(View)和模型(Model),降低代碼耦合,提?視圖或者邏輯的重?性: ?如視圖(View)可以獨(dú)?于 Model 變化和修改,?個(gè) ViewModel 可以綁定不同的"View"上,當(dāng) View 變化的時(shí)候 Model不可以不變,當(dāng)Model 變化的時(shí)候 View 也可以不變。你可以把?些視圖邏輯放在?個(gè)ViewModel??,讓很多 view 重?這段視圖邏輯提?可測(cè)試性: ViewModel 的存在可以幫助開發(fā)者更好地編寫測(cè)試代碼
?動(dòng)更新dom: 利?雙向綁定,數(shù)據(jù)更新后視圖?動(dòng)更新,讓開發(fā)者從繁瑣的?動(dòng)dom 中解放
缺點(diǎn):
Bug 很難被調(diào)試: 因?yàn)槭?雙向綁定的模式,當(dāng)你看到界?異常了,有可能是你 View 的代碼有 Bug,也可能是 Model 的代碼有問(wèn)題。數(shù)據(jù)綁定使得?個(gè)位置的Bug 被快速傳遞到別的位置,要定位原始出問(wèn)題的地?就變得不那么容易了。另外,數(shù)據(jù)綁定的聲明是指令式地寫在View 的模版當(dāng)中的,這些內(nèi)容是沒(méi)辦法去打斷點(diǎn)debug 的?個(gè)?的模塊中model 也會(huì)很?,雖然使??便了也很容易保證了數(shù)據(jù)的?致性,當(dāng)時(shí)?期持有,不釋放內(nèi)存就造成了花費(fèi)更多的內(nèi)存
對(duì)于?型的圖形應(yīng)?程序,視圖狀態(tài)較多,ViewModel 的構(gòu)建和維護(hù)的成本都會(huì)?較?。
v-if 和v-for 哪個(gè)優(yōu)先級(jí)更高?如果同時(shí)出現(xiàn),應(yīng)如何優(yōu)化?
v-for 優(yōu)先于 v-if 被解析,如果同時(shí)出現(xiàn),每次渲染都會(huì)先執(zhí)行循環(huán)再判斷條件,無(wú)論如何循環(huán)都不可避免,浪費(fèi)了性能。
要避免出現(xiàn)這種情況,則在外層嵌套template,在這一層進(jìn)行v-if判斷,然后在內(nèi)部進(jìn)行 v-for 循環(huán)。如果條件出現(xiàn)在循環(huán)內(nèi)部,可通過(guò)計(jì)算屬性提前過(guò)濾掉那些不需要顯示的項(xiàng)。
對(duì)Vue 組件化的理解
-
組件是獨(dú)立和可復(fù)用的代碼組織單元。組件系統(tǒng)是 Vue 核心特性之一,它使開發(fā)者使用小型、獨(dú)立和通??蓮?fù)用的組件構(gòu)建大型應(yīng)用;
-
組件化開發(fā)能大幅提高應(yīng)用開發(fā)效率、測(cè)試性、復(fù)用性等;
-
組件使用按分類有:頁(yè)面組件、業(yè)務(wù)組件、通用組件;
-
vue 的組件是基于配置的,我們通常編寫的組件是組件配置而非組件,框架后續(xù)會(huì)生成其構(gòu)造函數(shù),它們基于 VueComponent,擴(kuò)展于 Vue;
-
vue 中常見組件化技術(shù)有:屬性 prop,自定義事件,插槽等,它們主要用于組件通信、擴(kuò)展等;6.合理的劃分組件,有助于提升應(yīng)用性能;
-
組件應(yīng)該是高內(nèi)聚、低耦合的;
-
遵循單向數(shù)據(jù)流的原則
對(duì)vue 設(shè)計(jì)原則的理解
-
漸進(jìn)式 JavaScript 框架:與其它大型框架不同的是,Vue 被設(shè)計(jì)為可以自底向上逐層應(yīng)用。Vue 的核心庫(kù)只關(guān)注視圖層,不僅易于上手,還便于與第三方庫(kù)或既有項(xiàng)目整合。另一方面,當(dāng)與現(xiàn)代化的工具鏈以及各種支持類庫(kù)結(jié)合使用時(shí),Vue 也完全能夠?yàn)閺?fù)雜的單頁(yè)應(yīng)用提供驅(qū)動(dòng)。
-
易用性:vue 提供數(shù)據(jù)響應(yīng)式、聲明式模板語(yǔ)法和基于配置的組件系統(tǒng)等核心特性。這些使我們只需要關(guān)注應(yīng)用的核心業(yè)務(wù)即可,只要會(huì)寫js、html 和css 就能輕松編寫vue 應(yīng)用。
-
靈活性:漸進(jìn)式框架的最大優(yōu)點(diǎn)就是靈活性,如果應(yīng)用足夠小,我們可能僅需要vue 核心特性即可完成功能;隨著應(yīng)用規(guī)模不斷擴(kuò)大,我們才可能逐漸引入路由、狀態(tài)管理、vue-cli 等庫(kù)和工具,不管是應(yīng)用體積還是學(xué)習(xí)難度都是一個(gè)逐漸增加的平和曲線。
-
高效性:超快的虛擬 DOM 和di?算法使我們的應(yīng)用擁有最佳的性能表現(xiàn)。追求高效的過(guò)程還在繼續(xù),vue3 中引入 Proxy 對(duì)數(shù)據(jù)響應(yīng)式改進(jìn)以及編譯器中對(duì)于靜態(tài)內(nèi)容編譯的改進(jìn)都會(huì)讓vue 更加高效。
說(shuō)一下Vue 的生命周期
Vue 實(shí)例有?個(gè)完整的?命周期,也就是從開始創(chuàng)建、初始化數(shù)據(jù)、編譯模版、掛載Dom -> 渲染、更新 -> 渲染、卸載 等?系列過(guò)程,稱這是Vue 的?命周期。
-
beforeCreate
(創(chuàng)建前):數(shù)據(jù)觀測(cè)和初始化事件還未開始,此時(shí) data 的響應(yīng)式追蹤、event/watcher 都還沒(méi)有被設(shè)置,也就是說(shuō)不能訪問(wèn)到data、computed、watch、methods 上的方法和數(shù)據(jù)。 -
created
(創(chuàng)建后) :實(shí)例創(chuàng)建完成,實(shí)例上配置的 options 包括 data、computed、watch、methods 等都配置完成,但是此時(shí)渲染得節(jié)點(diǎn)還未掛載到 DOM,所以不能訪問(wèn)到 $el 屬性。 -
beforeMount
(掛載前):在掛載開始之前被調(diào)用,相關(guān)的render函數(shù)首次被調(diào)用。實(shí)例已完成以下的配置:編譯模板,把data 里面的數(shù)據(jù)和模板生成html。此時(shí)還沒(méi)有掛載html 到頁(yè)面上。 -
mounted
(掛載后):在 el 被新創(chuàng)建的 vm.$el 替換,并掛載到實(shí)例上去之后調(diào)用。實(shí)例已完成以下的配置:用上面編譯好的 html 內(nèi)容替換el 屬性指向的DOM 對(duì)象。完成模板中的 html 渲染到html 頁(yè)面中。此過(guò)程中進(jìn)行ajax 交互。 -
beforeUpdate
(更新前):響應(yīng)式數(shù)據(jù)更新時(shí)調(diào)用,此時(shí)雖然響應(yīng)式數(shù)據(jù)更新了,但是對(duì)應(yīng)的真實(shí) DOM 還沒(méi)有被渲染。 -
updated
(更新后) :在由于數(shù)據(jù)更改導(dǎo)致的虛擬 DOM 重新渲染和打補(bǔ)丁之后調(diào)用。此時(shí) DOM 已經(jīng)根據(jù)響應(yīng)式數(shù)據(jù)的變化更新了。調(diào)用時(shí),組件 DOM 已經(jīng)更新,所以可以執(zhí)行依賴于 DOM 的操作。然而在大多數(shù)情況下,應(yīng)該避免在此期間更改狀態(tài),因?yàn)檫@可能會(huì)導(dǎo)致更新無(wú)限循環(huán)。該鉤子在服務(wù)器端渲染期間不被調(diào)用。 -
beforeDestroy
(銷毀前):實(shí)例銷毀之前調(diào)用。這一步,實(shí)例仍然完全可用,this 仍能獲取到實(shí)例。 -
destroyed
(銷毀后):實(shí)例銷毀后調(diào)用,調(diào)用后,Vue 實(shí)例指示的所有東西都會(huì)解綁定,所有的事件監(jiān)聽器會(huì)被移除,所有的子實(shí)例也會(huì)被銷毀。該鉤子在服務(wù)端渲染期間不被調(diào)用。
另外還有keep-alive 獨(dú)有的生命周期,分別為activated 和 deactivated。用 keep-alive 包裹的組件在切換時(shí)不會(huì)進(jìn)行銷毀,而是緩存到內(nèi)存中并執(zhí)行deactivated 鉤子函數(shù),命中緩存渲染后會(huì)執(zhí)行activated 鉤子函數(shù)。
Vue 子組件和父組件執(zhí)行順序
加載渲染過(guò)程:
-
父組件 beforeCreate
-
父組件 created
-
父組件 beforeMount
-
子組件 beforeCreate
-
子組件 created
-
子組件 beforeMount
-
子組件 mounted
-
父組件 mounted
更新過(guò)程:
-
父組件 beforeUpdate
-
子組件 beforeUpdate
-
子組件 updated
-
父組件 updated
銷毀過(guò)程:
-
父組件 beforeDestroy
-
子組件 beforeDestroy
-
子組件 destroyed
-
父組件 destoryed
created 和mounted 的區(qū)別
created:在模板渲染成 html 前調(diào)用,即通常初始化某些屬性值,然后再渲染成視圖。
mounted:在模板渲染成 html 后調(diào)用,通常是初始化頁(yè)面完成后,再對(duì)html 的dom 節(jié)點(diǎn)進(jìn)行一些需要的操作。
一般在哪個(gè)生命周期請(qǐng)求異步數(shù)據(jù)
我們可以在鉤子函數(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è)面加載時(shí)間,用戶體驗(yàn)更好;
SSR 不支持 beforeMount 、mounted 鉤子函數(shù),放在 created 中有助于一致性。
keep-alive 中的生命周期哪些
keep-alive 是 Vue 提供的一個(gè)內(nèi)置組件,用來(lái)對(duì)組件進(jìn)行緩存——在組件切換過(guò)程中將狀態(tài)保留在內(nèi)存中,防止重復(fù)渲染DOM。
如果為一個(gè)組件包裹了 keep-alive,那么它會(huì)多出兩個(gè)生命周期: deactivated、activated。同時(shí),beforeDestroy 和 destroyed 就不會(huì)再被觸發(fā)了,因?yàn)榻M件不會(huì)被真正銷毀。
當(dāng)組件被換掉時(shí),會(huì)被緩存到內(nèi)存中、觸發(fā) deactivated 生命周期;當(dāng)組件被切回來(lái)時(shí),再去緩存里找這個(gè)組件、觸發(fā) activated 鉤子函數(shù)。
路由的hash 和history 模式的區(qū)別
Vue-Router 有兩種模式:hash 模式和 history 模式。默認(rèn)的路由模式是hash 模式。
- hash 模式
簡(jiǎn)介: hash 模式是開發(fā)中默認(rèn)的模式,它的URL 帶著一個(gè)#,例如: http://www.abc.com/#/vue,它的hash 值就是#/vue。
特點(diǎn):hash 值會(huì)出現(xiàn)在URL 里面,但是不會(huì)出現(xiàn)在HTTP 請(qǐng)求中,對(duì)后端完全沒(méi)有影響。所以改變hash 值,不會(huì)重新加載頁(yè)面。這種模式的瀏覽器支持度很好,低版本的IE 瀏覽器也支持這種模式。hash
路由被稱為是前端路由,已經(jīng)成為SPA(單頁(yè)面應(yīng)用)的標(biāo)配。
原理: hash 模式的主要原理就是onhashchange()事件:
使用onhashchange()事件的好處就是,在頁(yè)面的 hash 值發(fā)生變化時(shí),
無(wú)需向后端發(fā)起請(qǐng)求,window 就可以監(jiān)聽事件的改變,并按規(guī)則加載相應(yīng)的代碼。除此之外,hash 值變化對(duì)應(yīng)的 URL 都會(huì)被瀏覽器記
錄下來(lái),這樣瀏覽器就能實(shí)現(xiàn)頁(yè)面的前進(jìn)和后退。雖然是沒(méi)有請(qǐng)求后端服務(wù)器,但是頁(yè)面的hash 值和對(duì)應(yīng)的URL 關(guān)聯(lián)起來(lái)了。
- history 模式
簡(jiǎn)介: history 模式的URL 中沒(méi)有#,它使用的是傳統(tǒng)的路由分發(fā)模式,即用戶在輸入一個(gè) URL 時(shí),服務(wù)器會(huì)接收這個(gè)請(qǐng)求,并解析這個(gè) URL,然后做出相應(yīng)的邏輯處理。
特 點(diǎn) : 當(dāng) 使 用 history 模 式 時(shí) , URL 就 像 這 樣 : http://abc.com/user/id。相比 hash 模式更加好看。但是,history模式需要后臺(tái)配置支持。如果后臺(tái)沒(méi)有正確配置,訪問(wèn)時(shí)會(huì)返回 404。
API: history api 可以分為兩大部分,切換歷史狀態(tài)和修改歷史狀態(tài):
修改歷史狀態(tài): 包括了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法,這兩個(gè)方法應(yīng)用于瀏覽器的歷史記錄棧,提供了對(duì)歷史記錄進(jìn)行修改的功能。只是當(dāng)他們進(jìn)行修改時(shí),雖然修改了url,但瀏覽器不會(huì)立即向后端發(fā)送請(qǐng)求。如果要做到改變url 但又不刷新頁(yè)面的效果,就需要前端用上這兩個(gè)API。
切換歷史狀態(tài): 包括 forward()、back()、go()三個(gè)方法,對(duì)應(yīng)瀏覽器的前進(jìn),后退,跳轉(zhuǎn)操作。
雖然 history 模式丟棄了丑陋的#。但是,它也有自己的缺點(diǎn),就是在刷新頁(yè)面的時(shí)候,如果沒(méi)有相應(yīng)的路由或資源,就會(huì)刷出 404 來(lái)。
如果想要切換到history 模式,就要進(jìn)行以下配置(后端也要進(jìn)行配置):
- 兩種模式對(duì)比
調(diào)用 history.pushState() 相比于直接修改 hash,存在以下優(yōu)勢(shì):
pushState() 設(shè)置的新 URL 可以是與當(dāng)前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能設(shè)置與當(dāng)前 URL 同文檔的 URL;
pushState() 設(shè)置的新 URL 可以與當(dāng)前 URL 一模一樣,這樣也會(huì)把記錄添加到棧中;而 hash 設(shè)置的新值必須與原來(lái)不一樣才會(huì)觸發(fā)動(dòng)作將記錄添加到棧中;
pushState() 通過(guò) stateObject 參數(shù)可以添加任意類型的數(shù)據(jù)到記錄中;而 hash 只可添加短字符串;
pushState() 可額外設(shè)置 title 屬性供后續(xù)使用。
hash 模式下,僅hash 符號(hào)之前的url 會(huì)被包含在請(qǐng)求中,后端如果沒(méi)有做到對(duì)路由的全覆蓋,也不會(huì)返回 404 錯(cuò)誤;history 模式下,前端的url 必須和實(shí)際向后端發(fā)起請(qǐng)求的url 一致,如果沒(méi)有對(duì)用的路由處理,將返回 404 錯(cuò)誤。
hash 模式和 history 模式都有各自的優(yōu)勢(shì)和缺陷,還是要根據(jù)實(shí)際情況選擇性的使用。
Vue-router 跳轉(zhuǎn)和 location.href 有什么區(qū)別
使用 location.href= /url 來(lái)跳轉(zhuǎn),簡(jiǎn)單方便,但是刷新了頁(yè)面;使用 history.pushState( /url ) ,無(wú)刷新頁(yè)面,靜態(tài)跳轉(zhuǎn);
引進(jìn) router ,然后使用 router.push( /url ) 來(lái)跳轉(zhuǎn),使用了 diff算法,實(shí)現(xiàn)了按需加載,減少了 dom 的消耗。其實(shí)使用 router 跳轉(zhuǎn)和使用 history.pushState() 沒(méi)什么差別的,因?yàn)?vue-router 就是用了 history.pushState() ,尤其是在history 模式下。
Vuex 的原理
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。每一個(gè) Vuex 應(yīng)用的核心就是 store(倉(cāng)庫(kù))?!皊tore” 基本上就是一個(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)的變化。
Vuex 為Vue Components 建立起了一個(gè)完整的生態(tài)圈,包括開發(fā)中的 API 調(diào)用一環(huán)。
- 核心流程中的主要功能:
Vue Components 是 vue 組件,組件會(huì)觸發(fā)(dispatch)一些事件或動(dòng)作,也就是圖中的 Actions;
在組件中發(fā)出的動(dòng)作,肯定是想獲取或者改變數(shù)據(jù)的,但是在 vuex中,數(shù)據(jù)是集中管理的,不能直接去更改數(shù)據(jù),所以會(huì)把這個(gè)動(dòng)作提交(Commit)到 Mutations 中;
然后 Mutations 就去改變(Mutate)State 中的數(shù)據(jù);
當(dāng) State 中的數(shù)據(jù)被改變之后,就會(huì)重新渲染(Render)到 Vue Components 中去,組件展示更新后的數(shù)據(jù),完成一個(gè)流程。
- 各模塊在核心流程中的主要功能:
Vue Components∶ Vue 組件。HTML 頁(yè)面上,負(fù)責(zé)接收用戶操作等交互行為,執(zhí)行dispatch 方法觸發(fā)對(duì)應(yīng)action 進(jìn)行回應(yīng)。
dispatch∶操作行為觸發(fā)方法,是唯一能執(zhí)行action 的方法。
actions∶ 操作行為處理模塊。負(fù)責(zé)處理 Vue Components 接收到的所有交互行為。包含同步/異步操作,支持多個(gè)同名方法,按照注冊(cè)的順序依次觸發(fā)。向后臺(tái) API 請(qǐng)求的操作就在這個(gè)模塊中進(jìn)行,包括觸發(fā)其他action 以及提交mutation 的操作。該模塊提供了 Promise的封裝,以支持action 的鏈?zhǔn)接|發(fā)。
commit∶狀態(tài)改變提交操作方法。對(duì) mutation 進(jìn)行提交,是唯一能執(zhí)行mutation 的方法。
mutations∶狀態(tài)改變操作方法。是 Vuex 修改state 的唯一推薦方法,其他修改方式在嚴(yán)格模式下將會(huì)報(bào)錯(cuò)。該方法只能進(jìn)行同步操作,且方法名只能全局唯一。操作之中會(huì)有一些 hook 暴露出來(lái),以進(jìn)行 state 的監(jiān)控等。
state∶ 頁(yè)面狀態(tài)管理容器對(duì)象。集中存儲(chǔ) Vuecomponents 中 data對(duì)象的零散數(shù)據(jù),全局唯一,以進(jìn)行統(tǒng)一的狀態(tài)管理。頁(yè)面顯示所需的數(shù)據(jù)從該對(duì)象中進(jìn)行讀取,利用 Vue 的細(xì)粒度數(shù)據(jù)響應(yīng)機(jī)制來(lái)進(jìn)行高效的狀態(tài)更新。
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來(lái)直接上傳(img-EgYN12F4-1682748429789)(media/image8.png)]getters∶ state 對(duì)象讀取方法。圖中沒(méi)有單獨(dú)列出該模塊,應(yīng)該被包含在了render 中,Vue Components 通過(guò)該方法讀取全局state 對(duì)象。
總結(jié):
Vuex 實(shí)現(xiàn)了一個(gè)單向數(shù)據(jù)流,在全局擁有一個(gè) State 存放數(shù)據(jù),當(dāng)組件要更改 State 中的數(shù)據(jù)時(shí),必須通過(guò) Mutation 提交修改信息,
Mutation 同時(shí)提供了訂閱者模式供外部插件調(diào)用獲取 State 數(shù)據(jù)的更新。而當(dāng)所有異步操作(常見于調(diào)用后端接口異步獲取更新數(shù)據(jù))或批量的同步操作需要走 Action ,但 Action 也是無(wú)法直接修改 State 的,還是需要通過(guò)Mutation 來(lái)修改State 的數(shù)據(jù)。最后,根據(jù) State 的變化,渲染到視圖上。
Vuex 和 localStorage 的區(qū)別
- 最重要的區(qū)別
-
vuex 存儲(chǔ)在內(nèi)存中
-
localstorage 則以文件的方式存儲(chǔ)在本地,只能存儲(chǔ)字符串類型的數(shù)據(jù),存儲(chǔ)對(duì)象需要 JSON 的stringify 和parse 方法進(jìn)行處理。 讀取內(nèi)存比讀取硬盤速度要快
- 應(yīng)用場(chǎng)景
-
Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲(chǔ)管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測(cè)的方式發(fā)生變化。vuex 用于組件之間的傳值。
-
localstorage 是本地存儲(chǔ),是將數(shù)據(jù)存儲(chǔ)到瀏覽器的方法,一般是在跨頁(yè)面?zhèn)鬟f數(shù)據(jù)時(shí)使用 。
Vuex 能做到數(shù)據(jù)的響應(yīng)式,localstorage 不能
- 永久性
刷新頁(yè)面時(shí)vuex 存儲(chǔ)的值會(huì)丟失,localstorage 不會(huì)。
注意:對(duì)于不變的數(shù)據(jù)確實(shí)可以用localstorage 可以代替vuex,但是當(dāng)兩個(gè)組件共用一個(gè)數(shù)據(jù)源(對(duì)象或數(shù)組)時(shí),如果其中一個(gè)組件改變了該數(shù)據(jù)源,希望另一個(gè)組件響應(yīng)該變化時(shí),localstorage 無(wú)法做到,原因就是區(qū)別 1。
Redux 和 Vuex 有什么區(qū)別,它們的共同思想
- Redux 和 Vuex 區(qū)別
Vuex 改進(jìn)了 Redux 中的Action 和Reducer 函數(shù),以 mutations 變化函數(shù)取代 Reducer,無(wú)需 switch,只需在對(duì)應(yīng)的mutation 函數(shù)里改變state 值即可
Vuex 由于 Vue 自動(dòng)重新渲染的特性,無(wú)需訂閱重新渲染函數(shù),只要生成新的State 即可
Vuex 數(shù)據(jù)流的順序是∶View 調(diào)用 store.commit 提交對(duì)應(yīng)的請(qǐng)求到 Store 中對(duì)應(yīng)的mutation 函數(shù)->store 改變(vue 檢測(cè)到數(shù)據(jù)變化自動(dòng)渲染)
通俗點(diǎn)理解就是,vuex 弱化 dispatch,通過(guò)commit 進(jìn)行 store 狀態(tài)的一次更變;取消了action 概念,不必傳入特定的 action 形式進(jìn)行指定變更;弱化reducer,基于 commit 參數(shù)直接對(duì)數(shù)據(jù)進(jìn)行轉(zhuǎn)變,使得框架更加簡(jiǎn)易;
- 共同思想
單—的數(shù)據(jù)源
變化可以預(yù)測(cè)
本質(zhì)上:redux 與vuex 都是對(duì)mvvm 思想的服務(wù),將數(shù)據(jù)從視圖中抽離的一種方案;
形式上:vuex 借鑒了 redux,將 store 作為全局的數(shù)據(jù)中心,進(jìn)行 mode 管理;
為什么要用 Vuex 或者 Redux
由于傳參的方法對(duì)于多層嵌套的組件將會(huì)非常繁瑣,并且對(duì)于兄弟組件間的狀態(tài)傳遞無(wú)能為力。我們經(jīng)常會(huì)采用父子組件直接引用或者通過(guò)事件來(lái)變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會(huì)導(dǎo)致代碼無(wú)法維護(hù)。
所以需要把組件的共享狀態(tài)抽取出來(lái),以一個(gè)全局單例模式管理。在這種模式下,組件樹構(gòu)成了一個(gè)巨大的"視圖",不管在樹的哪個(gè)位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為。
另外,通過(guò)定義和隔離狀態(tài)管理中的各種概念并強(qiáng)制遵守一定的規(guī)則,代碼將會(huì)變得更結(jié)構(gòu)化且易維護(hù)。
Vuex 有哪幾種屬性?
有五種,分別是 State、 Getter、Mutation 、Action、 Module
state => 基本數(shù)據(jù)(數(shù)據(jù)源存放地)
getters => 從基本數(shù)據(jù)派生出來(lái)的數(shù)據(jù)
mutations => 提交更改數(shù)據(jù)的方法,同步
actions => 像一個(gè)裝飾器,包裹mutations,使之可以異步。
modules => 模塊化Vuex
Vuex 和單純的全局對(duì)象有什么區(qū)別?
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)。改變 store 中的狀態(tài)的唯一途徑就是顯式地提交 (commit) mutation。這樣可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而能夠?qū)崿F(xiàn)一些工具幫助更好地了解我們的應(yīng)用。
為什么 Vuex 的 mutation 中不能做異步操作?
Vuex 中所有的狀態(tài)更新的唯一途徑都是 mutation,異步操作通過(guò) Action 來(lái)提交 mutation 實(shí)現(xiàn),這樣可以方便地跟蹤每一個(gè)狀態(tài)的變化,從而能夠?qū)崿F(xiàn)一些工具幫助更好地了解我們的應(yīng)用。
每個(gè)mutation 執(zhí)行完成后都會(huì)對(duì)應(yīng)到一個(gè)新的狀態(tài)變更,這樣 devtools 就可以打個(gè)快照存下來(lái),然后就可以實(shí)現(xiàn) time-travel 了。
如果mutation 支持異步操作,就沒(méi)有辦法知道狀態(tài)是何時(shí)更新的,無(wú)法很好的進(jìn)行狀態(tài)的追蹤,給調(diào)試帶來(lái)困難。
Vue3.0 有什么更新
- 監(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。
-
模板
作用域插槽,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 。 -
對(duì)象式的組件聲明方式
vue2.x 中的組件是通過(guò)聲明的方式傳入一系列 option , 和 TypeScript 的結(jié)合需要通過(guò)一些裝飾器的方式來(lái)做,雖然能實(shí)現(xiàn)功能,但是比較麻煩。
3.0 修改了組件的聲明方式, 改成了類式的寫法, 這樣使得和 TypeScript 的結(jié)合變得很容易
- 其它方面的更改
支持自定義渲染器,從而使得 weex 可以通過(guò)自定義渲染器的方式來(lái)擴(kuò)展,而不是直接 fork 源碼來(lái)改的方式。
支持 Fragment(多個(gè)根節(jié)點(diǎn))和 Protal(在 dom 其他部分渲染組建內(nèi)容)組件,針對(duì)一些特殊的場(chǎng)景做了處理。
基于 tree shaking 優(yōu)化,提供了更多的內(nèi)置功能。
defineProperty 和proxy 的區(qū)別
Vue 在 實(shí) 例 初 始 化 時(shí) 遍 歷 data 中 的 所 有 屬 性 , 并 使 用 Object.defineProperty 把這些屬性全部轉(zhuǎn)為 getter/setter。這樣當(dāng)追蹤數(shù)據(jù)發(fā)生變化時(shí),setter 會(huì)被自動(dòng)調(diào)用。
Object.defineProperty 是 ES5 中一個(gè)無(wú)法 shim 的特性,這也就是 Vue 不支持 IE8 以及更低版本瀏覽器的原因。
但是這樣做有以下問(wèn)題:
-
添加或刪除對(duì)象的屬性時(shí),Vue 檢測(cè)不到。因?yàn)樘砑踊騽h除的對(duì)象沒(méi) 有 在 初 始 化 進(jìn) 行 響 應(yīng) 式 處 理 , 只 能 通 過(guò) $set 來(lái) 調(diào) 用 Object.defineProperty()處理。
-
無(wú)法監(jiān)控到數(shù)組下標(biāo)和長(zhǎng)度的變化。
Vue3 使用 Proxy 來(lái)監(jiān)控?cái)?shù)據(jù)的變化。Proxy 是 ES6 中提供的功能,其作用為:用于定義基本操作的自定義行為(如屬性查找,賦值,枚舉,函數(shù)調(diào)用等)。相對(duì)于 Object.defineProperty(),其有以下特點(diǎn):
-
Proxy 直接代理整個(gè)對(duì)象而非對(duì)象屬性,這樣只需做一層代理就可以監(jiān)聽同級(jí)結(jié)構(gòu)下的所有屬性變化,包括新增屬性和刪除屬性。
-
Proxy 可以監(jiān)聽數(shù)組的變化。
Vue3.0 為什么要用 proxy?
在 Vue2 中, 0bject.defineProperty 會(huì)改變?cè)紨?shù)據(jù),而 Proxy是創(chuàng)建對(duì)象的虛擬表示,并提供 set 、get 和 deleteProperty 等處理器,這些處理器可在訪問(wèn)或修改原始對(duì)象上的屬性時(shí)進(jìn)行攔截,有以下特點(diǎn)∶
不需用使用 Vue.$set 或 Vue.$delete 觸發(fā)響應(yīng)式。
全方位的數(shù)組變化檢測(cè),消除了Vue2 無(wú)效的邊界情況。
支持 Map,Set,WeakMap 和 WeakSet。
Proxy 實(shí)現(xiàn)的響應(yīng)式原理與 Vue2 的實(shí)現(xiàn)原理相同,實(shí)現(xiàn)方式大同小異∶
-
get 收集依賴
-
Set、delete 等觸發(fā)依賴
-
對(duì)于集合類型,就是對(duì)集合對(duì)象的方法做一層包裝:原方法執(zhí)行后執(zhí)行依賴相關(guān)的收集或觸發(fā)邏輯。
虛擬 DOM 的解析過(guò)程
首先對(duì)將要插入到文檔中的 DOM 樹結(jié)構(gòu)進(jìn)行分析,使用 js 對(duì)象將其表示出來(lái),比如一個(gè)元素對(duì)象,包含 TagName、props 和 Children這些屬性。然后將這個(gè) js 對(duì)象樹給保存下來(lái),最后再將 DOM 片段插入到文檔中。
當(dāng)頁(yè)面的狀態(tài)發(fā)生改變,需要對(duì)頁(yè)面的 DOM 的結(jié)構(gòu)進(jìn)行調(diào)整的時(shí)候,首先根據(jù)變更的狀態(tài),重新構(gòu)建起一棵對(duì)象樹,然后將這棵新的對(duì)象樹和舊的對(duì)象樹進(jìn)行比較,記錄下兩棵樹的的差異。
最后將記錄的有差異的地方應(yīng)用到真正的 DOM 樹中去,這樣視圖就更新了。
diff算法的原理
在新老虛擬DOM 對(duì)比時(shí):
首先,對(duì)比節(jié)點(diǎn)本身,判斷是否為同一節(jié)點(diǎn),如果不為相同節(jié)點(diǎn),則刪除該節(jié)點(diǎn)重新創(chuàng)建節(jié)點(diǎn)進(jìn)行替換
如果為相同節(jié)點(diǎn),進(jìn)行patchVnode,判斷如何對(duì)該節(jié)點(diǎn)的子節(jié)點(diǎn)進(jìn)行處理,先判斷一方有子節(jié)點(diǎn)一方?jīng)]有子節(jié)點(diǎn)的情況(如果新的 children 沒(méi)有子節(jié)點(diǎn),將舊的子節(jié)點(diǎn)移除)
比較如果都有子節(jié)點(diǎn),則進(jìn)行updateChildren,判斷如何對(duì)這些新
老節(jié)點(diǎn)的子節(jié)點(diǎn)進(jìn)行操作(diff 核心)。
匹配時(shí),找到相同的子節(jié)點(diǎn),遞歸比較子節(jié)點(diǎn)
在diff 中,只對(duì)同層的子節(jié)點(diǎn)進(jìn)行比較,放棄跨級(jí)的節(jié)點(diǎn)比較,使得時(shí)間復(fù)雜從O(n3)降低值O(n),也就是說(shuō),只有當(dāng)新舊children都為多個(gè)子節(jié)點(diǎn)時(shí)才需要用核心的Diff 算法進(jìn)行同層級(jí)比較。
Vue 中key 的作用
vue 中 key 值的作用可以分為兩種情況來(lái)考慮:
第一種情況是 v-if 中使用 key。由于 Vue 會(huì)盡可能高效地渲染元素,通常會(huì)復(fù)用已有元素而不是從頭開始渲染。因此當(dāng)使用 v-if 來(lái)實(shí)現(xiàn)元素切換的時(shí)候,如果切換前后含有相同類型的元素,那么這個(gè)元素就會(huì)被復(fù)用。如果是相同的 input 元素,那么切換前后用戶的輸入不會(huì)被清除掉,這樣是不符合需求的。因此可以通過(guò)使用 key 來(lái)唯一的標(biāo)識(shí)一個(gè)元素,這個(gè)情況下,使用 key 的元素不會(huì)被復(fù)用。這個(gè)時(shí)候 key 的作用是用來(lái)標(biāo)識(shí)一個(gè)獨(dú)立的元素。
第二種情況是 v-for 中使用 key。用 v-for 更新已渲染過(guò)的元素列表時(shí),它默認(rèn)使用“就地復(fù)用”的策略。如果數(shù)據(jù)項(xiàng)的順序發(fā)生了改變,Vue 不會(huì)移動(dòng) DOM 元素來(lái)匹配數(shù)據(jù)項(xiàng)的順序,而是簡(jiǎn)單復(fù)用此處的每個(gè)元素。因此通過(guò)為每個(gè)列表項(xiàng)提供一個(gè) key 值,來(lái)以便 Vue跟蹤元素的身份,從而高效的實(shí)現(xiàn)復(fù)用。這個(gè)時(shí)候 key 的作用是為了高效的更新渲染虛擬 DOM。
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),比遍歷方式更快文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-461851.html
下期更新React部分
??文末福利:搜索公眾號(hào)【前端二次元】回復(fù)關(guān)鍵字「前端資料」,領(lǐng)取前端系統(tǒng)課程,涵蓋前端所有內(nèi)容。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-461851.html
到了這里,關(guān)于2023前端面試上岸手冊(cè)——VUE部分的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!