Vue 3 Migrations
Tags: vue
Published: 2020-09-08 15:23:29

[Vue 3 migration introduction](https://v3.vuejs.org/guide/migration/introduction.html)


# New Features
Composition API = hooks
Teleport = Portal
Fragments = Fragments
`emits` Component Option = 定義 emit 的事件
`v-model` modifiers = 可以在 v-model 用 mod
createRenderer = can create custom renderers


以上不討論


---

比較常用的
- [Global Vue -> app instance](#anchor00)  
- [Global Vue methods -> named export](#anchor01)  
- [Render function h -> named export](#anchor02)  
- [v-model & .sync](#anchor03)  
- [Functional component](#anchor04)  
- [Async components](#anchor05)  
- [data is function](#anchor06)  
- [Watch](#anchor07)  

比較不常用的
- [Custom elements](#anchor08)
- [is & v-is](#anchor09)
- [$scopedSlots](#anchor10)
- [Attributes coercion strategy](#anchor11)
- [v-directive](#anchor12)
- [Transition classes](#anchor13)
- [outerHTML -> innerHTML](#anchor14)
- [keyCode](#anchor15)
- [\$on, \$off, \$once](#anchor16)
- [Filters](#anchor17)
- [Inline template](#anchor18)

---

# Breaking Changes

## <a name="anchor00" href="#anchor00">#</a> Global Vue -> app instance
```js
// 2.x
import Vue from 'vue';

//3.x
import { createApp } from 'vue';
const app = createApp({});
```

| 2.x Global API             | 3.x Instance 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                    |

## <a name="anchor01" href="#anchor01">#</a> Global Vue methods -> named export
```js
// 2.x
import Vue from 'vue'
Vue.nextTick(...)

// 3.x
import { nextTick } from 'vue'
nextTick(...)
```

| Effected         |                        |
| ---------------- | ---------------------- |
| `Vue.nextTick`   |
| `Vue.observable` | replaced by `reactive` |
| `Vue.version`    |
| `Vue.compile`    | only in full builds    |
| `Vue.set`        | only in compat builds  |
| `Vue.delete`     | only in compat builds  |


## <a name="anchor02" href="#anchor02">#</a> Render function `h` -> named export
```js
import { h } from 'vue'

export default {
  render() {
    return h('div')
  }
}
```

## <a name="anchor03" href="#anchor03">#</a> v-model & .sync
統一介面
`myProp.sync` 語法移除，改為 `v-model:myProp`
`v-model` 語法糖改為等同於 `v-model:modelValue`

|     | shorthand      | prop       | event             |
| --- | -------------- | ---------- | ----------------- |
| 2.x | v-model        | value      | input             |
|     | :myProp.sync   | myProp     | update:myProp     |
|     |
| 3.x | v-model        | modelValue | update:modelValue |
|     | v-model:myProp | myProp     | update:myProp     |





## <a name="anchor04" href="#anchor04">#</a> Functional component
`<template functional>` 跟 `{ functional: true }` 被移除
functional component 對效能幾乎沒有影響，可以不用刻意標示

真的要做 functional component 的話只能以匯出 function 的方式
```js
import { h } from 'vue'

const DynamicHeading = (props, context) => {
  return h(`h${props.level}`, context.attrs, context.slots)
}

DynamicHeading.props = ['level']
export default DynamicHeading
```
(但這樣有比較輕鬆嗎)


## <a name="anchor05" href="#anchor05">#</a> Async components
- 非同步組件必須用 `defineAsyncComponent` 包起來
- `component` 屬性改名為 `loader`
- loader 函式必須回傳 Promise
```js
// 3.x
import { defineAsyncComponent } from 'vue'

// Async component without options
const asyncPage = defineAsyncComponent(() => import('./NextPage.vue'))

// Async component with options
const asyncPageWithOptions = defineAsyncComponent({
  loader: () => import('./NextPage.vue'),
  delay: 200,
  timeout: 3000,
  errorComponent: ErrorComponent,
  loadingComponent: LoadingComponent
})
```

```js
// 3.x
const asyncComponent = defineAsyncComponent(
  () => new Promise(...)
)
```

## <a name="anchor06" href="#anchor06">#</a> `data` 現在一律必須是 function
很好


## <a name="anchor07" href="#anchor07">#</a> Watch
不再支援 path 作為 watcher (例如 `'$store.state.count'()`)
必須透過 computed


---
# Breaking Changes (比較不常用的東西)


## <a name="anchor08" href="#anchor08">#</a> Custom elements
`Vue.ignoredElements` 要改為 `vue-loader` 的 options, 或者 `app.config.isCustomElement`


## <a name="anchor09" href="#anchor09">#</a> is & v-is

`is` 現在只能用在 `<component>` 上
在 custom component (Web Component API) 上要換成 `v-is`

## <a name="anchor10" href="#anchor10">#</a> $scopedSlots

`this.$scopedSlots` 都要改成 `this.$slots` 

## <a name="anchor11" href="#anchor11">#</a> Attributes coercion strategy
不用管
簡單來說如果 v-bind 一個 attribute 到一個 element 上
2.x 如果值是 `null` `undefined` `false` 那屬性會從元素被移除，但對於某些特殊屬性如 draggable 又不太一致
3.x 統一為 `null` `undefined` 會被移除，其他都會照原本值 render 出來


## <a name="anchor12" href="#anchor12">#</a> v-directive
當定義 directive 的時候 (例如寫一個 `v-tooltip` 時)，lifecycle hooks 的命名改了，改成跟一般 component 的 lifecycle 一樣

| 2.x              | 3.x           |
| ---------------- | ------------- |
| bind             | beforeMount   |
| inserted         | mounted       |
| -                | beforeUpdate  |
| update           | -             |
| componentUpdated | updated       |
| -                | beforeUnmount |
| unbind           | unmounted     |


## <a name="anchor13" href="#anchor13">#</a> Transition classes
改名
`v-enter` -> `v-enter-from`
`v-leave` -> `v-leave-from`


## <a name="anchor14" href="#anchor14">#</a> outerHTML -> innerHTML
2.x 會把指定的 root element 透過 outerHTML 整個換掉
3.x 會用 innerHTML 塞裡面

## <a name="anchor15" href="#anchor15">#</a> keyCode
直接以數字 keyCode 作 modifier 被移除
但還是可以用鍵名例如 `@keyup.delete`
```js
// removed
<input v-on:keyup.112="showHelpText" />
```

## <a name="anchor16" href="#anchor16">#</a> \$on, \$off, \$once
移除．vue instance 不再實作 event emitter
```js
// removed
this.$on('some-event', ...)
```
讚

## <a name="anchor17" href="#anchor17">#</a> Filters
移除．
```js
// removed
<p>{{ accountBalance | currencyUSD }}</p>
```
超讚，討厭 filter

## <a name="anchor18" href="#anchor18">#</a> Inline template
移除. 
```js
// removed
<my-component inline-template>
  <div>
    <p>These are compiled as the component's own template.</p>
    <p>Not parent's transclusion content.</p>
  </div>
</my-component>
```
這什麼從來沒看過