版本:nuxt 2.15.8
前情提要一下,在 Vue 的專案下,常會需要做父子元件或是頁面之間的溝通傳值,如果說只是單層(ex:父元件 → 子元件、子元件 → 父元件、頁面 → 頁面),我們可以很簡單的使用 props
、 emit
或 event bus
即可,但在大型專案,共用資料就不是如此單純,可能會有元件內含元件、多層級的溝通,如果只用上述方法,對於開發及除錯都不便利,如下圖範例,元件 1-1 跟元件 2-1 的溝通相對複雜。
為了處理高難度溝通,VueX 狀態管理工具就誕生了,那麼在 Nuxt 專案下又該怎麼使用呢?
首先先安裝 VueX 套件 npm i [email protected]
Nuxt v2.x 必須搭配 VueX v3.x
接著在專案最外層新增 store 資料夾,並在裡面建立 .js 檔,範例使用 userInfo.js
export const state = () => {}; export const getters = {}; export const mutations = {}; export const actions = {};
|
Nuxt 專案會自動創建實例 new Vuex.Store()
,將 store 檔案包裝進模組內,像這樣:
new Vuex.Store({ modules: { userInfo: { namespaced: true, state: () => {}, getters: {}, mutations: {}, actions: {} } } });
|
VueX 基本架構:
- state:用以儲存狀態,功能同 .vue 檔內的 data,因此會使用方法來包裝,並 return 內容
- getters:功能同 computed,用以計算 state 內的狀態,不能直接改變 state
- mutations:用來更改 state,不能使用非同步語法
- actions:非同步語法只能寫在 actions,不能直接改變 state,需透過 mutations 改變 state
接著來替 store 加入一些內容吧
export const state = () => ({ count: 0, products: [ { name: 'food', onSale: false }, { name: 'drink', onSale: true } ] }); export const getters = { onSaleProducts(state) { return state.products.filter(item => item.onSale); } }; export const mutations = { increment(state, number) { state.count += number; } }; export const actions = { incrementAsync({ commit }, number) { setTimeout(() => { commit('increment', number); }, 1000); } };
|
如果要在頁面使用 store 的內容,有以下兩種方式:
透過 this.$store 操作
VueX 將 store 注入到 Vue 實例,因此我們透過 $store 就可以取得相關資料及方法,使用方式如下(範例頁面 pages/about.vue):
export default { name: 'About', computed: { count() { return this.$store.state.userInfo.count; } onSaleProducts() { return this.$store.getters['userInfo/onSaleProducts']; } }, methods: { increment(number) { this.$store.commit('userInfo/increment', number); }, incrementAsync(number) { this.$store.dispatch('userInfo/incrementAsync', number); } } }
|
但是每一筆資料或是方法,都透過 this.$store 取得,程式碼冗長,如果今天我們有三個 store 儲存庫,易讀性又更低了,難道沒有簡單的使用方式嗎?
透過輔助函式
VueX 提供一系列輔助函式(mapState, mapGetters, mapMutations, mapActions),可以幫助我們更簡易的取得 store 內容,首先必須從 VueX 引入輔助函式,接著來改寫 about.vue 頁面
import { mapState, mapGetters, mapMutations, mapActions } from 'vuex';
export default { name: 'About', computed: { ...mapState('userInfo', [ 'count' ]), ...mapGetters('userInfo', [ 'onSaleProducts' ]) }, methods: { ...mapMutations('userInfo', [ 'increment' ]), ...mapActions('userInfo', [ 'incrementAsync' ]) } }
|
透過輔助函式,減少許多程式碼,閱讀起來也輕鬆許多。
VueX 相當方便,但還是存在一個問題,就是當頁面重新整理的時候,會回復到初始狀態。
某些情境下,我們需要保留更新後的資料,例如登入後儲存使用者資訊到 store,重整頁面後資料被清空,使用者必須重新登入。為了解決這個問題,我們可以將資料儲存於 localStorage,待畫面重整後再將資料取回放入 store,或者是使用套件 vuex-persistedstate
2022.02.04 vuex-persistedstate 套件已不繼續維護更新
接下來說明如何 Nuxt 專案如何搭配 vuex-persistedstate,存放在 localStorage 的內容,可以輕易地被讀取,因此我們需要將內容加密(搭配套件 secure-ls)
首先安裝套件:npm i vuex-persistedstate
及加密套件:npm i secure-ls
,接著於 plugins 增加檔案,範例使用 persistedstate.js
import createPersistedState from 'vuex-persistedstate'; import SecureLS from 'secure-ls';
const ls = new SecureLS({ encodingType: 'aes', isCompression: false, encryptionSecret: process.env.APP_KEY });
export default ({ store, isHMR }) => { if (isHMR) { return; } window.onNuxtReady(() => { createPersistedState({ key: 'test', storage: { getItem: key => ls.get(key), setItem: (key, value) => ls.set(key, value), removeItem: key => ls.remove(key) } })(store); }); };
|
接著配置到 nuxt.config.js 內
export default { plugins: [ { src: '@/plugins/persistedstate', ssr: false } ] }
|
這樣一來即使畫面重整,store 也可以順利保存資料,在 localStorage 查看 test 內容也確實被加密了!
參考文章:
https://medium.com/itsems-frontend/vue-vuex1-state-mutations-364163b3acac
https://chiafangsung.medium.com/使用-vuex-persistedstate-維持-vuex-狀態-f0d7c522c73a
評論