# Vue 2.x 至 3.0 快速升級指南
在本書的內容,我們主要以 Vue.js 3.0 的語法來做解說。 但是相信隨著時間累積,在讀者群當中,已經使用 Vue.js 2.x 來開發的讀者朋友,肯定數量不在話下,若讀者沒有支援 IE 的需求,那麼即可放心使用 Vue 3.0 來升級你的專案。
在這篇內容中,我將快速導覽 Vue 2.x 升級至 3.x 需要的步驟,以及可能要調整的地方。
# 快速升級
如果讀者已經安裝好 Vue CLI (見本書第三章),則可透過指令
vue add vue-next
進行升級。
在升級之後,可以安裝 @vue/cli-plugin-eslint
https://cli.vuejs.org/core-plugins/eslint.html (opens new window),
安裝方式很簡單,同樣在已經安裝過 Vue CLI 之後,輸入指令:
vue add eslint
安裝完成後,你會發現 package.json
多了一個 lint
指令:
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
2
3
4
5
這時我們就可以執行:
npm run lint
Vue CLI 就會檢查整個專案,並提醒你有哪些不相容的部分了,檢查的規則建議可以使用 "plugin:vue/vue3-essential"
。
# 需自行修正的常見部分
# 元件實體建立
用 Vue.createApp()
取代 new Vue()
:
// vue 2.x
const vm = new Vue({ ... }).$mount('#app');
2
// vue 3.x
const vm = Vue.createApp({ ... }).mount('#app');
2
而且自 Vue 3.0 開始,元件實體不再支援 el
屬性。
# 不提供全域性註冊
Vue 3.0 開始不再提供功能的全域性註冊:
// Vue 2.x
Vue.use(...);
Vue.directive(...);
Vue.component(...);
Vue.mixins(...);
const vm = new Vue({ ... }).$mount('#app');
2
3
4
5
6
7
// Vue 3.x
const vm = Vue.createApp({ ... });
vm.use(...);
vm.directive(...);
vm.component(...);
vm.mixins(...);
vm.mount('#app');
2
3
4
5
6
7
8
9
而是註冊在根元件實體上。
所以若讀者們原本就已透過 Vue CLI 來建置專案,應該就會發現原本的 main.js
進入點:
// Vue 2.x
import Vue from 'vue';
new Vue({
router,
store,
render: h => h(App)
}).$mount('#app')
2
3
4
5
6
7
8
直接變成:
// Vue 3.x
import { createApp } from 'vue';
createApp(App).use(router).use(store).mount('#app');
2
3
4
# filter
屬性被移除
請參照本書【1-3】小節內容,就不再贅述。
# 生命週期鉤子函式名稱的變動
請參照本書 【1-7】小節內容。
# 移除了事件的 $on
, $off
以及 $once
請參照本書 【2-2】小節內容。
# Scoped Styles 的改寫
在 SFC Style 的部分,原本我們為了將樣式穿透至子元素,會使用 /deep/
或 >>>
的語法來處理,
到了 Vue 3.0 以後需要改成 ::v-deep()
的形式:
/* Vue 2.x */
::v-deep .bar { ... }
2
/* Vue 3.0 */
::v-deep(.bar) { ... }
2
並且新增了一個 ::v-global
的 Pseudo-elements,可以用在 Scoped Styles 的範圍下。
# 取消 .sync
修飾子
<!-- Vue 2.x -->
<ChildComponent :title.sync="pageTitle" />
<!-- 意義等同於 -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
2
3
4
5
升級後需要改為:
<!-- Vue 3.x -->
<ChildComponent v-model="pageTitle" />
<!-- 意義等同於 -->
<ChildComponent
:modelValue="pageTitle"
@update:modelValue="pageTitle = $event"
/>
2
3
4
5
6
7
8
而且 Vue 3.x 允許同時使用多組 v-model
。
# 移除 v-on.native
修飾子
<!-- 2.x -->
<my-component
v-on:close="handleComponentEvent"
v-on:click.native="handleNativeClickEvent"
/>
2
3
4
5
到了 Vue 3.x 可以直接使用:
<!-- 3.x -->
<my-component
v-on:close="handleComponentEvent"
v-on:click="handleNativeClickEvent"
/>
2
3
4
5
然後在 my-component.vue
元件內加上 emits
指定事件
// my-component.vue
export default {
emits: ['close']
}
2
3
4
# 移除 inline-template
語法
以前在 Vue 2.x 可以使用 inline-template
來建立模板,自 Vue 3.0 起被移除:
<!-- Vue 2.x -->
<my-component inline-template>
<div>...</div>
</my-component>
2
3
4
# key
屬性的變動
在 Vue 2.x 的時候, :key
屬性只能放在子元素內:
<!-- Vue 2.x -->
<template v-for="item in list">
<div :key="item.id">...</div>
<span :key="item.id">...</span>
</template>
2
3
4
5
到了 Vue 3.0 以後, :key
屬性應該放在 <template>
上面:
<!-- Vue 3.x -->
<template v-for="item in list" :key="item.id">
<div>...</div>
<span>...</span>
</template>
2
3
4
5
# <transition>
對應的 Class 名稱更新
在 Vue 2.x 的 v-enter
以及 v-leave
分別改名為 v-enter-from
與 v-leave-from
:
/* Vue 2.x */
.v-enter,
.v-leave-to {
opacity: 0;
}
.v-leave,
.v-enter-to {
opacity: 1;
}
2
3
4
5
6
7
8
9
10
更新後:
/* Vue 3.x */
.v-enter-from,
.v-leave-to {
opacity: 0;
}
.v-leave-from,
.v-enter-to {
opacity: 1;
}
2
3
4
5
6
7
8
9
10
# watch
觀察陣列更新
在 Vue 3.0 以後,使用 watch
觀察陣列的更新,只有在整個陣列被換掉才會觸發 (re-assign) ,
如果需要觀察內部元素的更新,需要加上 deep: true
:
// Vue 3.x
watch: {
bookList: {
handler(val, oldVal) {
console.log('book list changed')
},
deep: true
},
}
2
3
4
5
6
7
8
9
這樣就可以正常運作,但若是考慮到效能,除非必要請盡量減少使用。