Vue.JS のホットリロードされてもストアが初期化されないためには

## 概要

Vuex にある [replaceState](https://vuex.vuejs.org/ja/api/#replacestate) という便利なメソッドの紹介です。

Vue.JS で開発している場合、ソースを更新した際に自動的にホットリロードが走って、ブラウザ側の画面が自動的に最新に反映されるという嬉しい機能を皆さんも使っていることと思います。
ただ、その際、**ストアが初期化** されてしまいます。
ストアが初期化されるのでホットリロードが走った途端、画面が真っ白になることも。

そこで、ストアが初期化されないために一時的に state の内容を更新/復元する開発用コンポーネントを作ったのですが、
簡単に作れるので作り方と、そこで使ってる技術を紹介します。


## どんなことをしたいのか

- モジュールで階層化されたストア(Vuex)の state の全内容を sessionStorage に保存
- sessionStorage から取り出した内容でストアを復元


## 方法

```js:ストア(Vuex)のstateの全内容を取り出す
const allState = this.$store.state
```

```js:sessionStorageに保存する
// モジュールも含めて全階層分取得できます
sessionStorage.setItem('stateを保存するキー名', JSON.stringify(allState))
```

```js:sessionStorageから取り出す
const allState = JSON.parse(sessionStorage.getItem('stateを保存するキー名'))
```

↓これが肝です。これで復元できるのです。

```js:ストア(Vuex)のstateの全内容を復元する
if (allState) {
    this.$store.replaceState(allState)
}
```



## 実装例の共有

見た目はともかく、下記の機能を実装した例を紹介します。

- 画面上に「開発用ツール」という枠を常時表示
- 「State保存」ボタンで現状の Store (Vuex) の State がモジュールも含めてすべて保存
- 値が保存されていたらホットリロードで復元
- 「クリア」ボタンで復元を解除
- ツール自体を隠したり、表示させたりできるように
- 開発中(localhost:8080)でのアクセスのみ反応するように


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29899/99ff8c89-bfa1-574a-3565-c759684e40a4.png)
↑↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29899/4ac05873-49de-b653-2dc4-6322af8b4242.png)


## ソース

```vuejs:DevTool.vue
<template>
  <div v-if="isDevelop" :class="{hide: !isShown}" style="position: absolute; top: 0; left: 0; background-color: white; border: 2px black solid; z-index: 99999; padding: 4px; display: flex;">
    <div style="margin-right: 5px;" @click="toggleShown">[開発用ツール]</div>
    <div v-if="!isStateSaved"><button @click="saveState">State保存</button> (Stateの状態をsessionStorageに保存します。)</div>
    <div v-else>State保存中 <button @click="deleteState">クリア</button> (sessionStorageに保存されているStateの内容を削除し、リロードしても復帰しないようにします。)</div>
  </div>
</template>

<script>
export default {
  name: 'DevelopTool',
  data () {
    return {
      isShown: true, // 見えるようにするか。隠したい場合は透明になる。（存在はする）
      isStateSaved: false,
    }
  },
  computed: {
    isDevelop () {
      return location.host === 'localhost:8080'
    }
  },
  created () {
    if (!this.isDevelop) return
    const allState = JSON.parse(sessionStorage.getItem('develop-allState'))
    if (allState) {
      console.log('developTool loadState', allState)
      this.$store.replaceState(allState)
    }
    this.isStateSaved = !!allState
  },
  methods: {
    saveState () {
      if (!this.isShown) return this.toggleShown()
      const allState = this.$store.state
      sessionStorage.setItem('develop-allState', JSON.stringify(allState))
      console.log('developTool saveState', allState)
      this.isStateSaved = true
    },
    deleteState () {
      if (!this.isShown) return this.toggleShown()
      sessionStorage.removeItem('develop-allState')
      console.log('developTool deleteState')
      this.isStateSaved = false
    },
    toggleShown () {
      this.isShown = !this.isShown
    },
  },
}
</script>

<style>
  .hide {
    opacity: 0;
  }
</style>
```

これを、トップレベルの Vue ファイルの template のどこかに配置すればOK。


## 備考: vuex-persistedstate では要求満たせなかった

[vuex-persistedstate](https://github.com/robinvdvleuten/vuex-persistedstate) プラグインを使って開発時だけ永続化しようかとも思ったのですが、いったん有効化すると無効化するのが難しいという問題があり、自作した方が早いと判断しました。


以上、何かツッコミありましたら是非ください！