スロット
親コンポーネント
<template>
<Ko>ほげほげ</Ko>
</template>
子コンポーネント
<template>
<slot>デフォルト値</slot>
</template>
propsとslotの使い分け
- 文字列や数値などのデータを受け渡すだけならpropsを使うべき。
- 状態で中の要素が変わるならslotを使うべき。
- v-ifなどで中の要素が動的に変わる場合は、slotを使った方が子コンポーネント側にpropsで状態を渡してやるよりも依存関係が薄くなる。
<!-- 以下のような場合はslotにするべき -->
<template v-if="flag">
<h1>Vue.js!</h1>
</template>
<template v-else>
<p>Error!!!</p>
</template>
名前付きスロット
- 複数のスロットを差し込みたい場合に使う。
- 名前付きスロットは、親コンポーネントのtemplate要素のv-slot属性で名前を指定する。
- 「v-slot:スロット名」は省略記法で「#スロット名」とすることができる。
- name属性を指定しないslot要素は、name=”default”となる。
親コンポーネント
<template>
<Ko>
<template v-slot:default>defaultスロットです。</template>
<template #explanation>説明文のテストです。</template>
</Ko>
</template>
子コンポーネント
<template>
<slot>defaultスロットのデフォルト値</slot>
<slot name="explanation">名前付きスロットのデフォルト値</slot>
</template>
スコープ付きスロット
- 親コンポーネントから子コンポーネントに渡すスロット内で子コンポーネントの変数を使うことができる。
- 子コンポーネントでslot要素のv-bindディレクティブで変数をバインドすると、親コンポーネントでv-slot=”変数名”でオブジェクトとして受け取ることができる。
- 数が少ない場合は分割代入で受けた方がよい。
<template v-slot="{msg, num}">msg: {{ msg }}、num: {{ num }}</template>
親コンポーネント
<template>
<Ko>
<template v-slot="slotProps">msg: {{ slotProps.msg }}、num: {{ slotProps.num }}</template>
</Ko>
</template>
子コンポーネント
<script setup>
import { ref } from 'vue'
const msg = ref('hoge')
</script>
<template>
<slot :msg="msg" :num="777"></slot>
</template>
以下では子コンポーネントの変数を参照することはできない。
親コンポーネント
<template>
<Ko>{{ msg }}</Ko>
</template>
子コンポーネント
<script setup>
import { ref } from 'vue'
const msg = ref('hoge')
</script>
<template>
<slot></slot>
</template>
Pinia
- Props, Emitによるコンポーネント間でデータを受け渡しはローカルステートと呼ばれる。
- 全体で共有したデータはグローバルステートと呼ばれ、Vue.js2ではVuexだったがVue.js3ではPiniaというライブラリを利用することが推奨されている。
- npm install piniaでインストール
Piniaの使い方
ステートの作成
- mkdir stores
- touch stores/counter.js
- stateにデータを返却する関数を定義する。
- defineStoreの戻り値(関数)をエクスポートする。
- 慣例としてエクスポートする関数の名前の先頭はuseにする。
- defineStore関数の第1引数はストアIDで、ユニークな文字列とする。
- ストアIDは通常は必要となることはない。
用途は「devToolで確認する際の識別子となる」「1つのファイルに複数のストアを定義した場合、$subscribe関数で監視した際の識別子となる」くらい。
import { defineStore } from "pinia";
export const useStore = defineStore('counter', {
state: () => ({
count: 0,
score: 0,
})
})
main.jsの修正
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import './style.css'
import App from './App.vue'
const pinia = createPinia()
createApp(App).use(pinia).mount('#app')
コンポーネントからアクセス
<script setup>
import { useStore } from '../../stores/counter'
const store = useStore()
</script>
<template>
<p>store.counter: {{ store.counter }}</p>
</template>
ゲッター
- ステートに対して算出プロパティを設定したい場合に利用する。
- gettersに引数としてステートを受け取る関数を定義する。
import { defineStore } from "pinia";
export const useStore = defineStore('counter', {
state: () => ({
count: 1,
memo: 'hoge',
}),
getters: {
double: (state) => state.count * 2
}
})
コンポーネントからアクセス
<script setup>
import { useStore } from '../../stores/counter'
const store = useStore()
</script>
<template>
<p>store.counter: {{ store.double }}</p>
</template>
アクション / リセット
- ステートのデータを更新するためのメソッドをactionsに定義する。
import { defineStore } from "pinia";
export const useStore = defineStore('counter', {
state: () => ({
count: 1,
memo: 'hoge',
}),
getters: {
double: (state) => state.count * 2
},
actions: {
countUp(step=1) {
this.count += step
}
}
})
コンポーネントからアクセス
- ストアの$reset()メソッドでステートをリセットすることができる。
store.count = 100
のように直接代入もできるが、actionsで定義した更新用メソッドを使うべき。
<script setup>
import { useStore } from '../../stores/counter'
const store = useStore()
const countUp = () => {
store.countUp()
}
const reset = () => {
store.$reset()
}
</script>
<template>
<p>store.count: {{ store.count }}</p>
<p><button @click="countUp">countUp</button></p>
<p><button @click="store.countUp(3)">3 countUp</button></p>
<p><button @click="reset">reset</button></p>
<p><button @click="store.$reset()">reset</button></p>
</template>
storeToRefs関数
- storeToRefs関数を使うとステートをリアクティブな状態で取得することができる。
<script setup>
import { useStore } from '../../stores/counter'
import { storeToRefs } from 'pinia'
const store = useStore()
let {memo} = storeToRefs(store)
memo.value = 'hogehoge'
</script>
<template>
<p>storeToRefs: {{store.memo}}</p>
</template>