升級 Vue 3 前,你必須要知道的改動

就在前幾天 Vue3 RC 版本已經發佈啦 (撒花)!距離正式版越來越近了 🎉🎉🎉! (預計是8月)

這篇主要是幫讀者從官方文件整理 Vue3 的新功能以及和 Vue2 的差異,讓讀者快速了解升級 Vue3 前需要注意什麼!

新增的功能

1. Composition API

相信大家最常聽到 Vue3 的新功能就是 Composition API 了。

這邊建議的升級重點就是優先用 Composition API 寫法取代舊版 mixins

改用 Composition API 的好處:

  • 相關邏輯可以集中,且更容易重複使用 (反觀 Vue2 的 mixins)
  • 不用因為莫名出現的變數或方法名稱找老半天 (結果在 mixins)
  • 減少 this 指向的問題

而 Vue 3 也相容部分舊的寫法,所以可以視專案情況再陸續修改完整!

用 Vue3 這樣寫可以相容,也可以取得 setup 的東西

2. useCssVars

可以在目前的組件加上自訂的 css 變數 var()

也可以動態的改變 var 值,我們改寫一下原本的 code

⚠️ 建議外面加上一層根元素,因為使用 useCssVars 會在根元素上加上 style,若不加根元素會在每個元素都加上 style 喔!

3. Teleport

Teleport 可以將定義的內容轉移到目標元素。

以往我們在做 彈框、對話框、通知等,需要在每個頁面加入組件或是用 vuex 來控制顯示與否。Teleport 可以幫我們簡化這些動作並提高開發體驗!

🚀 使用場景範例

我們以 Modal 為例,先建立一個 Modal 組件,並給予 id !

Modal.vue<template>
<div id="modal-wrapper"></div>
</template>

我們把這個 Modal 引入 App.vue

加入打開彈窗的邏輯,teleport 需要在 to 參數指定元素選擇器

以上這樣就完成了,teleport 中的內容都會被渲染在目標元素裡頭 🎉

渲染後的 HTML

當然也可以同時指定多個 teleport 到同一個元素上。

渲染結果

會自動疊加

4. Fragments

在 Vue 2 中在每個模板我們都要加上根元素,會多了許多不必要的元素在 HTML 裡 😅

<!-- Layout.vue -->
<template>
<div> --> 根元素
<li>...</li>
<li>...</li>
<li>...</li>
</div>
</template>

而在 Vue 3 終於可以省略根元素了,這對於 list 類型的組件非常方便 🎉

<!-- Layout.vue -->
<template>
<li>...</li>
<li>...</li>
<li>...</li>
</template>

5. Emits Component Option

Vue 3 提供 emits 的聲明,用法就像 props 一樣。

<template>...</template> 
<script>
export default {
emits: [ "myEvent", "click" ]
}
</script>

這樣寫的話可以快速知道這個組件含有什麼 emit 事件,一目瞭然。

🚀 Emits 也可以進行驗證,必須為 function 並返回 布林值

app.component('custom-form', {
emits: {
// 不會被驗證
click: null,

// 驗證 submit 事件
submit: ({ email, password }) => {
if (email && password) {
return true
} else {
console.warn('Invalid submit event payload!')
return false
}
}
},
methods: {
submitForm() {
this.$emit('submit', { email, password })
}
}
})

⚠️ 注意

假如自訂的事件名稱為 camelCased 命名方式

this.$emit('customEvent')

必須這樣寫才有效果!

<!-- Won't work -->
<my-component v-on:custom-event="doSomething"></my-component>
<!-- Work -->
<my-component v-on:customevent="doSomething"></my-component>

所以官方建議直接一律使用 kebab-case 來命名,避免浪費時間處理這種命名問題。

For these reasons, we recommend you always use kebab-case for event names.

this.$emit('custom-event')<my-component v-on:custom-event="doSomething"></my-component>

6. Suspense & Error Boundary

Suspense 是非同步載入的組件,在資料讀取完成以及渲染完成之前,會先顯示 #fallback 的內容。

有了 Suspense 可以不用特地加一個 loading 變數來處理加載資料時的判斷。

<!-- 傳統做法 --><div v-if="loading"> Loading ...</div>
<div v-else> {{ data }} </div>
<!-- 使用 suspense --><Suspense>
<template #default>{{ data }}</template>
<template #fallback> Loading... </template>
</Suspense>
⚠️ #default #fallback 其實是 v-slot 的簡寫

也可以搭配 onErrorCaptured 來做錯誤的處理,以下為例,假如 Suspended-component call api 發生錯誤,onErrorCaptured 方法就會捕捉到錯誤,並在畫面顯示錯誤訊息。

<template>
<div v-if="error">
{{ error }}
</div>
<Suspense v-else>
<template #default>
<Suspended-component />
</template>
<template #fallback>
<div>Loading..</div>
</template>
</Suspense>
</template>
<script>
import { onErrorCaptured, ref } from 'vue'
export default {
setup() {
const error = ref(null)
onErrorCaptured(e => {
error.value = e
return true
})
return { error }
}
}
</script>

除了以上用法,也可以搭配 Vue Router 使用 😎

<Suspense>   
<template #default>
<router-view />
</template>
<template #fallback>
<span>畫面讀取中...</span>
</template>
</Suspense>

7. ::v-slotted & ::v-global

🚀 ::v-slotted 使用情境

一般我們都會在組件的 style 加上 scoped,就是為了要避免被其它頁面 style影響。

但假如我想要在 slot 中如果傳入 p元素,並將字體改為斜體,我們會這樣寫…

Modal.vue<template>
<div>
<slot></slot>
</div>
</template>
<style scoped>
p {
font-style: italic;
}
</style>
App.vue<template>
<Modal>
<p>我要斜體字</p>
</Modal>
</template>

但你會發現沒變斜體,因為 slot 的內容是屬於父組件的!這時候我們可以使用 ::v-slotted 來達到效果!

<template>
<div id="modal-wrapper">
<slot></slot>
</div>
</template>
<style scoped>
::v-slotted(p) {
font-style: italic;
}

</style>

🚀 ::v-global

可以在 <style scoped> 宣告一個全域的樣式。

<style scoped>
::v-global(p) {
font-style: italic;
}

</style>
同等於<style>
p {
font-style: italic;
}
</style>

棄用的功能

1. KeyCode

Vue 3 將不支援 KeyCode 寫法

<!-- keyCode version -->
<input v-on:keyup.13="submit" />

<!-- alias version -->
<input v-on:keyup.enter="submit" />

2. $on, $off $once

移除了$on, $off 以及 $once 也就代表不能使用 EventBus 😂。

但作者尤雨溪大大有提到,未來相容版本還是可以使用 (可能是 2.7 版),或是改用 mitt 這類的套件

In Vue 2, the most common usage of the event emitter API is using an empty Vue instance as an event hub. This can be replaced by using an external library implementing the event emitter interface, for example mitt.

These methods can also be supported in compatibility builds.

3. Inline Template Attribute

這個比較少用,所以影響不大,官方的建議是用 v-slot 作法來取代。

<!-- before -->
<my-comp inline-template :msg="parentMsg">
{{ msg }} {{ childState }}
</my-comp>

<!-- after -->
<my-comp v-slot="{ childState }">
{{ parentMsg }} {{ childState }}
</my-comp>
<!-- my-comp -->
<slot :childState="childState" />

4. Filters

其實 Filters 移除也蠻合理的,因為可以直接用 methods 或是 computed 來取代。

5. $listeners

在 Vue3 中 $listeners 已經整合到 $attrs

Vue2: 
<input type="text" v-bind="$attrs" v-on="$listeners" />
Vue3:
<input type="text" v-bind="$attrs" />

變更的功能

1. 修飾符 .sync 移除,v-model 可以多個使用

Vue 3 移除了 .sync 且合併到 v-model 裡頭,也因為這樣的關係可以同時使用多個 v-model 了!

<!-- Vue 2 --> 
<CustomComponent v-bind:style.sync="style" />
<!-- Vue 3 -->
<CustomComponent v-model:style="style" />

多個 v-model 相當於以前的 .sync

<Form   
v-model:name="name"
v-model:email="email"
/>

🚀 組件實作 v-model 的寫法也有些許改變 (真的就是與 .sync 的綜合寫法)

  • props 的 value 改為 modelValue
  • $emit input 改為 update:modelValue
<CustomComponent v-model="style" />

2. ::v-deep

⚠️ 為了將樣式穿透至子元素,像是要改寫 element-ui 樣式常用到 ::v-deep,但 Vue 3 寫法稍有不同。

3. Directive

主要是針對生命週期的名稱重新命名,確保名稱一致。

  • ⚠️ bind 改為 beforeMount
  • ⚠️ inserted 改為 mounted
  • 🚀 新增 beforeUpdate: 元素更新前執行
  • 🚀 新增 beforeUnmount: 元素卸載前執行
  • ⚠️ unbind 改為 unmounted

4. 非同步組件

🚀 在 Vue 2 建立非同步組件寫法是定義一個 function ,Vue 3 改為使用 defineAsyncComponent

5. transition-class

🚀 v-enter改為v-enter-from

🚀 v-leave改為 v-leave-from

.v-enter-from, .v-leave-to { opacity: 0; } 
.v-leave-from, .v-enter-to { opacity: 1 }

6. 全局 api

全局 api 改動差異不大

🚀 新增 createApp

⚠️ 移除 Vue.config.productionTip

Vue Router & Vuex

假如有用 Vue RouterVuex 的話這兩個一定要升級新版本,目前皆還在 Beta 階段,但目前觀察已經不會有大規模改動了,還是可以先進行升級。

Vue Router 新舊版差異

Vuex 新舊版差異

其實 Vue Router 和 Vuex 的改動並不會差太多,這部分相對容易許多。

檢測工具

官方之後也會提供檢測工具 vue-migration-helper 幫助工程師瞭解專案還有哪邊需要進行修改,也可以搭配使用。(目前 Vue 3 版本還在開發中)

結語

以上就是我們整理出需要注意的改動,詳細可以到官方文件查看。相信大家對於 Vue 3 也是迫不及待想要使用了,若要升級勢必免不了大量的重構,但相信長遠來看還是值得的,希望這篇可以幫助大家開心「玩」 Vue 3 🤣!

參考

--

--