📑

Vueのscript setupをもっと便利にするVue Macros

2022/12/25に公開

この記事はQiita Vue Advent Calendar 2022の12月24日分の記事です。

Vue 3でサポートされるようになったComposition API、そしてVue 3.2で正規サポートされたscript setupにより、Vue 3開発者体験に以下のトレードオフや不完全さがもたらされました。

  • Refを使用した際の .value の氾濫とつけ忘れ
  • scriptscript 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コアが提供するコンパイラマクロとして definePropsdefineEmits があります。

defineProps を使用することで(PropType を使わずに)TypeScriptの型宣言でPropsを宣言できることを知っている読者もいるでしょう。

<script setup>
withDefaults(defineProps<{ myProp: MyType }>(), {
  myProp: () => ({ key: 'value' })
})
</script>

Vue Macros

Vue Macrosは、Vueのコンパイラマクロをあつめたライブラリです。

Vueコアが提供する definePropsdefineEmits の他に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 を使用した双方向バインディングのために propsemits を定義する必要がありますが、このコンパイラマクロによって記述を簡潔にできます。

以下、公式サイトからの引用です(リンク)。

<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