Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
8
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Nuxt.jsでウェブストレージを扱う

はじめに

ウェブストレージ便利ですよね、特にローカルストレージはCookieが使えないPWAや、DBを用意するほどでもないちょっとしたデータを保存したいときに使うことが多いのではないかと思います。

ただ、いざウェブストレージのデータを扱おうと実装を進めるとデータの同期や保存のタイミング等でつっかかることがままあります。
そこで今回はNuxt.jsでウェブストレージを簡単に扱う手法を考えましたので簡単にご紹介しようと思います。

実装方針

コンポーネントやページから直接ウェブストレージにアクセスしていると処理やデータが散乱してしまい辛い状況に陥りやすいです。
そこで、ウェブストレージの参照や処理は全てストアに一本化します。
各コンポーネントやページは共通のステートやアクションを参照することになりますのでデータが非常に扱いやすくなります。

実装

入力した値をウェブストレージに保存する簡単なデモを作成します。
今回はローカルストレージを使用しますがセッションストレージでも同様の方法で対応可能です。

ウェブストレージ操作用のクラスを作成

1つのウェブストレージを扱うだけならクラスを作るまでもないのですが、今回は複数のウェブストレージを管理することを前提に作成しておきます。
実装例ですので登録・削除する機能だけ用意してあります。

lib/storage.js
export default class {
  constructor(key) {
    this.key = key
    this.data = []
  }
  init() {
    this.data = JSON.parse(localStorage.getItem(this.key)) || []
  }
  save() {
    localStorage.setItem(this.key, JSON.stringify(this.data))
  }
  regist(payload) {
    this.data.push(payload)
    this.save()
  }
  remove(payload) {
    this.data = this.data.filter(el => el !== payload)
    this.save()
  }
}

クライアント起動時のプラグイン作成

クライアント起動時にウェブストレージのデータをストアへ注入する必要があるのですが、Nuxtのストアにはクライアント起動時の関数は用意されていませんのでその機能を作成します。
Nuxtのデフォルトにあるアクション「nuxtServerInit」にならって「nuxtClientInit」という名称で管理します。

plugins/nuxt-client-init.js
export default ({ store }) => {
  for (const key in store._actions) {
    if (key.match(/nuxtClientInit/)) {
      store.dispatch(key)
    }
  }
}
nuxt.config.js
plugins: [
  { src: '~/plugins/nuxt-client-init', mode: 'client' }
]

nuxt-client-init-moduleを使う場合

nuxt-client-init-moduleというモジュールが既にあります。このモジュールはルートストアに「nuxtClientInit」というアクションを用意するものですので、必要なアクションをルートストアで動かしてください。

$ yarn add nuxt-client-init-module
nuxt.config.js
modules: ['nuxt-client-init-module']
store/index.js
export const actions = {
  nuxtClientInit({ dispatch }) {
    dispatch('module/nuxtClientInit')
  }
}

ストア実装

ストアのアクションはあくまでインスタンスの関数を実行し、その結果をステートへ返すだけのシンプルな設計とします。
アクションに直接ウェブストレージ周りの機能を実装してもよいのですが、複数ウェブストレージを扱ったり機能が複雑になってくると煩雑になりやすいので、ウェブストレージの処理とストアの処理を明確に分担するように設計しています。

store/message.js
import LocalStorage from '~/lib/storage'

const storageMessage = new LocalStorage('message')

export const state = () => ({
  data: []
})

export const mutations = {
  dataUpdate(state, payload) {
    state.data = [...payload]
  }
}

export const actions = {
  nuxtClientInit({ commit }) {
    storageMessage.init()
    commit('dataUpdate', storageMessage.data)
  },
  regist({ commit }, payload) {
    storageMessage.regist(payload)
    commit('dataUpdate', storageMessage.data)
  },
  remove({ commit }, payload) {
    storageMessage.remove(payload)
    commit('dataUpdate', storageMessage.data)
  }
}

完成

あとは通常のストアと同様に利用できます。

pages/index.vue
<template>
  <div>
    <form @submit.prevent="regist()">
      <input type="text" v-model="message">
      <button type="button" @click="regist()">
        登録
      </button>
    </form>
    <ul>
      <li v-for="(item, index) in messageData" :key="index">
        {{ item }}
     <button type="button" @click="remove(item)">
            削除
         </button>
      </li>
    </ul>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  data() {
    return {
      message: ''
    }
  },
  computed: {
    ...mapState({
      messageData: state => state.message.data
    })
  },
  methods: {
    regist() {
      if (this.message) {
        this.$store.dispatch('message/regist', this.message)
        this.message = ''
      }
    },
    remove(payload) {
      if (window.confirm('削除しますか?')) {
        this.$store.dispatch('message/remove', payload)
      }
    }
  }
}
</script>

おまけ(Vueインスタンス版)

小規模でストア用意するほどでもないよって人向けにプラグインでVueインスタンスへ注入する方法も記載しておきます。
以下のデモでは上記で使ったクラスを流用してください。

プラグインを作成する

Vueインスタンスへ注入するプラグインファイルを作成します。

plugins/storage.js
import Vue from 'vue'
import LocalStorage from '~/lib/storage'

Vue.prototype.$storageMessage = new LocalStorage('message')
Vue.prototype.$storageMessage.init()
nuxt.config.js
  plugins: [
    { src: '~/plugins/storage', mode: 'client' }
  ]

完成

設定はこれだけで後はページやコンポーネントからウェブストレージのデータを操作できます。

pages/index.vue
<template>
  <div>
    <form @submit.prevent="regist()">
      <input type="text" v-model="message">
      <button type="button" @click="regist()">
        登録
      </button>
    </form>
    <ul>
      <li v-for="(item, index) in messageData" :key="index">
        {{ item }}
     <button type="button" @click="remove(item)">
            削除
         </button>
      </li>
    </ul>
  </div>
</template>

<script>
import { mapState } from 'vuex'

export default {
  data() {
    return {
      messageData: [],
      message: ''
    }
  },
  mounted() {
    this.update()
  },
  methods: {
    update() {
      this.messageData = this.$storageMessage.data
    },
    regist() {
      if (this.message) {
        this.$storageMessage.regist(this.message)
        this.update()
        this.message = ''
      }
    },
    remove(payload) {
      if (window.confirm('削除しますか?')) {
        this.$storageMessage.remove(payload)
        this.update()
      }
    }
  }
}
</script>

さいごに

ウェブストレージのデータを更新するときに「ウェブストレージにあるデータ」と「ウェブストレージから引っ張ってきたデータ」と2つを管理する必要があるのでそこがもたつきやすいですよね。
今回のように「ウェブストレージ←→ストア←→ページ・コンポーネント」という形で一方向の処理で管理すると、土台さえ作ってしまえばウェブストレージ周りの処理を意識せずに扱えるのでオススメです。
大した手法ではないのですがウェブストレージの実装で手をこまねいている方は参考にしていただければ幸いです!

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
8
Help us understand the problem. What are the problem?