LoginSignup
44
29

TypeScriptが強化されたVue3.3アップデートと将来の機能について

Last updated at Posted at 2023-05-11

ついに待望の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の記述簡易化

nameinheritAttrsなどの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.jsonjsxImportSourceオプションを設定してください。

また、今回の更新であるジェネリック型なども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のブログによると今年後半に続報が出るかも・・・とのこと。

44
29
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
44
29