Vue3 Composition API を学ぶ

📢 この記事は gemini-2.5-flash によって翻訳されました

プロジェクト作成

Node.js 16.0以上が必要だよ。以下のコマンドを実行してみて。

1
npm init vue@latest

このコマンドを実行すると、create-vueがインストールされて実行されるんだ。

setup

実行タイミング

beforeCreate()よりも早く実行されるから、thisは使えないんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
<script>
export default{
    setup(){
        console.log('setup', this)
    },
    beforeCreate(){
        console.log('beforeCreate')
    }
}
</script>

実行すると、まず「setup undefined」が出力されて、それから「beforeCreate」が表示されるよ。

データ呼び出し

<template>setup()関数内で定義したデータや関数を使いたいなら、returnしないといけないんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
<script>
export default{
    setup(){
        // data
        const msg = 'hello vue3'
        // function
        const logMsg = () => {
            console.log(msg)
        }
        // return
        return{
            msg,
            logMsg
        }
    }
}
</script>

returnしてからじゃないと、<template>で使えないからね。

1
2
3
4
<template>
<div>{{ msg }}</div>
<button @click="logMsg">button</button>
</template>

シンタックスシュガー

毎回returnするのは正直面倒だよね。実は、<script>タグにsetupって付ければ、自動でreturnしてくれるようになるんだ。さっきの例だと、こう書けるようになるよ。

1
2
3
4
5
6
<script setup>
const msg = 'hello vue3'
const logMsg = () => {
        console.log(msg)
    }
</script>

もちろん、実際はreturnされてるんだけど、自分で書かなくてよくなるってことだね。

リアクティブなデータ

reactive

reactive()関数はオブジェクト型のデータを受け取って、リアクティブなオブジェクトを返すんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<script setup>
import { reactive } from 'vue'
const state = reactive({
  count: 100
})
const addCount = () => {
  state.count++
}
</script>

<template>
<div>
  <div>{{ state.count }}</div>
  <button @click="addCount">+1</button>
</div>
</template>

ref

ref()は、プリミティブ型でも複雑な型でも受け取って、リアクティブなオブジェクトを返すよ。本質的には、渡されたデータの上にオブジェクトでラッピングして、より複雑な型にしてるんだ。これも実はreactive()を使ってリアクティブを実現してるんだよね。

だから、<script>でデータにアクセスするときは.valueを使う必要があるんだ。でも、<template>でアクセスするときは、変数名をそのまま使えばOKだよ。さっきの例をref()で書き直すとこうなるよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup>
import { ref } from 'vue'
const c = ref(0)
const addC = () => {
  c.value++
}
</script>

<template>
<div>
  <div>{{ c }}</div>
  <button @click="addC">+1</button>
</div>
</template>

実際の開発ではref()だけを使うと、より柔軟で統一感のあるコードになることが多いよ。

computed

算出プロパティのcomputed()はVue2と似てるけど、好きなときに呼び出せる関数になったんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<script setup>
import { computed, ref } from 'vue'

const list = ref([1, 2, 3, 4, 5, 6, 7, 8])

const computedList = computed(() => {
    return list.value.filter(item => item > 2)
})
</script>

<template>
<div>{{ list }}</div>
<div>{{ computedList }}</div>
</template>

こうやって作ったプロパティは読み取り専用で、書き込みはできないんだ。もし書き込みも必要なら、get()set()を明示的に宣言する必要があるよ。以下は公式のサンプルから引用したんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
<script>
const count = ref(1)
const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
    count.value = val - 1
  }
})

plusOne.value = 1
console.log(count.value) // 0
</script>

引用元: https://cn.vuejs.org/api/reactivity-core.html#computed

watch

watch()関数も同じように、1つまたは複数のデータの変更を監視して、データが変更されたときにコールバック関数を実行するんだ。ただ、immediatedeepっていう2つの追加パラメータが増えたよ。

  • 単一データ
1
2
3
watch(count, (newValue, oldValue) => {
    console.log('count changed', oldValue, newValue)
})
  • 複数データ
1
2
3
watch([count, name], ([newCount, newName], [oldCount. oldName]) => {
    console.log('count or name has changed', [newCount, newName], [oldCount. oldName])
})

サンプルだよ。ここでは簡略化したPugを使ってるけど、これもけっこう分かりやすいはずだよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<script setup>
import { ref, watch } from 'vue'

const count = ref(0)
const name = ref('John')

const addCount = () => count.value++
const changeName = () => name.value = 'Mike'

// 単一データの変更を監視
watch(count, (newValue, oldValue) =>{
    console.log('count changed', oldValue, newValue)
})

// 複数データの変更を監視
watch([count, name], (newArr, oldArr) => {
    console.log('count or name changed', oldArr, newArr)
})
</script>

<template lang="pug">
    div count: {{ count }}
    button(@click="addCount") +1
    div name: {{ name }}
    button(@click="changeName") change name
</template>

immediate

immediateはすぐに実行されるって意味で、ページに入ったらすぐに1回実行されるんだ。このときoldValueundefinedになるよ。

1
2
3
4
5
watch(count, (newValue, oldValue) =>{
    console.log('count changed', oldValue, newValue)
}, {
    immediate: true
})

deep

deepはディープウォッチャーだよ。デフォルトのwatchはシャローウォッチャーだから、複雑な型の変更は監視できないんだ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<script setup>
import { ref, watch } from 'vue'

const userInfo = ref({
    name: 'John',
    age: 18
})

const changeUserInfo = () => {
    userInfo.value.age++
}

watch(userInfo, (newValue) => {
    console.log('userInfo changed', newValue)
}, {
    deep: true
})
</script>

<template lang="pug">
    div {{ userInfo }}
    button(@click="changeUserInfo") change userInfo
</template>

複雑な型の単一プロパティ監視

deepを使うと、複雑な型のすべてのプロパティを監視することになるんだ。つまり、どのプロパティでも変更があれば関数が実行されるってこと。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<script setup>
import { ref, watch } from 'vue'

const userInfo = ref({
    name: 'John',
    age: 18
})

const changeAge = () => {
    userInfo.value.age++
}
const changeName = () => {
    userInfo.value.name = 'Mike'
}

watch(() => userInfo.value.age, (newValue, oldValue) => {
    console.log('age changed', oldValue, newValue)
})
</script>

<template lang="pug">
    div {{ userInfo }}
    button(@click="changeAge") change age
    button(@click="changeName") change name
</template>

こうすると、ageが変更されたときにだけトリガーされるし、戻り値もageの値になるんだ。

ライフサイクルフック

Vue3のライフサイクル比較

Option APIComposition API
beforeCreate/createdsetup
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeUnmountonBeforeUnmount
unmountedonUnmounted

Composition APIでの違いは、関数呼び出しになって、複数回呼び出せるようになったってことだよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<script setup>
import { onMounted } from 'vue';

// Composition APIでのbeforeCreateとcreatedは直接ここに書くよ
const getList = () => {
    console.log('バックエンドからデータを取得')
}

// 実行、ページに入ったらすぐにリクエスト
getList()
    
// ライフサイクルフックは複数回呼び出せて、順番に実行されるよ
onMounted(() => {
    console.log('ロジック1')
})
onMounted(() => {
    console.log('ロジック2')
})
</script>

Visits Since 2025-02-28

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。