Vue3.3 新機能リリース!便利になった機能を紹介
2023/5/11 に、vue 3.3 がリリースされました。 https://blog.vuejs.org/posts/vue-3-3#generic-components
Vue3.3 では、TypeScript 関連の強化や便利な機能などが追加されています。 従来よりも型宣言がしやすく、かつ vue 自体も書きやすくなっているため、開発体験の向上が図れると感じています。
今回の記事では、新機能についての内容と、その中で便利だと感じた点を紹介していきます。
以下は新機能の一覧です。
-
<script setup>
+ TypeScript DX Improvements(script setup 構文における TypeScript 体験の向上)- Imported and Complex Types Support in Macros(外部インポートされた複合の型サポート)
- Generic Components(コンポーネントでのジェネリック型パラメータのサポート)
- More Ergonomic defineEmits(人間工学的な宣言)
- Typed Slots with defineSlots(型付き slot の宣言)
-
Experimental Features(実験的機能)
- Reactive Props Destructure(props の応答性維持とデフォルト宣言の簡易化)
- defineModel(defineModel を用いた v-model 宣言の簡易化)
-
Other Notable Features(その他の注目すべき機能)
- defineOptions(コンポーネントオプション の記述簡易化)
- Better Getter Support with toRef and toValue(toRef, toValue: getter のサポート)
- JSX Import Source Support(JSX の Import Source のサポート)
<script setup>
+ TypeScript DX Improvements(script setup 構文における TypeScript 体験の向上)
大きな変更点としては、TypeScript のサポートがより行われた点となります。
それでは見ていきましょう。
Imported and Complex Types Support in Macros(外部インポートされた複合の型サポート)
以前は、defineProps および defineEmits の型パラメーターの位置で使用される型はローカル型に限定されており、型リテラルとインターフェースのみがサポートされていました。
以下のようにvueファイル内に直接Interface定義しなければなりませんでした。
<script setup lang="ts">
interface Props {
name: string
kana: string
email: string
}
defineProps<Props>()
</script>
この制限は 3.3 で解決されました。コンパイラは外部インポートされた型を解決できるようになり、限られた複合型のセットをサポートするようになりました。
便利になった点としては、以前のようにローカル型で型定義しなくても良くなったので、共通のインターフェースを別のコンポーネントや他tsファイルから使用できるようになりました。
型定義に関する事は、ts ファイル側で管理できるのも良い点です。
<script setup lang="ts">
import type { Props } from './foo'
// 他tsファイルからimportしたinterface + nicknameの型の追加
defineProps<Props & { nickname?: string }>()
</script>
Generic Components(コンポーネントでのジェネリック型パラメータのサポート)
<script setup>
を使用するコンポーネントは、ジェネリック属性を介してジェネリック型パラメータを受け入れることができるようになりました。
ここで従来の例を見てみましょう。
以下の場合を想定します。
type Item を継承したItem1やItem2を、propsの値として親コンポーネントから子コンポーネントへ渡せるようにしたい
これを実現したい場合、以下の記載方法となります。
<script setup lang="ts">
import type { Item1, Item2 } from './types'
interface Props {
list: Array<Item1 | Item2>
}
defineProps<Props>()
</script>
従来の例だと、Propsのlistを定義する際、ジェネリック型を使って縛ることが出来ないため、
type Item1, type Item2 をユニオン型で定義する必要があり、Itemを継承したtypeの数分の型定義が必要となってしまいます。
しかし、今回のアップデートにより、この問題は解決できます。
以下は、ジェネリック型にユニオン型の制約や、インターフェイス継承の制約をつける場合の例となります。
<script setup lang="ts" generic="U extends Item">
import type { Item } from './types'
defineProps<{
list: U[]
}>()
</script>
ジェネリック型で定義できると component の中身がスッキリする事が一目見てわかると思います。
この例から、listについて、ジェネリック型を使って縛ることが出来るので、自らtype Item1、type Item2それぞれのユニオン型を作る手間がなくなったり、可読性が増したりします。
defineEmits(人間工学的な宣言)
以前の defineEmits の構文です。
const emit = defineEmits<{
(e: 'foo', id: number): void
(e: 'bar', name: string, ...rest: any[]): void
}>()
少し冗長で書きにくかったので、Vue3.3 では、より人間工学に基づいた書きやすい構文が導入されました。
以前の構文ではe: にemitのイベント名を記載する形でしたが、イベント名の数だけe: を記載する事になるため、少し冗長な記載方法になっていました。
Vue3.3では、emitのイベント名をキーとした以下の構文で記載する事ができるようになったため、以前の冗長さは解消されました。
const emit = defineEmits<{
foo: [id: number]
bar: [name: string, ...rest: any[]]
}>()
Typed Slots with defineSlots(型付き slot の宣言)
新しい defineSlots マクロを使用して、スロットの型を宣言できます。
これまでは、スロットで受け取る引数に型を宣言できなかったため、より型システムの恩恵を受ける事ができるようになりました。
<script setup lang="ts">
defineSlots<{
default?: (props: { msg: string }) => any
item?: (props: { id: number }) => any
}>()
</script>
Experimental Features(実験的機能)
まだ実験的な機能ではありますが、今後の安定版が期待できる機能と感じていますので紹介します。
Reactive Props Destructure(props の応答性維持とデフォルト宣言の簡易化)
Vue3.3 以前では、propsのreactive値の変化の検知やデフォルト値宣言の際は、以下のような記述が必要でした。
<script setup>
import { watchEffect } from 'vue'
interface Props {
msg: string
}
// デフォルト値を指定するためには、withDefaultsを使用する必要がある
const props = withDefaults(defineProps<Props>(), {
msg: 'hello',
})
watchEffect(() => {
// 親コンポーネントから子コンポーネントに渡したreactive値の変化を検知するためには、propsオブジェクトを介して値にアクセスする必要がある
console.log(`msg is: ${props.msg}`)
Vue3.3 では、以下の様にシンプルな形で記述できるようになります。
<script setup>
import { watchEffect } from 'vue'
// withDefaultsを使用せずともデフォルト値の指定が可能となった
const { msg = 'hello' } = defineProps(['msg'])
watchEffect(() => {
// propsオブジェクトを介さずとも、`msg` と記述するだけで値の変化に追従できるようになった
console.log(`msg is: ${msg}`)
})
</script>
利用には以下の設定が必要となります。
export default {
plugins: [
vue({
script: {
propsDestructure: true
}
})
]
}
defineModel(v-model 宣言の簡易化)
従来は v-model との双方向バインディングを行うためには、
従来は、親コンポーネントからreactive値を子コンポーネントに渡して、状態を変化させるためには、props と emit を用いて表現する必要がありました。
<script setup>
const props = defineProps(['modelValue'])
const emit = defineEmits(['update:modelValue'])
console.log(props.modelValue)
function onInput(e) {
emit('update:modelValue', e.target.value)
}
</script>
<template>
<input :value="modelValue" @input="onInput" />
</template>
Vue3.3 では、新しい defineModel を使用する事で、シンプルな記述にする事ができるため、便利な機能でしょう。
<script setup>
const modelValue = defineModel()
console.log(modelValue.value)
</script>
<template>
<input v-model="modelValue" />
</template>
まとめ
ここまで新機能についての内容説明と、便利だと感じた点を紹介してきましたが、
特に、<script setup>
+ TypeScript DX Improvements(script setup 構文における TypeScript 体験の向上)
の機能追加項目に関してはメリットが大きく、開発体験が大きく変わると感じています。
皆さんも、是非とも開発に取り入れてみましょう!