ついに待望のVue3.3がリリースされました!
Vue.jsのリリースにはアニメのコードネームがアルファベット順に付けられていますが、今回は「Rurouni Kenshin(るろうに剣心)」との事です。外国では「るろうに」は翻訳されていないようですね(一部では「Samurai X」という名前も使われているそうです)。
今回の更新ではTypeScript関連の強化や便利な機能などが追加されています。公式のブログにVue3.3の変更に関して網羅的にまとめられているので、詳細な部分はそちらをご参照ください。(本記事も当該のブログをベースに記載しています)
Vue3.3概要
- Reactivity Transform非推奨
- defineProps: 外部インポートされた複合の型サポート
- defineEmits: 人間工学的な宣言
- defineSlots: 型付きslotの宣言
- defineOptions: コンポーネントoptionの記述簡易化
- ジェネリック型パラメータのサポート
- toRef, toValue: getterのサポート
- JSXのImport Sourceのサポート
- (実験的機能) Reactive Props Destructure: propsの応答性維持とデフォルト宣言の簡易化
- (実験的機能) defineModel:
v-model
宣言の簡易化
Reactivity Transform非推奨
2023年の2月頃に「Reactivity Transform R.I.P」との訃報が届き、RFCもドロップとなってしまった機能ですが、Vue3.3からは非推奨、3.4で削除となります。現在利用されている方で、引き続き利用を検討されている方はVue Macrosを利用してください。今回の更新の多くはVue Macros、そして作者のsxzzさんの貢献が大きく占めています。比較的最近Vue.jsのコアチームに入った方で最年少とのこと。素晴らしい才能ですね。
defineProps: 外部インポートされた複合型サポート
長年issueに上がっていたdefineProps
における外部インポートされた複合型サポートの問題が解消されました。今まではオブジェクトでの対応などが必要でしたが、以下の様なインポートをしても解決することが出来ます。
<script setup lang="ts">
import type { Props } from './foo'
// imported + intersection type
defineProps<Props & { extraProp?: string }>()
</script>
ただし、Conditional Typesなどを利用した一部の特殊なケースはサポートされていないためご注意ください。
defineEmits: 人間工学的な宣言
Vue3.2以前のdefineEmits
は少し理解しづらい記述方法となっていましたが、3.3ではより人間工学に則した記述方法がサポートされています。3.2以前のものも引き続き利用可能です。
// 3.2以前
const emit = defineEmits<{
(e: 'foo', id: number): void
(e: 'bar', name: string, ...rest: any[]): void
}>()
// 3.3以降
const emit = defineEmits<{
foo: [id: number]
bar: [name: string, ...rest: any[]]
}>()
defineSlots: 型付きslotの宣言
掲題のとおり、型付のslotの宣言が可能となります。
<script setup lang="ts">
defineSlots<{
default?: (props: { msg: string }) => any
item?: (props: { id: number }) => any
}>()
</script>
defineOptions: コンポーネントoptionの記述簡易化
name
やinheritAttrs
などのoptionはsetup
シンタックスでは記述することが出来ず、別途scriptタグを記述する必要があったのですが、この度不要となりました。setup
のようにnameなどをタグに記載するという案もRFCでは出ていたのですが、個人的にはこちらの方が一貫した表現で好きです。
<script setup>
defineOptions({ inheritAttrs: false })
</script>
ジェネリック型パラメータのサポート
script setup
でジェネリック型が利用できるようになりました。下記の例ではitemsがstring[]
ならばselectedをnumber
などstring
以外にすると怒られます。
<script setup lang="ts" generic="T">
defineProps<{
items: T[]
selected: T
}>()
</script>
複数の宣言、extends
なども利用可能です。
<script setup lang="ts" generic="T extends string | number, U extends Item">
import type { Item } from './types'
defineProps<{
id: T
list: U[]
}>()
</script>
toRef, toValue: getterのサポート
toRef
ではRef化する際に.valueのアクセスに対してgetterを呼ぶ処理を記述することが可能となります。内容は以下のコードとコメントのとおりとなります。computed
に近いですが、getterを呼ぶだけのコストが低いアクセスが可能です。
// ref(1)と同様
toRef(1)
// .valueのアクセスの際にgetterを呼ぶreadonlyのRefを作成
toRef(() => props.foo)
// 元々Refなため変更なし
toRef(existingRef)
toValue
も同様にgetterがサポートされます。
toValue(1) // --> 1
toValue(ref(1)) // --> 1
toValue(() => 1) // --> 1
JSXのImport Sourceのサポート
Vue3.3以降ではTypeScriptのjsxImportSource
オプションを介したJSXの名前空間の指定がサポートされます。3.3まではグローバルな名前空間のまま、3.4ではグローバルから削除されるため、3.3から利用される方はtsconfig.json
のjsxImportSource
オプションを設定してください。
また、今回の更新であるジェネリック型などもJSXでサポートされています。
(実験的機能) Reactive Props Destructure: propsの応答性維持とデフォルト宣言の簡易化
propsの応答性維持はtoRefs
などを利用していたと思いますが、以下の様にシンプルな形で記述することが可能となります。デフォルト値もwithDefaults
が不要となりシンプルな記述となります。
<script setup>
import { watchEffect } from 'vue'
const { msg = 'hello' } = defineProps(['msg'])
watchEffect(() => {
// accessing `msg` in watchers and computed getters
// tracks it as a dependency, just like accessing `props.msg`
console.log(`msg is: ${msg}`)
})
</script>
<template>{{ msg }}</template>
実験的な機能なため利用には注意してください。また利用には以下の設定が必要です。
// vite.config.js
export default {
plugins: [
vue({
script: {
propsDestructure: true
}
})
]
}
(実験的機能) defineModel: v-model
宣言の簡易化
Vue3.2以前ではコンポーネント側で双方向バインディングを行う際は、以下の様にpropsとemitsの処理を両方記述する必要がありました。
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="modelValue" @input="onInput" />
</template>
Vue3.3以降の記載は以下の様になります。かなりシンプルになりました。
<script setup>
const modelValue = defineModel()
</script>
<template>
<input v-model="modelValue" />
</template>
Vue.jsのユーティリティライブラリであるVueUseではuseVModelという関数が記述を楽にしてくれていましたが、こちらは公式のサポートとなります。ただし、こちらも実験的機能なため利用には注意が必要であり、かつ以下の設定が必要です。
// vite.config.js
export default {
plugins: [
vue({
script: {
defineModel: true
}
})
]
}
その他
以下の関連ライブラリもバージョンアップが推奨されています。利用されている場合は併せて行いましょう。
- volar / vue-tsc@^1.6.4
- vite@^4.3.5
- @vitejs/plugin-vue@^4.2.0
- vue-loader@^17.1.0 (if using webpack or vue-cli)
Vue3.4以降の変更予定
Suspenseのstable化
ローディングなど非同期の依存解消などに利用される組み込みコンポーネントですが、現在は実験的機能のまま止まっています。Vue3.4で取り組まれる予定とのこと。(公式Twitterより)
SSR lazy hydration
AstroのアイランドアーキテクチャやQwicなどを受けて発展させた概念であり、SSRのハイドレーションを最適化する方法です。詳細は続報を待ちましょう。
Vapor Mode
Solidに触発されて考えられた概念です。Virtual DOMベースの代替となりパフォーマンス向上、メモリ削減、バンドルサイズ削減といい事づくめの機能です。Vue.jsのブログによると今年後半に続報が出るかも・・・とのこと。