38
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ニフティグループAdvent Calendar 2021

Day 14

Vue3.2から追加された<script setup>記法をまとめてみた

Last updated at Posted at 2021-12-13

この記事は、ニフティグループ 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()の中に記述します。

sample.vue
<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から追加された新しい書き方は次のように書けます。

sample.vue
<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プロパティに登録しなくて良くなったのはとても嬉しいですね。

component-sample.vue
<script setup>
import MyComponent from './MyComponent.vue'
</script>

<template>
  <MyComponent />
  <my-component> <!-- ケバブケースも可 -->
</template>

動的コンポーネント

<script setup>構文で動的コンポーネントを利用する場合は、インポートしたコンポーネントをv-bindでバインドします。

component-sample2.vue
<script setup>
import MyComponent from './MyComponent.vue'
</script>

<template>
  <component :is="MyComponent" />
</template>

名前空間コンポーネント

コンポーネントのエントリポイントを作成して、名前空間つきでインポートできるようになりました。

components/form-components/Input.vue
<template>
  <input type="text" />
</template>
components/form-components/index.ts
export { default as Input } from "./Input.vue";
components/form-components/Form.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オプションと同じ値を取ります。

PropsJavaScriptSample.vue
<script setup>
const props = defineProps({
  text: String
})
</script>

TypeScriptの場合は次のようにして型定義ができます。
type: String as PropType<SampleType>のように書かなくてよくなったのは便利ですね。

PropsJavaScriptSample.vue
<script setup>
interface Props {
  text: string;
}

const props = defineProps<Props>();
console.log(props.text);
</script>

emitはdefineEmitsを利用して宣言できます。
defineEmitsはemitsオプションと同じ値を取ります。

EmitsJavaScriptSample.vue
<script setup>
const emit = defineEmits(['change', 'close']);
</script>

Propsと同様に、TypeScriptの場合は次のようにして型定義ができます。

PropsJavaScriptSample.vue
<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でも利用可能です。

SlotAndAttrSample.vue
<script setup>
import { useSlots, useAttrs } from 'vue';

const slots = useSlots();
const attrs = useAttrs();
</script>

トップレベルawait

トップレベルawaitが利用できます。
内部的にはasync setup()と同じのようなので、トップレベルawaitを利用した場合は、コンポーネントをSuspenseで受ける必要があります。

TopLevelAwaitSample.vue
<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>記法で書いていこうと思いました。

38
28
1

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
38
28

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?