10
7

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 1 year has passed since last update.

【Vue.js】<script setup>って何がいいの?

Posted at

概要

[2022年決定版] Vue3 x Typescriptによるアプリケーションづくりでは、<script setup>構文が使用されています。
一方、Vue.js公式ドキュメントのサンプルコードでは使用されていない箇所がほとんどです。 

混乱しそうになったので、<script setup>に関して調査してみました。

結論 <script setup>とは?

<script setup> は単一ファイルコンポーネント(SFC)内で Composition API を使用するコンパイル時のシンタックスシュガー(糖衣構文)です。

糖衣構文とは、読み書きのしやすさのために導入される構文です。通常よりシンプルで分かりやく記述できるというメリットがあります。
実際にソースコードを比較してみましょう。

また、他にも以下のような利点がありますが、こちらは後ほど解説します。

SFC と Composition API の両方を使うならば、おすすめの構文です。これは通常の <script> 構文よりも、多くの利点があります:

  • ボイラープレートが少なくて、より簡潔なコード
  • 純粋な TypeScript を使ってプロパティと発行されたイベントを宣言する機能
  • 実行時のパフォーマンスの向上(テンプレートは中間プロキシなしに同じスコープ内のレンダリング関数にコンパイルされます)
  • IDE で型推論のパフォーマンス向上(言語サーバがコードから型を抽出する作業が減ります)
    Vue.js公式ドキュメント SFC<script setup>

まずはViteを利用してプロジェクトを作成します。

$ yarn create vite 
$ yarn
$ yarn dev

localhostにアクセスすると下のような画面が開けます。

それでは、ソースコードを確認していきます。
まずは<script setup>を使用しない場合です。

HelloWorld.vue
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
  props: {
    msg: {
      type: String,
      required: true,
    }
  },
  setup() {
    const count = ref(0)
    return {
      count,
    }
  }
})
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
      >create-vue</a
    >, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

次に<script setup>を使用する場合です。

HelloWorld.vue
<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

<template>
  <h1>{{ msg }}</h1>

  <div class="card">
    <button type="button" @click="count++">count is {{ count }}</button>
    <p>
      Edit
      <code>components/HelloWorld.vue</code> to test HMR
    </p>
  </div>

  <p>
    Check out
    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank"
      >create-vue</a
    >, the official Vue + Vite starter
  </p>
  <p>
    Install
    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
    in your IDE for a better DX
  </p>
  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
</template>

<style scoped>
.read-the-docs {
  color: #888;
}
</style>

スッキリした!!

scriptタグのみ抜粋すると、これが

HelloWorld.vue
<script lang="ts">
import { defineComponent, ref } from 'vue'
export default defineComponent({
  props: {
    msg: {
      type: String,
      required: true,
    }
  },
  setup() {
    const count = ref(0)
    return {
      count,
    }
  }
})
</script>

こうなります。いいですね!

HelloWorld.vue
<script setup lang="ts">
import { ref } from 'vue'

defineProps<{ msg: string }>()

const count = ref(0)
</script>

これが<script setup>を使用する上での糖衣構文としてのメリットです。

さて、ここからは公式ドキュメント記載の別のメリットについて説明します。
また、以降のサンプルコードは<script>タグのみ抜粋します。

ボイラープレートが少なくて、より簡潔なコード

これは、上で比較したscriptタグ内の記述のように、定型文の記述量が減るということです。

ボイラープレートとは

ボイラープレートコード (英: boilerplate code、または単にボイラープレート) は、コンピュータプログラミングでは、殆ど、または全く変化することなく、複数の場所で繰り返される定型コードのセクションのこと。
ボイラープレートコード Wikipedia

以下はボイラープレートのJavaサンプルコードで、名前と飼い主の情報を持つペットを表しています。
このうちゲッターやセッターはカプセル化を行う際の定型文であり、ペット以外のクラス宣言にも同様の構造を用いることができます。

Pet.java
public class Pet { 
 private String name;
 private Person owner;

 public Pet(String name, Person owner) {
   this.name = name;
   this.owner = owner;
 }

 public String getName() {
   return name;
 }

 public void setName(String name) {
   this.name = name;
 }

 public Person getOwner() {
   return owner;
 }

 public void setOwner(Person owner) {
   this.owner = owner;
 }
}

純粋なTypeScriptを使ってプロパティと発行されたイベントを宣言する機能

<script lang="ts">
import { defineComponent } from 'vue'
const Component = defineComponent({
  props: {
    foo: String,
  },
},
emits: ['change', 'delete'],
)
<script>

<script setup> の中で props と emits を宣言するには、defineProps と defineEmits の API を使用する必要があります。これらは <script setup> の中で自動的に利用できるようになっており、完全な型推論をサポートしています:

<script setup>
const props = defineProps({
 foo: String
})

const emit = defineEmits(['change', 'delete'])
</script> 

defineProps と defineEmits

defineProps()やdefineEmits()はインポートの必要もなく、型推論もサポートされているので使い勝手が良さそうです。

実行時のパフォーマンスの向上

パフォーマンス向上の要因として、公式ドキュメントには以下の記述があります。

テンプレートは中間プロキシなしに同じスコープ内のレンダリング関数にコンパイルされます

したがって、中間プロキシの生成が不要である分、パフォーマンスが向上します。

プロキシとは

Vue.jsの特徴であるリアクティブシステムを実現するために、例えば変数の値が変更された際に再レンダリングを行いビューを更新する必要があります。
そして、Vue.jsが値の変更を追跡・検知するために必要なものがプロキシです。

以下の例では、targetオブジェクトのプロキシをnewで作成して、オブジェクトへの操作を追跡しています。

Proxy オブジェクトにより別なオブジェクトのプロキシを作成することができ、そのオブジェクトの基本的な操作を傍受したり再定義したりすることができます。

const target = {
message1: "hello",
 message2: "everyone"
};

const handler1 = {};

const proxy1 = new Proxy(target, handler1);

MDN Web docs Proxy

よくわからない場合は、公式ドキュメントの以下の部分を理解しておくとよいです。

プロキシについては多くの文献がありますが、本当に知っておくべきことは、 プロキシとは他のオブジェクトをラップして、そのオブジェクトとのやりとりを傍受できるようにしたオブジェクトであるということです。
Vue が変更をどのように追跡するのか

レンダリング関数とは

以下の2つのコードは同じように画面表示され、blogTitleの値が変更された場合はVueが自動的にページを最新の状態に保ちます。

<h1>{{ blogTitle }}</h1>
render() {
 return h('h1', {}, this.blogTitle)
}

Render 関数

まとめ

<script setup> を使用することで

  • よりシンプルな記述が可能となり
  • 実行パフォーマンス向上
  • IDE上でのTypescript型推論パフォーマンス向上

というメリットがあります。

#参考文献

Vue.js公式ドキュメント
ボイラープレートコード Wikipedia
defineProps と defineEmits
MDN Web docs Proxy
Vue が変更をどのように追跡するのか
Render 関数

10
7
0

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
10
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?