Open8

Nuxt 2環境化で Pinia / Vuexを共存させて移行するメモ

redamoonredamoon

Nuxt2 -> Nuxt3にあげれない状況下(主にフレームワークに引っ張られてる)ため、VuexをPiniaに移行しつつNuxt3へアップデートを段階的に行うために調べたメモをスクラップする

Piniaを動かすためには、以下をインストールするのが前提

  • Vue2.7系を直接インストールする
  • compositionAPIを入れる
  • Vue-demiが必要
  • TypeScriptが必要(ビルド時にコケる)

修正が必要な部分

  • 既存コードdefineStoreで this参照できないようなので、 this.axios.$get などは axiosをインポートしてして axios.get に書き換えが必要
redamoonredamoon

Nuxt2配下だと以下のエラーが発生する

Can't import the named export from non EcmaScript module (only default export is available

nuxt.configのbuildの設定をいじらないといけない
https://github.com/vuejs/pinia/issues/675#issuecomment-945602370

build: {
    extend(config) {
      config.module.rules.push({
        test: /\.mjs$/,
        include: /node_modules/,
        type: 'javascript/auto',
      })
    },
  },
},
redamoonredamoon

Nuxt2の既存コードにPiniaを挿入していくため、ページ層だったりは従来のOptions APIを利用することが前提移行完了後にsetupに書き直す必要があるが完全移行前は以下のドキュメントに書いている内容で変更を加えていく

https://pinia.vuejs.org/cookbook/options-api.html

例えば stateをテンプレートに出したい場合は以下のように書く

<template>
  <div>
    {{ user }}
  </div>
</template>
<script>
import { mapState } from 'pinia'
import { useUsersStore } form '{path}/stores/XXXX'
export default {
   computed: {
     ...mapState(useUsersStore, {
           user: 'user'
           // 続く
     })
   }
}
</script>
redamoonredamoon

VuexのmapActionsとpiniaのmapActionsは共存はできない?
どちらかのmapActionsを import時に as 別名にしてあげると同時に実行することができる。

redamoonredamoon

piniaのコードを定義する
単純にuser情報を格納してfetchUserで取り出すコード

import { defineStore } from 'pinia'

export const useUsersStore = defineStore('users', {
  state: () => ({
    user: {}
  }),
  getters: {
    getUser: ({ user }) => user
  },
  actions: {
    async fetchUser() {
      try {
        console.log('Options API mounted')
        const res = await fetch('https://jsonplaceholder.typicode.com/users/1')
          .then((response) => response.json())
          .then((data) => data)
        this.user = JSON.parse(JSON.stringify(res))
      } catch (e) {
        console.error(e)
      }
    }
  }
})

Option APIで定義してあるmountedはsetupよりも前に実行される感じっぽい。
そのため、mounted実行時はPiniaのStateはundefinedだった。

<script>
import { onMounted } from 'vue'
import { useCounterStore } from '@/stores/counter'
import { useUsersStore } from '@/stores/users'

export default {
  setup() {
    const counterStore = useCounterStore()
    const usersStore = useUsersStore()
    onMounted(async () => {
      await usersStore.fetchUser()
      counterStore.increment()
      console.log('mounted')
      console.log(counterStore.count)
      console.log(usersStore.user.name)
    })
    return {
      message: 'Hello world!',
      counterStore,
      usersStore
    }
  },
  mounted() {
    this.usersStore.fetchUser()
    console.log(this.usersStore.user?.name)
    if (this.usersStore.user.name === 'Leanne Graham') return // ここの処理が通らない
    console.log('ある場合')
  }
}
</script>

<template>
  <h1 class="text-3xl font-bold underline">Hello world!</h1>
  <p>{{ message }}</p>
</template>

redamoonredamoon

piniaのactionsの実行処理が最後に呼ばれるっぽい感じ。