概要
来月 Vue3 がリリース予定です!
先日ステータスが RC となりました
https://github.com/vuejs/rfcs/issues/189
ただし、Vue3 にバージョンアップするのは、Vuex や Vue Router などの主要なエコシステムが Vue3 対応を正式にリリースしてからになると思います
Vue3 のコードはこちらに公開されています
https://github.com/vuejs/vue-next
中身のコードを見てみると、全部 TypeScript で書かれているのが分かります
今回コンパイラーから DOM、レンダリングコードまですべてゼロから実装し直したらしいです
以下は VueCLI 用ですが、vue-next を試すプラグインがありました
これを使うことで Vue およびそのエコシステムや vue-loader などの開発環境周りもまとめて Vue3 仕様にバージョンアップしてくれるものと思われます
https://github.com/vuejs/vue-cli-plugin-vue-next
残念ながら、私が今携わっているシステムでは CLI を使ってないので、バージョンアップしてどのくらいの影響が出るのかの確認は来月以降に見送ります
Vue3 移行時に破壊的変更を伴うものは、Vue2 で事前に対応できるように配慮されています
(たとえば、Vue3 では旧slot
構文が削除されるので、事前に Vue2.6 からv-slot
構文が追加されています)
よって、Vue2 のマイナーバージョンアップに追従して、事前に破壊的変更を伴うものを対応していれば、Vue3 への移行はかなりスムーズになると思います
今回は以下を調査しました
- 新機能
- 既存機能の変更
- Composition API を実際に使ってみる
※ネットから適当に拾って書いてます
ちなみに Vue3 ではnew Vue
してるところから書き方が変わってたので、バージョンアップしても秒で落ちます
new Vue({
render: (h): VNode => h(App)
}).$mount('#app')
createApp(App).mount('#app')
新機能
Teleport
あるコンポーネント内の要素を別のコンポーネントの DOM にマウントできる
React の Portal 機能と同等です
<Teleport to="#modals">
<div>A</div>
</Teleport>
<Teleport to="#modals">
<div>B</div>
</Teleport>
<div id="modals">
<div>A</div>
<div>B</div>
</div>
親子関係のコンポーネントであれば、$emit
を書かなくて済む
親子孫というネストしたコンポーネントで孫から親に$emit
する際、孫 -> 子
と子 -> 親
のそれぞれで$emit
を書く必要がある
そして、子は孫から親に中継するだけの自分自身には関係ない$emit
となる
この問題を解決するのに Vuex を使う手もあるが、Vuex に connect するコンポーネントは限定すべきである
上記を解決するのに Teleport 機能は利用できるかなと思います
Suspense
以下のコードだと ComponentA がロードされるまでの間、<div>Loading...</div>
を出力し、ロード後 ComponentA を表示してくれる
React にも同機能があります
<Suspense>
<template #default>
<ComponentA />
</template>
<template #fallback>
<div>Loading...</div>
</template>
</Suspense>
使い所は多そう
既存機能の変更
v-model が複数書ける
<example v-model:name="name" v-model:value="value" />
React でいう Fragment 構文に相当します
Vue3 では以下はエラーになりません
<template>
<div>AAA</div>
<div>BBB</div>
</template>
Vue2 だと上記はエラーになるので、以下のように書く必要がありました
<template>
<div>
<div>AAA</div>
<div>BBB</div>
</div>
</template>
その他
style scoped
の改善
::v-deep
,::v-slotted
,::v-global
の追加
>>>
や/deep/
などの書き方は将来的に削除される
- ただし、古い書き方をコンパイラーで警告するのみでしばらくは互換性を保つっぽい
- 参考
slot
構文は廃止、v-slot
構文へ移行する必要あり
|
演算子を使ったフィルター機能は Vue3 で削除
$on
,$off
,$once
は Vue3 で削除
Composition API
この機能が 1 番の目玉だと(個人的に)思ってます
Vue2 でも@vue/composition-api
をインストールすれば、Composition API が使えます
import VueCompositionAPI from '@vue/composition-api'
Vue.use(VueCompositionAPI)
以下のようなcomponents/ExampleTable.vue
を Composition API で書き直してみます
import Vue from 'vue'
import ExampleChart from '@/components/ExampleChart.vue'
export default Vue.extend({
name: 'ExampleTable',
components: { ExampleChart },
data(): {
pagination: { sortBy: string; descending: boolean }
} {
return {
pagination: { sortBy: 'id', descending: true }
}
},
computed: {
items(): {}[] {
return this.$store.getters.items || []
},
headers(): { text: string; value: string }[] {
return [
{ text: 'ID', value: 'id' },
{ text: '名前', value: 'name' }
]
}
},
methods: {
getTableData() {
this.$store.dispatch('GET_TABLE_DATA')
}
}
})
↓↓↓↓↓
import {
defineComponent,
reactive,
computed,
SetupContext
} from '@vue/composition-api'
import ExampleChart from '@/components/ExampleChart.vue'
export default defineComponent({
name: 'ExampleTable',
components: { ExampleChart },
setup(_, ctx: SetupContext) {
const pagination = reactive({
sortBy: 'id',
descending: true
})
const headers = computed(() => [
{ text: 'ID', value: 'id' },
{ text: '名前', value: 'name' }
])
const items = computed(() => ctx.root.$store.getters.items || [])
function getTableData() {
ctx.root.$store.dispatch('GET_TABLE_DATA')
}
return { pagination, headers, items, getTableData }
}
})
上記は単純に Composition API 仕様に書き換えただけです
ポイント
- とりあえず defineComponent に全部渡すように書くことで、defineComponent の型定義のおかげで型推論が効くようになっている
- setup 関数に色々記載する
computed<string[]>
のようにジェネリクスで型書けるのよい
- this(Vue コンテキスト)を書く必要がなくなった
- 今までの
this
に相当する部分は setup 関数の第 2 引数から取れる
- vue-loader などビルド環境周りをバージョンアップしてないけど、動いた
- vue-devtools でちゃんと中身見れた
設計上のポイント
Composition API の書き方にする際はコンポーネントの実装を再設計する必要があると考えています
テーブルコンポーネントを例にします
Vue2 の書き方だと以下のように最上位がdata
、computed
、methods
となっているので、その中にテーブルのヘッダー、中身、フッターの処理を書くことになります
規模の大きいテーブルチャートになるとヘッダーを変えたいだけなのに、テーブルの中身やフッターの処理まで目を通すことになり、メンテナンス性に優れているとは言えません
export default Vue.extend({
name: 'ExampleTable',
components: { ExampleChart },
data(): {
header:
items:
footer:
} {
return {
header:
items:
footer:
}
},
computed: {
itemsData(): {}[] {
return this.$store.getters.tableData || []
},
headers(): { text: string; value: string }[] {
return [
{ text: 'ID', value: 'id' },
{ text: '名前', value: 'name' }
]
}
},
methods: {
function getTableData() {
ctx.root.$store.dispatch('GET_TABLE_DATA')
}
}
})
Composition API だと以下のようになります
最上位が「処理の内容」です
明らかに Composition API で作られたコンポーネントの方が良い設計だというのは伝わるかなと思います
そして、この書き方だとコードの分離や再利用も容易に行なえます
たとえば、テーブルヘッダーの処理はどのテーブルも同様の処理をしているはずなので、別モジュールとして実装し、それを各テーブルコンポーネントが import して使うなど可能になります
export default defineComponent({
setup(_, ctx: SetupContext) {
const headers = computed(() => [...])
const pagination = reactive(...)
const itemsData = computed(...)
function getTableData() {...}
...
return { pagination, headers }
}
})
処理を別モジュールで実装してもよいという書き方をしましたが
それはつまり、単一のコンポーネントではなく、システム全体のコンポーネント設計力が求められるということかなと思います
その他
こうゆうバージョンアップではあるあるですが、高速化・ファイルサイズの軽量化・メンテナンス性の向上も期待されるとあります
高速化
ファイルサイズの軽量化
- Vue の内部的に不要なパッケージの削除
- Tree Shaking
- これは元々採用している機能
- export されてるが import されていない関数をバンドルファイルから削除するやつ
メンテナンス性の向上
まとめ
Vue は React にかなり影響を受けているので、React の機能を Vue でも使えるようにするというパターンが今回もそして今後も多いと思います
Composition API も Vue の独自機能のように見えますが、React Hooks にかなり影響を受けています(Function-based Component API)
最近以下のような関数型プログラミングをサポートする機能を JS に導入する提案がされているのを見ます
Composition API や React Hooks も関数型プログラミングをサポートする機能と言えます
これらを採用することは同時に関数型プログラミングへパラダイムシフトすることを求められます
Composition API や React Hooks をプロジェクトで採用する際は、このことも踏まえてチームで話し合う必要があるでしょう
React Hooks が登場した時と同じですが、Composition API を採用すると、以下の機能は今後書く機会が減っていくのかなと思います
- Class Component
- Mixins
- Higher-order Component
また、Vue2 の書き方が今後使えなくなるということは(今の所)ないです
- 段階的に Composition API に移行する
- 新しく追加したコンポーネントのみ Composition API で書く
- 今のシステムでは採用しない
など柔軟に移行計画が立てられます
Vue2 の書き方から、単純に Composition API に移行するだけであれば、比較的楽に移行できそうです
ただし、前述したように設計方針が大きく変わるので、むやみに移行するのは避けたいです
加えて、Composition API はフロントエンドの実装方針を大きく変えるものなので、段階的に移行する場合でもチームでコンセンサスをとって慎重に進めたほうがよいかなと思います
簡単ではありますが、雑多に調査結果をまとめました
詳細は Vue3 のリリース後に使いながら、知識を深めていこうと思います