Vueのscript setupをもっと便利にするVue Macros
この記事はQiita Vue Advent Calendar 2022の12月24日分の記事です。
Vue 3でサポートされるようになったComposition API、そしてVue 3.2で正規サポートされたscript setupにより、Vue 3開発者体験に以下のトレードオフや不完全さがもたらされました。
- Refを使用した際の
.value
の氾濫とつけ忘れ -
script
とscript setup
の併用(Options APIとComposition APIの併用)
前者に関しては、Reactivity Transformの登場で解決される方向に向かっていますが、後者に関しては、筆者の知る限りVueコアでの解決策はまだ発表されていません。
この記事では、後者の問題に対する解決策としてVue Macrosを紹介します。おそらく
script setupの限界
script setupには一部のVueコンストラクタオプションを記述できないため、Options APIとの併用が必要です((公式ドキュメント)[https://vuejs.org/api/sfc-script-setup.html#usage-alongside-normal-script])。
<script>
export default {
name: 'MyComponent',
inheritAttrs: false,
myCustomOptions: {},
props: {
myProp: {
type: Object as PropType<MyType>,
default () {
return { key: 'value' }
}
}
},
setup () {
const firstName = ref('Jonh')
const lastName = ref('Doe')
const fullName = computed(() => firstName.value + ' ' + lastName.value)
return {
firstName,
lastName,
fullName
}
}
}
</script>
<script>
export default {
name: 'MyComponent',
inheritAttrs: false,
myCustomOptions: {},
props: {
myProp: {
type: Object as PropType<MyType>,
default () {
return { key: 'value' }
}
}
}
}
</script>
<script setup>
const firstName = ref('Jonh')
const lastName = ref('Doe')
const fullName = computed(() => firstName.value + ' ' + lastName.value)
</script>
ほとんどの記述はsetup関数にあるのでscript setupに抜き出せますが、せっかくならscript setupだけでコードを記述したいところです。
コンパイラマクロ
コンパイラマクロとは、見た目は通常のJavaScript/TypeScriptのコードですが、Vue SFCをコンパイラする際に別のコードに変換されるもので、開発者体験の向上のために存在する構文です。
通常のコードではないため、importせずに使用することができます。
Vueコアが提供するコンパイラマクロとして defineProps
や defineEmits
があります。
defineProps
を使用することで(PropType
を使わずに)TypeScriptの型宣言でPropsを宣言できることを知っている読者もいるでしょう。
<script setup>
withDefaults(defineProps<{ myProp: MyType }>(), {
myProp: () => ({ key: 'value' })
})
</script>
Vue Macros
Vue Macrosは、Vueのコンパイラマクロをあつめたライブラリです。
Vueコアが提供する defineProps
や defineEmits
の他にOptions APIでしか定義できなかったVueコンストラクタオプションをscript setupに定義することを可能にします。
以下、一部を紹介します。
defineOptions
このコンパイラマクロは、Options APIでしか設定できなかったオプションをscript setupで記述することを可能にします。
記事の冒頭で示したコードは以下のように変更できます(defineProps
も使用しています)。
<script setup lang="ts">
defineOptions({
name: 'MyComponent',
inheritAttrs: false,
myCustomOptions: {}
})
withDefaults(defineProps<{ myProp: MyType }>(), {
myProp: () => ({ key: 'value' })
})
const firstName = ref('Jonh')
const lastName = ref('Doe')
const fullName = computed(() => firstName.value + ' ' + lastName.value)
</script>
defineModel
v-model
を使用した双方向バインディングのために props
と emits
を定義する必要がありますが、このコンパイラマクロによって記述を簡潔にできます。
以下、公式サイトからの引用です(リンク)。
<script setup lang="ts">
let { modelValue, count } = $defineModel<{
modelValue: string
count: number
}>()
console.log(modelValue)
modelValue = 'newValue'
count++
</script>
<script setup lang="ts">
const { modelValue, count } = defineProps<{
modelValue: string
count: number
}>()
const emit = defineEmits<{
(evt: 'update:modelValue', value: string): void
(evt: 'update:count', value: number): void
}>()
console.log(modelValue)
emit('update:modelValue', 'newValue')
emit('update:count', count + 1)
</script>
その他、script setupの中でJSXのrender関数を定義できる defineRender
や、Svelteに近いスタイルで .jsx
.tsx
を記述できる setupSFC
などのコンパイラマクロもあります。
Vue Macrosはまだバージョンが0.xなのでstableではありませんが、コミュニティからのフィードバックが活発になれば、Vueコアに組み込まれていくかもしれません。
Discussion