vuex狀態(tài)持久化 方案一

vuex狀態(tài)持久化 方案一

前言

我們都知道Vuex是一個(gè)狀態(tài)管理器,而他的缺點(diǎn)也很明確,在頁(yè)面刷新之后嫌术,Vuex中的狀態(tài)都會(huì)被重置,這對(duì)于一些不想被重置的狀態(tài)數(shù)據(jù)而言牌借,
是一個(gè)不好的表現(xiàn)度气。如果是完全用Vue構(gòu)建的 app 項(xiàng)目的話,則不需要考慮這些膨报,因?yàn)樵?app 中磷籍,不存在刷新瀏覽器的操作。

當(dāng)然现柠,如果是混合開發(fā)的院领,那還是有一些可能的,比如 app 端重新加載 webview 的話够吩,那也是等同于刷新瀏覽器的操作了比然,這個(gè)時(shí)候 Vuex 也會(huì)被重置。

而今天要討論的就是讓Vuex的狀態(tài)持久化废恋,當(dāng)然谈秫,這只是其中一個(gè)方案,這里我們需要配合本地存儲(chǔ)來(lái)達(dá)到我們的目標(biāo)鱼鼓。

問(wèn)題

  • 并不是所有狀態(tài)都需要存入本地緩存
  • 重置默認(rèn)值拟烫,并不是所有的狀態(tài)默認(rèn)值都是''

實(shí)現(xiàn)

Vuex Demo

首先我們初始化一個(gè)vue項(xiàng)目:

vue init webpack vuexDemo

初始化成功后,我們通過(guò)yarn安裝vuex

yarn add vuex

安裝成功后迄本,我們?cè)陧?xiàng)目根目錄src建立一個(gè)store文件夾硕淑,該文件夾用于存放vuex的內(nèi)容,結(jié)構(gòu)入下:

store
├── state.js            # vuex狀態(tài)集合
├── getter.js           # state的派生狀態(tài) 可對(duì)state做些過(guò)濾或者其他操作
├── action.js           # 異步mutation操作
├── mutation.js         # 修改state狀態(tài)
├── mutation-type.js    # mutation的類型
├── index.js            # vuex主文件

我們對(duì)各個(gè)文件加入點(diǎn)簡(jiǎn)單的內(nèi)容:

// state.js
const state = {
  count: 0
}

export default state

// mutation-type.js
export const SET_COUNT = 'SET_COUNT'

// mutation.js
import * as type from './mutation-type'

const mutation = {
  [type.SET_COUNT](state, data) {
    state.count = data
  }
}

export default mutation

// index.js
import Vue from 'vue'
import Vuex from 'vuex'
// import * as actions from './action'
// import * as getters from './getter'
import state from './state'
import mutations from './mutation'

Vue.use(Vuex)

export default new Vuex.Store({
  actions,
  getters,
  state,
  mutations
})

這里之所以沒(méi)有引入getteraction嘉赎,一個(gè)是因?yàn)槲覀內(nèi)?code>state中的count是直接取置媳,并沒(méi)有對(duì)其做什么特別的操作,所以getter中就省略了公条。

action它提交的是一個(gè)mutation拇囊,而且它和mutation的區(qū)別在于:

  • mutation是同步的,action可以包含異步操作
  • mutation直接修改state靶橱,而action提交的是mutation寥袭,然后再讓mutation去修改state
  • action可以一次提交多個(gè)mutation

我們現(xiàn)在就是個(gè)簡(jiǎn)單的修改state路捧,所以就先不管action

然后在vue模板中:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h3>{{ count }}</h3>
    <button @click="addStore">添加</button>
  </div>
</template>

<script>
import { mapState, mapMutations } from 'vuex'
export default {
  name: 'HelloWorld',
  computed: {
    ...mapState(['count'])
  },
  data() {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods: {
    addStore() {
      this.SET_COUNT(2)
    },
    ...mapMutations(['SET_COUNT'])
  }
}
</script>

這樣就完成了一個(gè)簡(jiǎn)單的vuex例子了,當(dāng)點(diǎn)擊添加按鈕的時(shí)候传黄,界面上的0就會(huì)變成2杰扫,并且如果裝有vue-devtools的話,
也能在vuex那一欄看到count的數(shù)值也變成了2膘掰,這里就不放動(dòng)圖演示了章姓。

持久化

接下來(lái)就是我們的關(guān)鍵內(nèi)容了,想要讓vuex持久化识埋,自然離不開本地存儲(chǔ)localStorage凡伊,我們往state里加些內(nèi)容:

// state.js
const str = window.localStorage

const state = {
  count: 0,
  account: str.getItem('account') ? str.getItem('account') : ''
}

我們加入了一個(gè)account屬性,這里表示如果緩存中有account的話就從緩存中取惭聂,沒(méi)有則為空窗声。

然后我們也同樣設(shè)置下mutationmutation-type

// mutation-type.js
export const SET_COUNT = 'SET_COUNT'

export const SET_ACCOUNT_0 = 'SET_ACCOUNT_0'

// mutation.js
import * as type from './mutation-type'

const mutation = {
  [type.SET_COUNT](state, data) {
    state.count = data
  },
  [type.SET_ACCOUNT_0](state, account) {
    state.account = account
  }
}

export default mutation

我們?cè)诙xmutation-type的時(shí)候,在尾部多加了個(gè)_0用來(lái)表示辜纲,該字段是存入緩存中的笨觅。

但是我們不會(huì)選擇在mutation中去做緩存操作,畢竟我個(gè)人認(rèn)為不適合在mutation中做過(guò)多的邏輯操作耕腾,我們選擇將這部分邏輯操作放在action中:

// action.js
import * as type from './mutation-type'
const str = window.localStorage

/**
 * 緩存操作
 */
export const withCache = ({ commit }, { mutationType, data }) => {
  commit(mutationType, data)
  // 是不是以_0結(jié)尾 是的話表示需要緩存
  if (~mutationType.indexOf('_0')) {
    setToStorage(mutationType, data)
  }
}

// 正則太爛见剩。。扫俺。就先這么寫著了
const reg = /(SET_)(\w+)(_0)/
function setToStorage(type, data) {
  let key = type.match(reg)[2].toLowerCase()
  if (typeof data === 'string') str.setItem(key, data)
  else {
    let formatData = JSON.stringify(data)
    str.setItem(key, formatData)
  }
}

上面這段代碼解決幾個(gè)問(wèn)題:

  • 因?yàn)樵?code>action中我不知道存儲(chǔ)的目標(biāo)屬于哪個(gè)type苍苞,所以將其當(dāng)做參數(shù)傳入
  • 存入緩存的key我們?nèi)〉氖?code>SET_xxx中的xxx(小寫),盡量保持和state的字段名稱一致狼纬。比如SET_A->a羹呵,SET_B_0->b
  • 本地緩存存Object類型會(huì)變成[Object object],所以針對(duì)Object類型的數(shù)據(jù)疗琉,我們需要將其轉(zhuǎn)成字符串再存入

提示
記得將actionindex.js引入

然后我們?cè)?code>vue模板中冈欢,先引入mapActions,然后進(jìn)行使用:

<script>
import { mapState, mapMutations, mapActions } from 'vuex'
export default {
  name: 'HelloWorld',
  computed: {
    ...mapState(['count'])
  },
  data() {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods: {
    addStore() {
      let account = { user: 'Randy', age: 22 }
      this.withCache({ mutationType: 'SET_COUNT', data: 2 })
      this.withCache({ mutationType: 'SET_ACCOUNT_0', data: account })
    },
    ...mapMutations(['SET_COUNT']),
    ...mapActions(['withCache'])
  }
}
</script>

直接調(diào)用withCache盈简,傳入mutationTypedata凑耻,withCache會(huì)根據(jù)mutationType判斷哪些是需要存入緩存的,哪些是不需要的柠贤。

當(dāng)然香浩,不需要存入緩存的,也可以直接調(diào)用mapMutations中的方法直接操作臼勉。

現(xiàn)在有個(gè)問(wèn)題邻吭,就是我緩存存入Object類型的是字符串類型,所以我state中的對(duì)應(yīng)數(shù)據(jù)也是字符串類型的宴霸,在模板中不利于使用镜盯,怎么辦岸裙?這時(shí)候就可以使用getter了,我們?cè)?code>getter中將其轉(zhuǎn)成Object類型即可:

// getter.js
export const getAccount = state => {
  let account = state.account
  if (typeof account === 'string' && !!account) return JSON.parse(account)
  else return account
}

提示
上面的判斷還不夠完整速缆,應(yīng)該還要判斷是否是 JSON 字符串類型,是的話再進(jìn)行JSON.parse操作恩闻,不然普通的字符串類型會(huì)報(bào)錯(cuò)艺糜,這個(gè)自行改善。

提示
記得將getterindex.js引入

然后在模板中使用mapGetters引入:

<template>
  <div>
    ...
    {{getAccount.user}}
    ...
  </div>
</template>
<script>
import { mapState, mapMutations, mapActions, mapGetters } from 'vuex'
export default {
  name: 'HelloWorld',
  computed: {
    ...mapState(['count']),
    ...mapGetters(['getAccount'])
  },
  data() {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods: {
    addStore() {
      let account = { user: 'Randy', age: 22 }
      this.withCache({ mutationType: 'SET_COUNT', data: 2 })
      this.withCache({ mutationType: 'SET_ACCOUNT_0', data: account })
    },
    ...mapMutations(['SET_COUNT']),
    ...mapActions(['withCache'])
  }
}
</script>

到這基本就結(jié)束了幢尚,現(xiàn)在刷新瀏覽器破停,那些需要持久化的屬性就不會(huì)被重置了。

可是真的就結(jié)束了嗎尉剩?那么如果我要將緩存的數(shù)據(jù)給清空或者重置呢真慢?因?yàn)?code>state中每個(gè)屬性的默認(rèn)值都是不一樣的,可能為''理茎、0黑界、false等各種類型的,那該怎么辦皂林?某問(wèn)題啦~

狀態(tài)重置

可以復(fù)制出一份state作為它的初始默認(rèn)值朗鸠,比如在store新建一個(gè)default_state.js

// default_state.js
const default_state = {
  count: 0,
  account: ''
}

export default default_state

然后我們定義一個(gè)類型為RESET_ALL_STATEmutation

// mutation-type.js
export const RESET_ALL_STATE = 'RESET_ALL_STATE'

// mutation.js
import * as type from './mutation-type'

const mutation = {
  [type.SET_COUNT](state, data) {
    state.count = data
  },
  [type.SET_ACCOUNT_0](state, account) {
    state.account = account
  },
  [type.RESET_ALL_STATE](state, data) {
    state[`${data.state}`] = data.value
  }
}

export default mutation

最后我們?cè)?code>action中定義一個(gè)重置的操作resetAllState

// action.js
import * as type from './mutation-type'
import default_state from './default_state'
const str = window.localStorage

/**
 * 重置所有狀態(tài)
 */
export const resetAllState = ({ commit }) => {
  // 循環(huán)默認(rèn)state 設(shè)置初始值
  Object.keys(default_state).forEach(state => {
    commit(type.RESET_ALL_STATE, { state, value: default_state[state] })
  })
  // 將有緩存的數(shù)據(jù)清空
  Object.keys(type).forEach(typeItem => {
    if (~typeItem.indexOf('_0')) clearStorage(type[typeItem])
  })
}

const reg = /(SET_)(\w+)(_0)/
function clearStorage(type) {
  let key = type.match(reg)[2].toLowerCase()
  str.removeItem(key)
}

完整action.js

import * as type from './mutation-type'
import default_state from './default_state'
const str = window.localStorage

/**
 * 緩存操作
 */
export const withCache = ({ commit }, { mutationType, data }) => {
  commit(mutationType, data)
  if (~mutationType.indexOf('_0')) {
    // 需要緩存
    setToStorage(mutationType, data)
  }
}

/**
 * 重置所有狀態(tài)
 */
export const resetAllState = ({ commit }) => {
  // 循環(huán)默認(rèn)state 設(shè)置初始值
  Object.keys(default_state).forEach(state => {
    commit(type.RESET_ALL_STATE, { state, value: default_state[state] })
  })
  // 將有緩存的數(shù)據(jù)清空
  Object.keys(type).forEach(typeItem => {
    if (~typeItem.indexOf('_0')) clearStorage(type[typeItem])
  })
}

const reg = /(SET_)(\w+)(_0)/
function setToStorage(type, data) {
  let key = type.match(reg)[2].toLowerCase()
  if (typeof data === 'string') str.setItem(key, data)
  else {
    let formatData = JSON.stringify(data)
    str.setItem(key, formatData)
  }
}

function clearStorage(type) {
  let key = type.match(reg)[2].toLowerCase()
  str.removeItem(key)
}

vue模板中的完整使用:

<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h3>{{ count }}</h3>
    <h4>{{ getAccount.user }}</h4>
    <button @click="addStore">添加</button>
    <button @click="clearStore">清空</button>
  </div>
</template>

<script>
import { mapGetters, mapState, mapMutations, mapActions } from 'vuex'
export default {
  name: 'HelloWorld',
  computed: {
    ...mapState(['count']),
    ...mapGetters(['getAccount'])
  },
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods: {
    addStore () {
      let account = { user: 'Randy', age: 22 }
      this.withCache({ mutationType: 'SET_COUNT', data: this.msg })
      this.withCache({ mutationType: 'SET_ACCOUNT_0', data: account })
    },
    clearStore () {
      this.resetAllState()
    },
    ...mapMutations(['SET_COUNT']),
    ...mapActions(['withCache', 'resetAllState'])
  }
}
</script>

好了,現(xiàn)在是真的結(jié)束了础倍。

總結(jié)
可能代碼有些亂烛占,但是大致的思路我想應(yīng)該還是都能理解的。
如果還有有其他vuex的持久化方式沟启,還會(huì)繼續(xù)更新的忆家。
也歡迎大家一同思考。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末德迹,一起剝皮案震驚了整個(gè)濱河市芽卿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌浦辨,老刑警劉巖蹬竖,帶你破解...
    沈念sama閱讀 218,036評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異流酬,居然都是意外死亡币厕,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門芽腾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)旦装,“玉大人,你說(shuō)我怎么就攤上這事摊滔∫蹙睿” “怎么了店乐?”我有些...
    開封第一講書人閱讀 164,411評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)呻袭。 經(jīng)常有香客問(wèn)我眨八,道長(zhǎng),這世上最難降的妖魔是什么左电? 我笑而不...
    開封第一講書人閱讀 58,622評(píng)論 1 293
  • 正文 為了忘掉前任廉侧,我火速辦了婚禮,結(jié)果婚禮上篓足,老公的妹妹穿的比我還像新娘段誊。我一直安慰自己,他們只是感情好栈拖,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,661評(píng)論 6 392
  • 文/花漫 我一把揭開白布连舍。 她就那樣靜靜地躺著,像睡著了一般涩哟。 火紅的嫁衣襯著肌膚如雪索赏。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評(píng)論 1 304
  • 那天染簇,我揣著相機(jī)與錄音参滴,去河邊找鬼。 笑死锻弓,一個(gè)胖子當(dāng)著我的面吹牛砾赔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播青灼,決...
    沈念sama閱讀 40,288評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼暴心,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了杂拨?” 一聲冷哼從身側(cè)響起专普,我...
    開封第一講書人閱讀 39,200評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎弹沽,沒(méi)想到半個(gè)月后檀夹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡策橘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,837評(píng)論 3 336
  • 正文 我和宋清朗相戀三年炸渡,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丽已。...
    茶點(diǎn)故事閱讀 39,953評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蚌堵,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情吼畏,我是刑警寧澤督赤,帶...
    沈念sama閱讀 35,673評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站泻蚊,受9級(jí)特大地震影響躲舌,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜性雄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,281評(píng)論 3 329
  • 文/蒙蒙 一孽糖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧毅贮,春花似錦、人聲如沸尘奏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)炫加。三九已至瑰煎,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間俗孝,已是汗流浹背酒甸。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赋铝,地道東北人插勤。 一個(gè)月前我還...
    沈念sama閱讀 48,119評(píng)論 3 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像革骨,于是被迫代替她去往敵國(guó)和親农尖。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,901評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • Vuex 概念篇 Vuex 是什么良哲? Vuex 是一個(gè)專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式盛卡。它采用集中式...
    Junting閱讀 3,079評(píng)論 0 43
  • 安裝 npm npm install vuex --save 在一個(gè)模塊化的打包系統(tǒng)中,您必須顯式地通過(guò)Vue.u...
    蕭玄辭閱讀 2,937評(píng)論 0 7
  • Vuex Vuex是一個(gè)專門為Vue.js應(yīng)用所設(shè)計(jì)的集中式狀態(tài)管理架構(gòu)筑凫,它借鑒了Flux和Redux的設(shè)計(jì)思想滑沧,...
    JunChow520閱讀 1,570評(píng)論 2 0
  • vuex是一個(gè)狀態(tài)管理模式,通過(guò)用戶的actions觸發(fā)事件巍实,然后通過(guò)mutations去更改數(shù)據(jù)(你也可以說(shuō)狀態(tài)...
    Ming_Hu閱讀 2,023評(píng)論 3 3
  • 一起去幼兒園的第七天滓技。 每天早上孩子們的情緒會(huì)決定他們一整天的學(xué)習(xí)效率,玩的狀態(tài)蔫浆。昨天早上因?yàn)槲业臓顟B(tài)不好殖属,大姨媽...
    瑞瑪和麥琪閱讀 158評(píng)論 0 0