「Vue.js3やさしい入門書」の感想・備忘録2

スポンサーリンク
「Vue.js3やさしい入門書」の感想・備忘録1の続き

スロット

親コンポーネント

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

コメント