Vue3常見API:
- reactive:
reactive返回proxy對(duì)象,如果發(fā)現(xiàn)被訪問的屬性是引用類型,還會(huì)用reactive遞歸處理,屬性是可以被修改的。- shallowReactive:
shallowReactive返回proxy對(duì)象,和reactive的區(qū)別是只建立第一層為響應(yīng)式,如果發(fā)現(xiàn)被訪問的屬性是引用類型也不會(huì)進(jìn)行遞歸代理,屬性是可以被修改的。- readonly:
readonly返回proxy對(duì)象,如果發(fā)現(xiàn)被訪問的屬性是引用類型會(huì)進(jìn)行遞歸處理,但是屬性是只讀的,不能修改??梢宰鰌rops傳遞給子組件使用。- shallowReadonly:
shallowReadonly返回proxy對(duì)象,shallowReadonly 僅對(duì)對(duì)象的第一次層屬性進(jìn)行只讀處理,對(duì)于嵌套的對(duì)象或數(shù)組,它不會(huì)進(jìn)行只讀保護(hù)。
Vue3 新特性有哪些?
1、優(yōu)化響應(yīng)式系統(tǒng):
- 使用ES6的 Proxy配合Reflect 代替vue2的 Object.defineProperty ,不僅可以更高效地追蹤依賴關(guān)系,還解決了 Vue 2 中響應(yīng)式無法追蹤數(shù)組長(zhǎng)度變化和對(duì)象屬性添加的問題。
2、更好的支持 typeScript:
Vue3在源碼使用了 TypeScript,可以為開發(fā)者 提供更好的類型檢查和類型推斷。3、新增 Composition API:
- Composition API 提供了一種新的代碼組織和復(fù)用方式,它允許開發(fā)者將邏輯拆分成獨(dú)立的函數(shù)(稱為“組合函數(shù)”),并在setup中按需使用。這使得代碼更加模塊化,易于測(cè)試和維護(hù),特別是在大型項(xiàng)目中。
4、新增組件:
- Fragment: template 模板可以有多個(gè)根元素。
- Teleport 傳送門,允許Teleport中的內(nèi)容傳送到任意的 DOM 中。
- Suspense 用于處理異步組件的加載狀態(tài),提供了更好的用戶體驗(yàn)。
5、支持多個(gè)根節(jié)點(diǎn)
6、Tree-shaking:支持搖樹優(yōu)化:
Vue3.x中的核心API都支持tree-shaking,這些API都是通過包引入的方式而不是直接掛載在vue原型上,只會(huì)對(duì)使用到的功能或特性進(jìn)行打包(按需打包),這意味著更多的功能和更小的體積。7、diff算法的優(yōu)化:
- vue2中虛擬dom進(jìn)行全量對(duì)比
- Vue 3 的虛擬 DOM Diff 算法通過引入靜態(tài)標(biāo)記(PatchFlag)和靜態(tài)提升等優(yōu)化手段,顯著提高了渲染性能。PatchFlag 允許 Vue 在比較新舊節(jié)點(diǎn)時(shí)跳過未發(fā)生變化的靜態(tài)部分,只關(guān)注動(dòng)態(tài)變化的部分。靜態(tài)提升則通過復(fù)用未發(fā)生變化的節(jié)點(diǎn),減少了不必要的節(jié)點(diǎn)創(chuàng)建和銷毀操作。
8、移除了一些功能:
- 移除v-on的鍵盤修飾符,鼓勵(lì)開發(fā)者使用鍵盤事件(如 keyCode 或 key)來替代;
- 移除過濾器filter。
Vue3性能提升主要是通過哪幾方面體現(xiàn)的?
1、編譯階段優(yōu)化
- diff算法優(yōu)化
vue3在diff算法中相比vue2增加了靜態(tài)標(biāo)記Patchflag,在編譯template模板時(shí),給vnode添加的一個(gè)PatchFlag標(biāo)識(shí)信息,這個(gè)標(biāo)識(shí)信息可以反映哪些vnode具有動(dòng)態(tài)綁定的屬性或子節(jié)點(diǎn),在與上次的節(jié)點(diǎn)進(jìn)行對(duì)比時(shí)候,Vue會(huì)利用patchFlag來高效地識(shí)別出哪些是需要更新的節(jié)點(diǎn),并僅對(duì)這些節(jié)點(diǎn)進(jìn)行對(duì)比和更新操作。這樣可以避免不必要的DOM操作,從而提升渲染性能。- 靜態(tài)提升
Vue3會(huì)對(duì)不參與更新的元素做靜態(tài)提升,只會(huì)在初始化的時(shí)候創(chuàng)建一次,重新渲染時(shí)直接復(fù)用。在初始化的時(shí)候會(huì)對(duì)該靜態(tài)元素打上特殊靜態(tài)標(biāo)記,值為-1,表示靜態(tài)提升,(特殊標(biāo)志是負(fù)整數(shù)表示永遠(yuǎn)不會(huì)用于 Diff),做了靜態(tài)提升后,未參與更新的元素,被放置在render 函數(shù)外,每次渲染的時(shí)候直接復(fù)用即可。
沒做靜態(tài)提升之前,未參與更新的元素也在render函數(shù)內(nèi)部,會(huì)重復(fù)創(chuàng)建階段。詳細(xì):
- Vue 2在對(duì)比較新舊節(jié)點(diǎn)時(shí),會(huì)檢查每個(gè)節(jié)點(diǎn)是否是靜態(tài)的,如果是靜態(tài)的會(huì)跳過這個(gè)節(jié)點(diǎn)及其子節(jié)點(diǎn)的比較。雖然會(huì)跳過靜態(tài)節(jié)點(diǎn)的比較,但是Vue2并沒有做靜態(tài)提升,靜態(tài)根節(jié)點(diǎn)還是在render函數(shù)內(nèi),所以這些靜態(tài)根節(jié)點(diǎn)每次都會(huì)被重新創(chuàng)建渲染。
- vue3在diff算法中相比vue2增加了靜態(tài)標(biāo)記Patchflag,在編譯template模板時(shí),給vnode添加的一個(gè)PatchFlag標(biāo)識(shí)信息,這個(gè)標(biāo)識(shí)信息可以反映哪些vnode具有動(dòng)態(tài)綁定的屬性或子節(jié)點(diǎn),在與上次的節(jié)點(diǎn)進(jìn)行對(duì)比時(shí)候,Vue會(huì)利用patchFlag來高效地識(shí)別出哪些是需要更新的節(jié)點(diǎn),并僅對(duì)這些節(jié)點(diǎn)進(jìn)行對(duì)比和更新操作。對(duì)不參與更新的元素做靜態(tài)提升,只會(huì)在初始化的時(shí)候創(chuàng)建一次,重新渲染時(shí)直接復(fù)用。在初始化的時(shí)候會(huì)對(duì)該靜態(tài)元素打上特殊靜態(tài)標(biāo)記,值為-1,表示靜態(tài)提升,這樣可以避免不必要的DOM操作,從而提升渲染性能。
2、源碼體積優(yōu)化
- 相比Vue2,Vue3整體體積變小了,除了移出一些不常用的API,最重要的是優(yōu)化了對(duì) Tree shanking的支持。Vue3中的核心API都支持tree-shaking,這些API都需通過包引入的方式才能使用,只會(huì)對(duì)使用到的功能或特性進(jìn)行打包(按需打包),這意味著更多的功能和更小的體積。
3、響應(yīng)式系統(tǒng)優(yōu)化
- vue2主要采用 object.defineProperty來劫持對(duì)象屬性,通過進(jìn)行深度遍歷所有屬性,給每個(gè)屬性添加getter和setter,實(shí)現(xiàn)響應(yīng)式攔截。
- vue3采用proxy重寫了響應(yīng)式系統(tǒng),因?yàn)閜roxy是對(duì)整個(gè)對(duì)象進(jìn)行監(jiān)聽的,所以無需遞歸遍歷對(duì)象的屬性。
- 可以監(jiān)聽到屬性的添加、刪除操作。
- 可以監(jiān)聽到數(shù)組的索引和數(shù)組length屬性的操作。
4、事件優(yōu)化
- 在Vue2中,每次渲染時(shí)都會(huì)重新創(chuàng)建事件處理函數(shù),即使是相同的事件處理邏輯。
- 在Vue3中,引入了緩存事件處理函數(shù)的概念,它會(huì)將事件處理函數(shù)在編譯階段緩存起來,重新渲染的時(shí)候直接復(fù)用。
// 靜態(tài)提升例子:
// Vue2的靜態(tài)節(jié)點(diǎn)
render(){
return createVNode("div", null, "Hello World")
}
// Vue3的靜態(tài)節(jié)點(diǎn)
const staticLifting = createVNode("div", null, "Hello World")
function render(){
// 直接使用 staticLifting 即可
}
<button @click="i++">plus</button>
// vue2
render(ctx){
return createVNode("button", {
onClick: function($event){
ctx.i++;
}
})
}
// vue3
render(ctx, _cache){
return createVNode("button", {
onClick: cache[0] || (cache[0] = ($event) => (ctx.i++))
})
}
Vue3和Vue2的區(qū)別?
1. 響應(yīng)式數(shù)據(jù)的監(jiān)聽方式不同:
- Vue2 的響應(yīng)式數(shù)據(jù)監(jiān)聽是采用ES5的Object.definePropert() 對(duì)數(shù)據(jù)進(jìn)行劫持的;
- Vue3 中使用ES6的Proxy()對(duì)數(shù)據(jù)進(jìn)行代理劫持。
- 相比Object.definePropert(),ES6的Proxy()有以下優(yōu)點(diǎn):
- Proxy代理監(jiān)聽的是整個(gè)對(duì)象,不需要通過遞歸+遍歷為對(duì)象屬性設(shè)置getter和setter進(jìn)行劫持,提高了性能;
- Proxy可以監(jiān)聽到對(duì)象屬性的動(dòng)態(tài)添加和刪除;
- 可以監(jiān)聽到數(shù)組的索引和數(shù)組length屬性的操作;
- 如果對(duì)象的屬性也是對(duì)象的話,只有在被訪問的時(shí)候才會(huì)進(jìn)行遞歸響應(yīng)觀測(cè),提高了性能;
2. Vue3新增了組合式(Composition API):
- Vue2是使用options API的,options API內(nèi)部的邏輯點(diǎn)是碎片化的,一個(gè)功能邏輯會(huì)出現(xiàn)在不同的位置,通過定義methods,computed,watch,data等options API,共同處理頁面邏輯。
- Vue3新增了Composition API(組合API),同時(shí)仍然兼容Vue2中的Options API。組合式API,組件根據(jù)邏輯功能來組織代碼的,一個(gè)功能所定義的所有 API 會(huì)放在一起,通過函數(shù)分割復(fù)用代碼,將其封裝成一個(gè)hooks,hooks需要在setup函數(shù)中使用。
3. 定義數(shù)據(jù)變量和方法不同:
- vue2是在對(duì)應(yīng)的選項(xiàng)中定義變量、方法、計(jì)算屬性等。在data選項(xiàng)中定義響應(yīng)式數(shù)據(jù),在method中定義方法。
- Vue3需要從vue中導(dǎo)出reactive、ref、computed等API,這些API需要在一個(gè)setup鉤子函數(shù)中使用,通過使用reactive、ref來定義響應(yīng)式數(shù)據(jù)。
4. template模板支持:
- Vue3 template 支持多個(gè)根節(jié)點(diǎn);
- Vue2 template 只能支持單個(gè)根節(jié)點(diǎn)。
5. 生命周期函數(shù):
- vue2的生命周期是選項(xiàng)式的,,主要生命周期函數(shù)是beforeCreate、created、beforeMount、Mounted、beforeUpdate、updated、beforeDestory、destroyed;
- vue3的生命周期是組合式的,需要通過包引入的方式才可以時(shí)使用;
- vue3用setup鉤子取代了beforeCreate和created鉤子函數(shù),setup函數(shù)在beforeCreate之前調(diào)用;
- vue3將beforeDestory和destroyed這兩個(gè)鉤子函數(shù)的名稱改為onBeforeUnmount和onUnmounted。
6. Vue實(shí)例的創(chuàng)建方式不同:
- Vue2是通過new一個(gè)Vue實(shí)例來創(chuàng)建Vue實(shí)例對(duì)象的;
- Vue3需要引入createApp方法,然后調(diào)用該方法創(chuàng)建Vue對(duì)象。
// Vue2
import Vue from 'vue';
import App from './App.vue'
new Vue({
render: h => h(App)
}).$mount('#app');
// Vue3
import {createApp} from 'vue';
import App from './App.vue'
createApp(App).mount('#app')
Options Api與Composition Api的區(qū)別?
代碼組織:
1、Options Api:選項(xiàng)式API內(nèi)部的邏輯點(diǎn)是碎片化的,一個(gè)功能邏輯會(huì)出現(xiàn)在不同的位置,通過定義methods,computed,watch,data等選項(xiàng)API,共同處理頁面邏輯。當(dāng)組件變得復(fù)雜時(shí),會(huì)降低代碼的可讀性和可維護(hù)性。
2、Composition API(組合式API),使用 ref、reactive、computed、watch 等函數(shù)來替代 Options API 中的對(duì)應(yīng)選項(xiàng)。組件根據(jù)邏輯功能來組織代碼,通過函數(shù)分割復(fù)用代碼,一個(gè)功能所定義的所有 API 會(huì)放在一起,這使得代碼更加集中和模塊化,易于閱讀和維護(hù)。( 更加的 高內(nèi)聚,低耦合 )。邏輯復(fù)用:
- 在vue2.0中,是通過mixin來實(shí)現(xiàn)邏輯復(fù)用的,當(dāng)使用多個(gè)mixin會(huì)存在兩個(gè)非常明顯的問題:命名沖突、數(shù)據(jù)來源不清晰
- Composition Api是通過函數(shù)復(fù)用,通過將相同邏輯功能的代碼封裝成一個(gè)函數(shù),這些代碼片段可以包含狀態(tài)、計(jì)算屬性、方法等,可以在多個(gè)組件中重復(fù)使用,而不會(huì)引起命名沖突或數(shù)據(jù)來源不明確的問題。
總結(jié):
- 在代碼組織和邏輯復(fù)用方面,Composition API是優(yōu)于Options API
- 因?yàn)镃omposition API幾乎是函數(shù),會(huì)有更好的類型推斷;
- Composition API 對(duì) tree-shaking 更友好,從而生成更小的包;
- Composition API中見不到this的使用,減少了this指向不明的情況;
- 如果是小型組件,可以繼續(xù)使用Options API,也是十分友好的。
- 更好的測(cè)試,由于 Composition API 中的邏輯更加模塊化,它們也更易于進(jìn)行單元測(cè)試。
Vue3.0里為什么要用 Proxy API 替代 defineProperty API ?
1、vue2是采用 Object.defineProperty來監(jiān)聽對(duì)象的操作的,通過遍歷+遞歸的方式給每個(gè)屬性添加getter和setter進(jìn)行劫持。
存在以下的問題:
- 檢測(cè)不到對(duì)象屬性的添加和刪除,Vue提供了
delete刪除,但是這需要手動(dòng)調(diào)用,有額外的負(fù)擔(dān);
- 監(jiān)聽不到通過數(shù)組索引添加新屬性和數(shù)組length屬性的操作;
- 在初始化階段,需要對(duì)對(duì)象屬性進(jìn)行遍歷監(jiān)聽,如果是嵌套對(duì)象,即使沒有被訪問到,也會(huì)被遞歸監(jiān)聽,當(dāng)對(duì)象過深的時(shí)候會(huì)造成性能問題。
2、Proxy監(jiān)聽的是整個(gè)對(duì)象的操作:
- Proxy 創(chuàng)建了一個(gè)對(duì)象的代理,這個(gè)代理可以攔截針對(duì)對(duì)象的任何操作(如讀取、賦值、函數(shù)調(diào)用等),而不僅僅是屬性訪問。因此,它可以更全面地追蹤對(duì)象的變化。
- Proxy不需要預(yù)先知道要監(jiān)聽哪些屬性,它可以動(dòng)態(tài)地處理對(duì)象的屬性添加和刪除。
- 可以監(jiān)聽到數(shù)組的索引和數(shù)組length屬性的操作;
- 對(duì)于嵌套對(duì)象,只有在被訪問到的時(shí)候才會(huì)對(duì)其進(jìn)行遞歸響應(yīng)觀測(cè),這有助于提高性能;
- Proxy提供了多達(dá)13種攔截方法,這些方法可以覆蓋對(duì)象的各種操作,提供了更大的靈活性和控制力。
Proxy 只會(huì)代理對(duì)象的第一層,那么 Vue3 又是怎樣處理這個(gè)問題的呢?
當(dāng)訪問到深層嵌套對(duì)象的話,會(huì)觸發(fā)getter,會(huì)判斷當(dāng)前 Reflect.get 的返回值是否是已經(jīng)代理過的對(duì)象,如果是一個(gè)普通對(duì)象則再使用 reactive 方法做代理, 這樣就實(shí)現(xiàn)了深度觀測(cè)。
說說Vue 3.0中Treeshaking特性?舉例說明一下?
1、什么是Tree shaking?
Tree shaking 是一種通過清除多余代碼方式來優(yōu)化項(xiàng)目打包體積的技術(shù),專業(yè)術(shù)語叫 (Dead code elimination)簡(jiǎn)單來講,就是在保持代碼運(yùn)行結(jié)果不變的前提下,去除無用的代碼
2、Tree shaking原理?
Tree shaking是基于ES6模塊化方案 (import與exports),模塊之間的依賴關(guān)系是高度確定和靜態(tài)的,與運(yùn)行狀態(tài)無關(guān),可以進(jìn)行可靠的靜態(tài)分析。在打包過程中 靜態(tài)分析 模塊之間的導(dǎo)入導(dǎo)出,確定哪些導(dǎo)出的模塊沒有被引用并打上標(biāo)記,最終將其刪除。
- 在Vue2中,很多API 都是掛載在Vue原型上的,程序無法檢測(cè)到該對(duì)象的哪些屬性在代碼中被使用到。
- 而Vue3優(yōu)化了對(duì)tree shaking的支持,所有的核心API都支持Tree Shaking, 需要通過包引入的方式才可以使用。如果您不使用其某些功能,它們將不會(huì)包含在您的基礎(chǔ)包中。
Tree shaking無非就是做了兩件事:
- 編譯階段利用ES6 Module判斷哪些模塊已經(jīng)加載
- 判斷哪些模塊和變量未被使用或者引用,進(jìn)而刪除對(duì)應(yīng)代碼
3、Tree shaking作用(好處)?- 減少程序體積(更?。?;
- 減少程序執(zhí)行時(shí)間(更快);
- 便于將來對(duì)程序架構(gòu)進(jìn)行優(yōu)化(更友好)。
watch 和 watchEffect 的區(qū)別?
watch 和 watchEffect 都是監(jiān)聽器,watchEffect 是一個(gè)副作用函數(shù)。
它們之間的區(qū)別有:
- watch :既要指明監(jiān)視的數(shù)據(jù)源,也要指明監(jiān)視的回調(diào)。
- watchEffect 可以自動(dòng)監(jiān)聽數(shù)據(jù)源作為依賴。不用指明監(jiān)視哪個(gè)數(shù)據(jù),監(jiān)視的回調(diào)中用到哪個(gè)數(shù)據(jù),那就監(jiān)視哪個(gè)數(shù)據(jù)。
- watch 可以訪問改變之前和之后的值,watchEffect 只能獲取改變后的值。
- watch 依賴的屬性改變后才會(huì)執(zhí)行,初始化的時(shí)候不會(huì)立即執(zhí)行,但是可以通過 watch 的配置項(xiàng) immediate 來改變;而 watchEffect 初始化的時(shí)候會(huì)立即執(zhí)行。
watchEffect有點(diǎn)像 computed :- computed 注重的計(jì)算出來的值(回調(diào)函數(shù)的返回值), 所以必須要寫返回值。
- watcheffect注重的是過程(回調(diào)函數(shù)的函數(shù)體),所以不用寫返回值。
ref與reactive的區(qū)別?
- ref與reactive 是 Vue3 新推出的主要 API 之一,主要用于創(chuàng)建響應(yīng)式數(shù)據(jù)。
- ref 函數(shù)創(chuàng)建的響應(yīng)式數(shù)據(jù),在模板中可以直接被使用,在 JS 中需要通過 .value 的形式才能使用。
- ref 函數(shù)可以接收原始數(shù)據(jù)類型與引用數(shù)據(jù)類型。
- reactive 函數(shù)只能接收引用數(shù)據(jù)類型。
- ref 底層還是使用 reactive 來做,ref 是在 reactive 上在進(jìn)行封裝的,增強(qiáng)了其能力,使它支持了對(duì)原始數(shù)據(jù)類型的處理。
- 在 Vue3 中 reactive 能做的,ref 也能做,reactive 不能做的,ref 也能做。
- ref還能獲取組件的實(shí)例。
setup() 函數(shù)特性:
- setup 函數(shù)有兩個(gè)參數(shù):(props、context(包含attrs、slots、emit));
- setup函數(shù)在 生命周期 beforeCreate 和 created 兩個(gè)鉤子函數(shù)之前執(zhí)行;
- setup函數(shù)中不能使用this,因?yàn)?setup 函數(shù)執(zhí)行時(shí),組件實(shí)例尚未被創(chuàng)建,Vue 為了避免我們錯(cuò)誤的使用,直接將 setup函數(shù)中的this修改成了undefined;
- 與模板一起使用:需要返回一個(gè)對(duì)象,將需要在模板中使用的變量和方法return 出去,不然無法在模板中使用
- setup函數(shù)只能是同步的不能是異步的。
- setup函數(shù)默認(rèn)暴露組件內(nèi)部的屬性和方法。
- setup 函數(shù)中的 props 是響應(yīng)式的,它是一個(gè)reactive。不能使用ES6的解構(gòu)來結(jié)構(gòu)props,因?yàn)樗鼤?huì)消除 props 的響應(yīng)性。如果需要解構(gòu) props,可以通過使用 setup 函數(shù)中的 toRefs 來完成解構(gòu)操作:
import {toRefs} from 'vue'
setup(props) {
const { name } = toRefs(props);
console.log(name.value);
onMounted(() => {
console.log('name: ' + props.name);
})
}
script setup 是干啥的?
scrtpt setup 是 vue3.2 的語法糖,簡(jiǎn)化了組合式 API 的寫法,并且運(yùn)行性能更好。
script setup 語法糖的特點(diǎn):
- 屬性和方法無需返回,可以直接在template模板中使用。
- 引入組件的時(shí)候,會(huì)自動(dòng)注冊(cè),無需通過 components 手動(dòng)注冊(cè)。
- 使用 defineProps 接收父組件傳遞的值、defineEmits 定義自定義事件。
- 使用 useAttrs 獲取額外沒有在props配置的屬性,useSlots 獲取插槽。
- 默認(rèn)不會(huì)對(duì)外暴露任何屬性方法,內(nèi)部自動(dòng)調(diào)用exporse方法,如果有需要可使用 defineExpose 指定暴露的屬性、方法。
- defineOptions可以關(guān)閉屬性透?jìng)骱投x組件name值。
export const enum PatchFlags {
// 表示vnode具有動(dòng)態(tài)textContent的元素
TEXT = 1,
// 表示vnode具有動(dòng)態(tài)的class
CLASS = 1 << 1,
// 表示具有動(dòng)態(tài)的style
STYLE = 1 << 2,
// 表示具有動(dòng)態(tài)的非class和style的props
PROPS = 1 << 3,
// 表示props具有動(dòng)態(tài)的key,與CLASS、STYLE、PROPS沖突
FULL_PROPS = 1 << 4,
// 表示有監(jiān)聽事件(在同構(gòu)期間需要添加)
HYDRATE_EVENTS = 1 << 5,
// 表示vnode是個(gè)children順序不會(huì)改變的fragment
STABLE_FRAGMENT = 1 << 6,
// 表示children帶有key的fragment
KEYED_FRAGMENT = 1 << 7,
// 表示children沒有key的fragment
UNKEYED_FRAGMENT = 1 << 8,
// 表示vnode只需要非props的patch。例如只有標(biāo)簽中只有ref或指令
NEED_PATCH = 1 << 9,
// 表示vnode存在動(dòng)態(tài)的插槽。例如動(dòng)態(tài)的插槽名
DYNAMIC_SLOTS = 1 << 10,
// 表示用戶在模板的根級(jí)別存在注釋而創(chuàng)建的片段,這是一個(gè)僅用于開發(fā)的標(biāo)志,因?yàn)樽⑨屧谏a(chǎn)中被剝離
DEV_ROOT_FRAGMENT = 1 << 11,
// 以下都是一些特殊的flag,它們不能使用位運(yùn)算進(jìn)行匹配
// 表示vnode經(jīng)過靜態(tài)提升
HOISTED = -1,
// diff算法應(yīng)該退出優(yōu)化模式
BAIL = -2
}
vue3 reactive響應(yīng)式原理:
- vue3 響應(yīng)式主要是采用發(fā)布者訂閱者模式 + es6的 Proxy 代理實(shí)現(xiàn)的。
- 首先判斷目標(biāo)對(duì)象是否是一個(gè)對(duì)象,如果不是,直接警告提示。
- 判斷目標(biāo)對(duì)象有沒有被 Proxy 代理過,如果已經(jīng)被代理過了直接返回目標(biāo)對(duì)象。
- 否則調(diào)用createReactiveObject方法對(duì)目標(biāo)對(duì)象進(jìn)行 Proxy 代理,設(shè)置getter和setter進(jìn)行攔截。當(dāng) Proxy 對(duì)象中的屬性被訪問的時(shí)候,會(huì)觸發(fā)getter,此時(shí)會(huì)去獲取值,如果該值是對(duì)象且沒有被Proxy代理過的話,會(huì)遞歸調(diào)用reactive去實(shí)現(xiàn)對(duì)其進(jìn)行 Proxy 代理。在getter中會(huì)調(diào)用track收集effect;當(dāng) 對(duì)象的屬性被修改或者新增了屬性的是,會(huì)調(diào)用trigger去通過訂閱該屬性的effect做出相應(yīng)的更新;
- track方法的作用就是收集effect,收集的effect會(huì)保存在全局WeakMap結(jié)構(gòu)的targetMap對(duì)象中,其key值是需要代理的目標(biāo)對(duì)象,value值是也是一個(gè)depsMap對(duì)象,它是一個(gè) Map 結(jié)構(gòu)的對(duì)象,是用來保存代理對(duì)象的key值所對(duì)應(yīng)的所有effect,所有effect都是存儲(chǔ)在Set結(jié)構(gòu)的集合中的。
- trigger方法主要是通知屬性對(duì)應(yīng)的所有effect去更新,會(huì)根據(jù)代理對(duì)象target去targetMap中查找代理對(duì)象所對(duì)應(yīng)的depsMap,然后根據(jù)訪問的代理對(duì)象的key值去depsMap查找該key的所對(duì)應(yīng)的effect集合,然后遍歷去執(zhí)行每個(gè)effect里的更新方法,進(jìn)而做出相應(yīng)的更新操作。
- effect是一個(gè)包裝函數(shù),接收一個(gè)副作用函數(shù),它通常代表了一個(gè)需要響應(yīng)數(shù)據(jù)變化的操作。當(dāng)屬性被訪問的時(shí)候,會(huì)觸發(fā)getter,此時(shí)會(huì)將當(dāng)前激活的activeEffect收集到當(dāng)前屬性的effect收集器中,待收到數(shù)據(jù)發(fā)生變化的消息時(shí),會(huì)調(diào)用自身對(duì)應(yīng)配置下的更新方法重新進(jìn)行更新。
Vue3 ref原理:
ref利用了ES6的類的屬性訪問器原理,它有一個(gè)value屬性,用于保存ref的值,在構(gòu)造函數(shù)被創(chuàng)建的時(shí)候會(huì)在constructor獲取初始值,如果值是對(duì)象的話,會(huì)使用reactive進(jìn)行響應(yīng)代理。value被設(shè)置了get和set進(jìn)行監(jiān)聽,當(dāng)value被訪問的時(shí)候會(huì)觸發(fā)get,在get里調(diào)用track方法進(jìn)行依賴的收集。當(dāng)對(duì)value進(jìn)行賦值的時(shí)候會(huì)觸發(fā)set,在set里會(huì)調(diào)用trigger方法通知所有的依賴該屬性的方法也就是所有的effect進(jìn)行更新。如果修改的值是一個(gè)對(duì)象的話,會(huì)調(diào)用reactive來對(duì)其進(jìn)行響應(yīng)式處理。
為什么 ref 類型數(shù)據(jù),必須要通過 .value 訪問值呢?
a. 因?yàn)?ref 既可以處理基本數(shù)據(jù)類型的數(shù)據(jù),也可以處理對(duì)象類型的數(shù)據(jù),但是基本數(shù)據(jù)類型的數(shù)據(jù)無法通過 proxy 進(jìn)行代理。
b. 而 vue 結(jié)合ES6的類的屬性訪問器原理,通過 get value() 和 set value() 定義了兩個(gè)屬性函數(shù),通過主動(dòng)觸發(fā)這兩個(gè)函數(shù)的形式來進(jìn)行依賴收集和依賴觸發(fā)。
c. 所以我們必須通過 .value 來保證數(shù)據(jù)響應(yīng)式。
computed的原理:
- 計(jì)算屬性有函數(shù)式寫法和對(duì)象寫法,當(dāng)用戶傳入的是一個(gè)函數(shù)的時(shí)候,默認(rèn)使用的是getter只讀操作;當(dāng)用戶傳入的是一個(gè)對(duì)象時(shí),用戶可以設(shè)置getter,setter,此時(shí)計(jì)算屬性具備可讀可寫的能力。
- 在vue3中實(shí)現(xiàn)計(jì)算屬性,第一步就是處理、收集用戶設(shè)置的getter和setter;
- 然后實(shí)例化一個(gè)計(jì)算屬性的類,在類的構(gòu)造器中,會(huì)實(shí)例化一個(gè)effect,這個(gè)effect接收用戶的設(shè)置的getter和一個(gè)調(diào)度函數(shù),用于計(jì)算計(jì)算屬性的值和更新計(jì)算屬性的_dirty屬性。計(jì)算屬性的effect使用的是lazy配置,在初始化的時(shí)候是不會(huì)立即執(zhí)行去獲取值的,因?yàn)橐紤]到計(jì)算屬性是否有被使用。
- 計(jì)算屬性類中有一個(gè)_dirty屬性,默認(rèn)為true,用于判斷計(jì)算屬性是否需要重新計(jì)算;還有一個(gè)_value屬性,利用ES6類的屬性訪問器原理,被設(shè)置了get和set進(jìn)行監(jiān)聽,當(dāng)計(jì)算屬性被修改時(shí),會(huì)觸發(fā)set,在set中只是簡(jiǎn)單的執(zhí)行用戶設(shè)置的setter;當(dāng)計(jì)算屬性value被訪問的時(shí)候會(huì)觸發(fā)get,在get里會(huì)調(diào)用effect的run方法也就是用戶設(shè)置的getter去獲取值,此時(shí)會(huì)訪問依賴屬性的值,依賴的屬性會(huì)將當(dāng)前活躍的計(jì)算屬性的effect收集起來。在get中還會(huì)將_dirty設(shè)置為false,如果依賴屬性沒有發(fā)生變化是不需要重新計(jì)算值的。
- 當(dāng)依賴的屬性發(fā)生變化的時(shí)候,會(huì)去遍歷該屬性所有的effect,此時(shí)會(huì)執(zhí)行計(jì)算屬性effect的調(diào)度函數(shù),將_dirty設(shè)置為true,當(dāng)計(jì)算屬性再次被訪問的時(shí)候,此時(shí)的_dirty為true,會(huì)重新計(jì)算值。