# 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
1

進行升級。

在升級之後,可以安裝 @vue/cli-plugin-eslint https://cli.vuejs.org/core-plugins/eslint.html (opens new window), 安裝方式很簡單,同樣在已經安裝過 Vue CLI 之後,輸入指令:

vue add eslint
1

安裝完成後,你會發現 package.json 多了一個 lint 指令:

"scripts": {
  "serve": "vue-cli-service serve",
  "build": "vue-cli-service build",
  "lint": "vue-cli-service lint"
},
1
2
3
4
5

這時我們就可以執行:

npm run lint
1

Vue CLI 就會檢查整個專案,並提醒你有哪些不相容的部分了,檢查的規則建議可以使用 "plugin:vue/vue3-essential"

# 需自行修正的常見部分

# 元件實體建立

Vue.createApp() 取代 new Vue()

// vue 2.x
const vm = new Vue({ ... }).$mount('#app');
1
2
// vue 3.x
const vm = Vue.createApp({ ... }).mount('#app');
1
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');
1
2
3
4
5
6
7
// Vue 3.x
const vm = Vue.createApp({ ... });

vm.use(...);
vm.directive(...);
vm.component(...);
vm.mixins(...);

vm.mount('#app');
1
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')
1
2
3
4
5
6
7
8

直接變成:

// Vue 3.x
import { createApp } from 'vue';

createApp(App).use(router).use(store).mount('#app');
1
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 { ... }
1
2
/* Vue 3.0 */
::v-deep(.bar) { ... }
1
2

並且新增了一個 ::v-global 的 Pseudo-elements,可以用在 Scoped Styles 的範圍下。

# 取消 .sync 修飾子

<!-- Vue 2.x -->
<ChildComponent :title.sync="pageTitle" />

<!-- 意義等同於 -->
<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />
1
2
3
4
5

升級後需要改為:

<!-- Vue 3.x -->
<ChildComponent v-model="pageTitle" />

<!-- 意義等同於 -->
<ChildComponent
  :modelValue="pageTitle"
  @update:modelValue="pageTitle = $event"
/>
1
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"
/>
1
2
3
4
5

到了 Vue 3.x 可以直接使用:

<!-- 3.x -->
<my-component
  v-on:close="handleComponentEvent"
  v-on:click="handleNativeClickEvent"
/>
1
2
3
4
5

然後在 my-component.vue 元件內加上 emits 指定事件

// my-component.vue
export default {
  emits: ['close']
}
1
2
3
4

# 移除 inline-template 語法

以前在 Vue 2.x 可以使用 inline-template 來建立模板,自 Vue 3.0 起被移除:

<!-- Vue 2.x -->
<my-component inline-template>
  <div>...</div>
</my-component>
1
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>
1
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>
1
2
3
4
5

# <transition> 對應的 Class 名稱更新

在 Vue 2.x 的 v-enter 以及 v-leave 分別改名為 v-enter-fromv-leave-from

/* Vue 2.x */
.v-enter,
.v-leave-to {
  opacity: 0;
}

.v-leave,
.v-enter-to {
  opacity: 1;
}
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;
}
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
  },
}
1
2
3
4
5
6
7
8
9

這樣就可以正常運作,但若是考慮到效能,除非必要請盡量減少使用。

Last Updated: 1/9/2021, 1:58:39 AM