【總結(jié)】1231- 記一次 Vue2 遷移 Vue3 的實(shí)踐總結(jié)
大廠技術(shù)??堅(jiān)持周更??精選好文
一、Vue3
Vue3中文文檔[1]
Vue3是什么,與Vue2區(qū)別(What)
Performance:性能更強(qiáng)。Tree shaking support:可以將無用模塊“剪輯”,僅打包需要的。Composition API:組合式APIFragment, Teleport, Suspense:“碎片”,Teleport即Protal傳送門,“懸念”Better TypeScript support:更優(yōu)秀的Ts支持Custom Renderer API:暴露了自定義渲染API
為什么要大版本迭代 (Why)
主流瀏覽器對(duì)新的JavaScript語(yǔ)言特性的普遍支持。 當(dāng)前Vue代碼庫(kù)隨著時(shí)間的推移而暴露出來的設(shè)計(jì)和體系架構(gòu)問題。
他是如何提升的(How)
響應(yīng)式系統(tǒng)提升: 使用Proxy提升了響應(yīng)式的性能和功能 編譯優(yōu)化: 標(biāo)記和提升所有的靜態(tài)節(jié)點(diǎn),diff時(shí)只需要對(duì)比動(dòng)態(tài)節(jié)點(diǎn)內(nèi)容 事件緩存: 提供了事件緩存對(duì)象cacheHandlers,無需重新創(chuàng)建函數(shù)直接調(diào)用緩存的事件回調(diào) 打包和體積優(yōu)化: 按需引入,Tree shaking支持(ES Module)
二、編碼
全局API
【新增】createApp: 入口文件(main.ts)掛載方式
import?{?createApp?}?from?"vue";
import?App?from?"./App.vue";
//?Vue2
new?Vue({
???render:?(h)?=>?h(App)
}).$mount("#app");
//?Vue3
createApp(App)
??.use(**)
??.mount("#app");
【修改】
| 2.x 全局 API | 3.x 實(shí)例 API (app) |
|---|---|
| Vue.config | app.config |
| Vue.config.productionTip | 無 |
| Vue.config.ignoredElements | app.config.isCustomElement |
| Vue.component | app.component |
| Vue.directive | app.directive |
| Vue.mixin | app.mixin |
| Vue.use | app.use |
| Vue.prototype | app.config.globalProperties |
config.ignoredElements替換為config.isCustomElement
引入此配置選項(xiàng)的目的是支持原生自定義元素,因此重命名可以更好地傳達(dá)它的功能,新選項(xiàng)還需要一個(gè)比舊的 string/RegExp 方法提供更多靈活性的函數(shù):
//?Vue2
Vue.config.ignoredElements?=?[
??//?用一個(gè)?`RegExp`?忽略所有“ion-”開頭的元素
??//?僅在?2.5+?支持
??/^ion-/
]
//?Vue3
const?app?=?Vue.createApp({})
app.config.isCustomElement?=?tag?=>?tag.startsWith('ion-')
Vue.prototype 替換為 config.globalProperties
在Vue 2中,Vue.prototype通常用于添加可在所有組件中訪問的屬性。
Vue 3中的等效項(xiàng)是config.globalProperties。在實(shí)例化應(yīng)用程序內(nèi)的組件時(shí),將復(fù)制這些屬性
//?Vue2
Vue.prototype.$http?=?()?=>?{}
//?Vue3
const?app?=?Vue.createApp({})
app.config.globalProperties.$http?=?()?=>?{}
生命周期
| 2.0生命周期 | 3.0生命周期 |
|---|---|
| beforeCreate(組件創(chuàng)建之前) | setup() |
| created(組件創(chuàng)建完成) | setup() |
| beforeMount(組件掛載之前) | onBeforeMount(組件掛載之前) |
| mounted(組件掛載完成) | onMounted(組件掛載完成) |
| beforeUpdate(數(shù)據(jù)更新,虛擬DOM打補(bǔ)丁之前) | onBeforeUpdate(數(shù)據(jù)更新,虛擬DOM打補(bǔ)丁之前) |
| updated(數(shù)據(jù)更新,虛擬DOM渲染完成) | onUpdated(數(shù)據(jù)更新,虛擬DOM渲染完成) |
| beforeDestroy(組件銷毀之前) | onBeforeUnmount(組件銷毀之前) |
| destroyed(組件銷毀之后) | onUnmounted(組件銷毀之后) |
| activated(被 keep-alive 緩存的組件激活時(shí)調(diào)用) | onActivated(被激活時(shí)執(zhí)行) |
| deactivated(被 keep-alive 緩存的組件停用時(shí)調(diào)用) | onDeactivated(比如從 A 組件,切換到 B 組件,A 組件消失時(shí)執(zhí)行) |
| errorCaptured(當(dāng)捕獲一個(gè)來自子孫組件的錯(cuò)誤時(shí)被調(diào)用) | onErrorCaptured(當(dāng)捕獲一個(gè)來自子孫組件的異常時(shí)激活鉤子函數(shù)) |
新特性
Options API => Composition API


setup()
import?{toRefs}?from?'vue'
export?default?{
????name:?'demo',
????props:{
??????name:?String,
????},
????// setup()作為在組件內(nèi)使用Composition API的入口點(diǎn)。
????//?執(zhí)行時(shí)機(jī)是在beforeCreate和created之間,不能使用this獲取組件的其他變量,
????//?而且不能是異步。setup返回的對(duì)象和方法,都可以在模版中使用。
????setup(props,?context){
??????//?這里需要使用toRefs來進(jìn)行解構(gòu)
??????//?這里的props與vue2基本一致,當(dāng)然這里的name也可以直接在template中使用
??????const?{?name?}=toRefs(props);
??????console.log(name.value);
??????
??????//?context是一個(gè)上下文對(duì)象
??????//【從原來?2.x?中?this?選擇性地暴露了一些?property(attrs/emit/slots)】
??????//?屬性,同vue2的?$attrs
??????console.log(context.attrs);
??????//?插槽
??????console.log(context.slots);
??????//?事件,同vue2的?$emit
??????console.log(context.emit);
??????//?生命周期鉤子
??????onMounted(()?=>?{})
??}
}
注意點(diǎn):
注意 props對(duì)象是響應(yīng)式的,watchEffect或watch會(huì)觀察和響應(yīng)props的更新,不要解構(gòu)props對(duì)象,那樣會(huì)使其失去響應(yīng)性attrs和slots都是內(nèi)部組件實(shí)例上對(duì)應(yīng)項(xiàng)的代理,可以確保在更新后仍然是最新值。所以可以解構(gòu),無需擔(dān)心后面訪問到過期的值this在setup()中不可用。由于setup()在解析 2.x 選項(xiàng)前被調(diào)用,setup()中的this將與 2.x 選項(xiàng)中的this完全不同。同時(shí)在setup()和 2.x 選項(xiàng)中使用this時(shí)將造成混亂
響應(yīng)式reactive,ref
import?{?ref,?reactive,?toRefs?}?from?'vue';
setup()?{
????//?ref
????//?ref?對(duì)我們的值創(chuàng)建了一個(gè)響應(yīng)式引用
????const?counter?=?ref(0);?
????
????//?reactive
????//?接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理
????const?obj?=?{a:1};
????const?objReactive?=?reactive(obj);
????const?add?=?()?=>?{
??????counter.value++;
??????objReactive.a++;
????}
????
????return?{
??????counter,??//?return返回會(huì)自動(dòng)解套【在模板中不需要.value】
??????objReactive,
??????add,
????};?//?這里返回的任何內(nèi)容都可以用于組件的其余部分
}
| ref | reactive | |
|---|---|---|
| 入?yún)?/td> | 基本類型 | 引用類型 |
| 返回值 | 響應(yīng)式且可變的 ref 對(duì)象 | 響應(yīng)式代理(Proxy) |
| 訪問方式 | 1.ref 對(duì)象擁有一個(gè)指向內(nèi)部值的單一屬性 .value2.在dom和setup()的return中會(huì)自動(dòng)解套 3.ref 作為 reactive 對(duì)象的 property 被訪問或修改時(shí),也將自動(dòng)解套 | 直接.訪問即可 |
問題 & 注意點(diǎn): 因?yàn)閞eactive是組合函數(shù)【對(duì)象】,所以必須始終保持對(duì)這個(gè)所返回對(duì)象的引用以保持響應(yīng)性,不能解構(gòu)該對(duì)象或者展開
例如:
const { a } = objReactive或者return { ...objReactive }
解決方法:
toRefs API
用來提供解決此約束的辦法——它將響應(yīng)式對(duì)象的每個(gè) property 都轉(zhuǎn)成了相應(yīng)的 ref【把對(duì)象轉(zhuǎn)成了ref】。
import?{?reactive,?toRefs?}?from?'vue';
setup()?{
????//?reactive
????//?接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理
????const?obj?=?{a:1};
????const?objReactive?=?reactive(obj);
????
????//?toRefs
????//?將響應(yīng)式對(duì)象轉(zhuǎn)換為普通對(duì)象,其中結(jié)果對(duì)象的每個(gè)?property?都是指向原始對(duì)象相應(yīng)?property?的ref
????const?objRef?=?toRefs(objReactive);
????
????const?{?a?}?=?objRef;
????const?addObj?=?()?=>?{
??????a.value++;
??????console.log(a.value,?objRef,?objReactive,?obj);
????}
????return?{
??????...objRef,
??????addObj
????};?
}
Hooks方式
counter.js
import?{?ref?}?from?'vue';
export?function?useCounter()?{
????const?count?=?ref(0);
????const?decrement?=?()?=>?{
????????count.value--;
????}
????const?increment?=?()?=>?{
????????count.value++;
????}
????return?{
????????count,
????????decrement,
????????increment
????}
}
父組件
????<h2>{{?count?}}h2>
????<button?@click="increment">incrementbutton>
????<button?@click="decrement">decrementbutton>
</template>
響應(yīng)式計(jì)算和偵聽
computed
const?count?=?ref(1)
/*不支持修改【只讀的】?*/
const?plusOne?=?computed(()?=>?count.value?+?1)
plusOne.value++?//?錯(cuò)誤!
/*【可更改的】?*/
const?plusOne?=?computed({
??get:?()?=>?count.value?+?1,
??set:?(val)?=>?{
????count.value?=?val?-?1
??},
})
watch
?//?直接偵聽一個(gè)?ref
const?count?=?ref(0)
watch(count,?(count,?prevCount)?=>?{
??/*?...?*/
})
//?也可以使用數(shù)組來同時(shí)偵聽多個(gè)源
watch([fooRef,?barRef],?([foo,?bar],?[prevFoo,?prevBar])?=>?{
??/*?...?*/
})
watchEffect
定義:在響應(yīng)式地跟蹤其依賴項(xiàng)時(shí)立即運(yùn)行一個(gè)函數(shù),并在更改依賴項(xiàng)時(shí)重新運(yùn)行它 第一個(gè)參數(shù): effect,顧名思義,就是包含副作用的函數(shù)。如下代碼中,副作用函數(shù)的作用是:當(dāng)count被訪問時(shí),旋即(隨即)在控制臺(tái)打出日志。返回值:也是一個(gè)函數(shù),顯式調(diào)用可以清除watchEffect,組件卸載時(shí)會(huì)被隱式調(diào)用
const?count?=?ref(0);
const?stop?=?watchEffect(()?=>?console.log(count.value));?//?->?logs?0
setTimeout(()?=>?{
??count.value++;?//?->?logs?1
},?100);
//?清除watchEffect
stop();
清除副作用(onInvalidate)
watchEffect 的第一個(gè)參數(shù)——effect函數(shù)——自己也有參數(shù):叫onInvalidate,也是一個(gè)函數(shù),用于清除 effect 產(chǎn)生的副作用。
onInvalidate 被調(diào)用的時(shí)機(jī)很微妙:它只作用于異步函數(shù),并且只有在如下兩種情況下才會(huì)被調(diào)用:
當(dāng) effect函數(shù)被重新調(diào)用時(shí)當(dāng)監(jiān)聽器被注銷時(shí)(如組件被卸載了)
import?{?asyncOperation?}?from?"./asyncOperation";
const?id?=?ref(0);
watchEffect((onInvalidate)?=>?{
??const?token?=?asyncOperation(id.value);
??//?onInvalidate?會(huì)在?id?改變時(shí)或停止偵聽時(shí),取消之前的異步操作(asyncOperation)
??onInvalidate(()?=>?{
????token.cancel();
??});
});
第二個(gè)參數(shù):options
主要作用是指定調(diào)度器,即何時(shí)運(yùn)行副作用函數(shù)。
//?fire?before?component?updates
watchEffect(
??()?=>?{
????/*?...?*/
??},
??{
????flush:?"pre",
????onTrigger(e)?{
??????//?依賴項(xiàng)變更導(dǎo)致副作用被觸發(fā)時(shí),被調(diào)用
??????debugger;
????},
????onTrack(e)?{
??????//?依賴項(xiàng)變更會(huì)導(dǎo)致重新追蹤依賴時(shí),被調(diào)用【調(diào)用次數(shù)為被追蹤的數(shù)量】
??????debugger
????},
??}
);
三、源碼
未完待續(xù)...
四、其他相關(guān)
Vite2.0,2月17號(hào)正式發(fā)布
中文文檔:https://cn.vitejs.dev/
五、感悟
優(yōu)點(diǎn):很優(yōu)秀
缺點(diǎn):他的對(duì)手(React),更優(yōu)秀
雖然好多地方神似React,但是我們也可以從中看出,他們的都源于比較成熟的編程范式——FP(Functional Programming)。
框架只是工具,解決問題才是終極目標(biāo);我們還是要把重點(diǎn)放在領(lǐng)悟框架的設(shè)計(jì)思想上;悟到了,才是真正掌握了解決問題的手段。(抄的)
參考文檔:
https://juejin.cn/post/6858558735695937544#heading-0
https://blog.csdn.net/qq_34998786/article/details/113287653
https://www.jianshu.com/p/ad38a1f27d0f
https://juejin.cn/post/6844904134303301645
https://juejin.cn/post/6858558735695937544#heading-25
https://juejin.cn/post/6888925879243079687
https://www.jianshu.com/p/a8fdf52d0bcf
參考資料
Vue3中文文檔: https://v3.cn.vuejs.org/

回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
點(diǎn)擊“閱讀原文”查看 130+ 篇原創(chuàng)文章
