この記事は、ニフティグループ Advent Calendar 2021 14日目の記事です。
Vue3.2から追加された<script setup>
記法について紹介します。
はじめに
Vue3.2からSFCを書く際に <script setup>
というCompositionAPIのシンタックスシュガーが追加されました。
公式ドキュメントには以下のように書いてあり、こちらが推奨される書き方のようです。
It is the recommended syntax if you are using both SFCs and Composition API. It provides a number of advantages over the normal <script> syntax:
これは、SFCとComposition APIの両方を使用している場合に推奨される構文です。通常の<script>構文に比べて、いくつかの利点があります。
翻訳 by DeepL
3.2から追加された記法のメリットは以下の通りです。
- More succinct code with less boilerplate
- Ability to declare props and emitted events using pure TypeScript
- Better runtime performance (the template is compiled into a render function in the same scope, without an intermediate proxy)
- Better IDE type-inference performance (less work for the language server to extract types from code)
翻訳 by DeepL
- ボイラープレートを減らし、より簡潔なコードに
- 純粋なTypeScriptを使用して、propsとemittedされたイベントを宣言することができます。
- ランタイムパフォーマンスの向上(テンプレートは、中間プロキシを介さず、同じスコープ内のレンダリング関数にコンパイルされる)
- IDEの型推論のパフォーマンスが向上(コードから型を抽出する言語サーバーの作業が減少)
とのことなので、今後は3.2から追加された記法を使っていくことになりそうです。
従来の書き方との違い
従来のCompositionAPIは次のように、setup()
の中に記述します。
<template>
<p>{{ msg }}</p>
</template>
<script lang="ts">
// vue
import { defineComponent } from 'vue';
export default defineComponent({
setup() {
const msg = 'Hello!'
return {
msg,
};
},
});
</script>
3.2から追加された新しい書き方は次のように書けます。
<script setup>
const msg = 'Hello!'
</script>
<template>
<p>{{ msg }}</p>
</template>
従来のCompositionAPIの書き方と比べて、とてもSvelteっぽく書きやすくなりました。
return
を書かなくてもtemplate内で変数が使えるようになりました。
これに伴い、VS Codeの拡張機能の推奨がVeturからVolarに変更されました。
参考)https://twitter.com/VueDose/status/1463169464451706897
新記法 <script setup>構文の書き方
従来のCompositionAPIでの書き方と比べて変更されたポイントを見ていきます。
コンポーネントの使用
<script setup>
内でインポートされたコンポーネントは、<template>
内で直接利用できるようになりました。
components
プロパティに登録しなくて良くなったのはとても嬉しいですね。
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<MyComponent />
<my-component> <!-- ケバブケースも可 -->
</template>
動的コンポーネント
<script setup>
構文で動的コンポーネントを利用する場合は、インポートしたコンポーネントをv-bind
でバインドします。
<script setup>
import MyComponent from './MyComponent.vue'
</script>
<template>
<component :is="MyComponent" />
</template>
名前空間コンポーネント
コンポーネントのエントリポイントを作成して、名前空間つきでインポートできるようになりました。
<template>
<input type="text" />
</template>
export { default as Input } from "./Input.vue";
<script setup>
import * as Form from "./form";
</script>
<template>
<Form.Input />
</template>
カスタムディレクティブ
グローバルに登録されているカスタムディレクティブはそのまま使えます。
ローカルのカスタムディレクティブを<template>
で利用するには、vディレクティブ名
という形式で命名する必要があります。
<script setup>
// directiveだとNG
const vDirective = {
beforeMount: (el) => {
el.style.color = "red";
},
};
</script>
<template>
<h1 v-directive>This is a Heading</h1>
</template>
PropsとEmit
propsはdefineProps
を利用して宣言できます。
defineProps
はpropsオプションと同じ値を取ります。
<script setup>
const props = defineProps({
text: String
})
</script>
TypeScriptの場合は次のようにして型定義ができます。
type: String as PropType<SampleType>
のように書かなくてよくなったのは便利ですね。
<script setup>
interface Props {
text: string;
}
const props = defineProps<Props>();
console.log(props.text);
</script>
emitはdefineEmits
を利用して宣言できます。
defineEmits
はemitsオプションと同じ値を取ります。
<script setup>
const emit = defineEmits(['change', 'close']);
</script>
Propsと同様に、TypeScriptの場合は次のようにして型定義ができます。
<script setup>
interface Emits {
(e: "change", value: string): void;
(e: "close", value: string): void;
}
const emit = defineEmits<Emits>();
const handleChange = (value: string) => {
emit("change", value);
};
</script>
defineProps
およびdefineEmits
はコンパイラマクロのため、<script setup>
構文ではインポートせずに利用可能です。
SlotとAttr
これまでsetup()
の引数で受け取っていたcontextの中身の残り2つはそれぞれヘルパーが用意されています。
useSlots
およびuseAttrs
は<script setup>
構文に限らず、通常のCompositionAPIでも利用可能です。
<script setup>
import { useSlots, useAttrs } from 'vue';
const slots = useSlots();
const attrs = useAttrs();
</script>
トップレベルawait
トップレベルawait
が利用できます。
内部的にはasync setup()
と同じのようなので、トップレベルawait
を利用した場合は、コンポーネントをSuspense
で受ける必要があります。
<script setup>
const result = await fetch('https://jsonplaceholder.typicode.com/todos/1');
const res = await result.json();
console.log(res);
</script>
まとめ
以上が、<script setup>
記法のまとめでした。
全体的により書きやすくなった印象で、とても良い感じです。
今後CompositionAPIを書く場合は<script setup>
記法で書いていこうと思いました。