📄

Vue2 + Options APIの記述のVue3 + <script setup>での書き方

2022/08/08に公開

Vue2 + Options APIからComposition APIをぶっ飛ばしてVue3 + <script setup>に乗り換えたら、記述が変わりまくっててインターネット浦島太郎状態になってしまったので、両者の対応する書き方をまとめた

とは言っても、Composition APIと<script setup>の記述はあまり違わないため、ほとんどOptions APIとComposition APIの対応する書き方のまとめみたくなってるかもしれない

※個人的なメモ程度のものですが、間違いに気づいた場合は指摘していただけると嬉しいです

使用するコンポーネントの定義

インポートするだけで使用可能になったため、必要なくなった

というかコンポーネントの定義に限らず、トップレベルで宣言されたものはテンプレートでそのまま使用可能になった

Vue2 + Options APIでの書き方

App.vue
<template>
  <Hoge />
</template>

<script>
import Hoge from '@/components/Hoge.vue';

export default {
  components: {
    Hoge,
  },
}
</script>

Vue3 + <script setup>での書き方

App.vue
<script setup>
import Hoge from '@/components/Hoge.vue';
</script>

<template>
  <Hoge />
</template>

ブロックの順序

今までは<template>, <script>, <style>の順だったが、<script setup>では<template><script>が逆になる

Vue2 + Options APIでの書き方

<template>
</template>

<script>
</script>

<style>
</style>

Vue3 + <script setup>での書き方

<script setup>
</script>

<template>
</template>

<style>
</style>

data

refまたはreactiveでラップしてリアクティブにする

オブジェクトにはreactive、それ以外にはrefを使うらしいが、全部refでも良さそう
値へのアクセスはvalueプロパティを見ることで可能、テンプレート内では.valueを省略できる

参考:
https://zenn.dev/azukiazusa/articles/ref-vs-article

Vue2 + Options APIでの書き方

<template>
  <div>{{ count }}</div>
</template>

<script>
// ~~~~略~~~~
data() {
  return {
    count: 0,
  }
},
created() {
  console.log(this.count);
}
</script>

Vue3 + <script setup>での書き方

<script setup>
import { ref } from 'vue';
const count = ref(0);

console.log(count.value);
</script>

<template>
  <div>{{ count.value }}</div>
</template>

props

definePropsを使って定義する(インポートは必要ない)

Vue2 + Options APIでの書き方

<template>
  <div>{{ initialCount }}</div>
</template>

<script>
// ~~~~略~~~~
props: {
  initialCount: Number,
},
created() {
  console.log(this.initialCount);
}
</script>

Vue3 + <script setup>での書き方

<script setup>
const props = defineProps({
  initialCount: Number,
});

console.log(props.initialCount);
</script>

<template>
  <div>{{ initialCount }}
</template>

emit

defineEmitsを使ってカスタムイベントを定義したうえでemitする

こちらもdefinePropsと同様にインポートは必要ない

以下はParent.vueからChild.vuecountの値をpropsで渡し、Child.vueのボタンがクリックされたらcountの値を+10してParent.vueに渡し、countの値を変更するサンプル

Vue2 + Options APIでの書き方

Child.vue
<template>
  <button @click="$emit('update-count', count + 10)">add 10</button>
</template>

<script>
// ~~~~略~~~~
props: {
  count: Number,
}
</script>
Parent.vue
<template>
  <div>
    <p>{{ count }}</p>
    <Child :count="count" @update-count="updateCount" />
  </div>
</template>

<script>
import Child from '@/components/Child.vue';
// ~~~~略~~~~
components: {
  Child,
},
data() {
  return {
    count: 0,
  }
},
methods: {
  updateCount(newCount) {
    this.count = newCount;
  },
}
</script>

Vue3 + <script setup>での書き方

Child.vue
<script setup>
const props = defineProps({
  count: Number,
});
const emit = defineEmits([
  'update-count',
]);
</script>

<template>
  <button @click="emit('update-count', count + 10)">add 10</button>
</template>
Parent.vue
<script setup>
import { ref } from 'vue';
import Child from '@/components/Child.vue';

const count = ref(0);
const updateCount = (newCount) => {
  count.value = newCount;
};
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <Child :count="count" @update-count="updateCount" />
  </div>
</template>

ライフサイクルフック

基本的にon~でラップするようになった(インポートする必要はない)

Vue2でのbeforeDestroy, destroyedはそれぞれVue3でbeforeUnmount, unmountedに変更されている(使ったことないけど)

また、beforeCreate, createdは直接<script setup>内に処理を書くようになっているので注意

Options API setup 内のフック
beforeCreate 不要*
created 不要*
beforeMount onBeforeMount
mounted onMounted
beforeUpdate onBeforeUpdate
updated onUpdated
beforeUnmount onBeforeUnmount
unmounted onUnmounted
errorCaptured onErrorCaptured
renderTracked onRenderTracked
renderTriggered onRenderTriggered
activated onActivated
deactivated onDeactivated

表の引用元:
https://v3.ja.vuejs.org/guide/composition-api-lifecycle-hooks.html

Vue2 + Options APIでの書き方

created() {
  console.log('created');
},
mounted() {
  console.log('mounted');
}

Vue3 + <script setup>での書き方

console.log('created');
onMounted(() => {
  console.log('mounted');
});

watch

Vue3から通常のwatchに加え、watchEffectが追加された

違いは以下の通り

watch watchEffect
監視対象の指定 明示的に記述 自動(関数内で使用されたリアクティブな値を監視)
変更前後の値 取得可能 取得不可
定義時の実行 実行されない 実行される

参考:
https://qiita.com/doz13189/items/d09cfc6e1ff38621c2cc

Vue2 + Options APIでの書き方

// ~~~~略~~~~
data() {
  return {
    count: 0,
  }
},
watch: {
  count(newValue, oldValue) {
    console.log(newValue, oldValue);
  }
}

Vue3 + <script setup>での書き方

import { ref, watch, watchEffect } from 'vue';

const count = ref(0);
const count2 = ref(0);

watch(count, (newValue, oldValue) => {
  console.log('count newValue: ' + newValue);
  console.log('count oldValue: ' + oldValue);
});

watchEffect(() => {
  // count2が更新された場合は実行されない
  console.log('count: ' + count.value);
});

$refs

<template>での記述は変わらないが、refでラップした同名の変数に格納してから参照するようになった

Vue2 + Options APIでの書き方

<template>
  <p ref="test">test component</p>
</template>

<script>
export default {
  mounted() {
    // <p>test component</p>
    console.log(this.$refs.test);
  },
}
</script>

Vue3 + <script setup>での書き方

<script setup>
import { ref } from 'vue';

// 初期値はnullでよい
const test = ref(null);

onMounted(() => {
  // <p>test component</p>
  console.log(test.value);
});
</script>

<template>
  <p ref="test">test component</p>
</template>

$el

Vue3でマルチルートノードコンポーネントがサポートされ、コンポーネントのルート要素は1つのみという制限がなくなったため代わりにrefを使うようになった

Vue3 + Options APIでは使用できるが、一貫性を保つためrefの使用が推奨されている
Vue3 + Composition APIおよび<script setup>ではそもそもthisが参照できないため使用不可?

Vue3で$elを使用した場合の挙動(下記引用のGoogle翻訳):

  • ルート要素が1つのコンポーネントの場合、$elはその要素を指す
  • テキストルートを持つコンポーネントの場合、$elはテキストノードを指す
  • 複数のルート要素を持つコンポーネントの場合、$elはVueがDOM内のコンポーネントの位置を追跡するために使用するプレースホルダDOM要素(テキストノード、またはSSRハイドレーションモードのコメントノード)となる
  • For components with a single root element, $el will point to that element.
  • For components with text root, $el will point to the text node.
  • For components with multiple root nodes, $el will be the placeholder DOM node that Vue uses to keep track of the component's position in the DOM (a text node, or a comment node in SSR hydration mode).

引用元:
https://vuejs.org/api/component-instance.html#el

おわり

Vueの(個人的に思う)基本的な機能が書けたので、ひとまずここまでにしておく
またどこかで「これって<script setup>だとどう書くんだ…?」っていうのが出てきたら追記するつもり

Discussion