iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 23
2
Modern Web

RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄系列 第 23

Day 23. Vuex 和 Cookie 哪個好? 小朋友才做決定,我兩個都要

  • 分享至 

  • xImage
  •  

原打算在介紹完 page component 之後繼續介紹個 layout component 和 <slot>,但因為這兩個相對簡單所以還是不要打醬油,直接跳到 Vuex 和 Cookie 了。

Vuex 和 Cookie 非常類似,都是將網站 (app) 的一些資料或狀態暫存下來,這樣當頁面切換之後,部分資料跟狀態可以再被使用,不需要重新發送 request 跟 API 要資料。其中,兩者最大的不同是,當使用者重新整理畫面或是重新拜訪網站,Vuex 裡面的資料會消失,而 cookie 不會,所以通常會依需求交互使用兩者!

Vuex

在 Nuxt 專案的 store 底下,每一個 .js 檔案都會被視為一個模組 (Vue Module),其中,store/index.js 會是主要 module。比較特別的是,我們不需要像在 Vue 專案裡面,需要將 modules 個別加到 Vuex Store 實例中。

一個 module 基本上會有下列四項屬性和方法:

  • state:存放要儲存的狀態或是資料。Vue 專案以及 Vuex 官網的介紹中,state 是一個物件,然而在 Nuxt,state 是匿名方法回傳一個物件:
// Vue:
const state = {
    jwtToken: undefined,
};

// Nuxt:
const state = () => {
    return {
        jwtToken: undefined,
    };
}
  • actions: 當我們在 component 中要設定、改變 state 中的資料,都需要透過 actions 底下的自訂方法,進一步呼叫 mutations 方法。Vuex 官方文件有特別提到 actions 是支持異步操作。

下面是一個儲存、更新來自 API 的 JWT token:

const actions = {
    setUserToken({ commit }, token) {
        if (token) {
            commit('UPDATE_JWT_TOKEN', token);
        }
    },
};

actions 的第一個變數是 context 物件,其中 commit 屬性是呼叫 mutations 的方法,第一個變數是 mutations 的名稱。

  • mutations:真正更新 state 的方法,第一個參數 state 會自動注入,要帶進來的資料在第二個參數。
const mutations = {
    ['UPDATE_JWT_TOKEN'](state, token) {
        state.jwtToken = token;
    },
};
  • getters:component 用來取得 state 的資料,在官方的說明當中建議大家將 getters 視為 computed 方法的概念 (實際上在 component 引入時也是掛在 computed 底下)。
const getters: {
    userToken(state) {
        return state.jwtToken;
    }
}

完整的 Vuex store module (auth.js)) 如下:

const state = () => {
    return {
        // ...
    };
};

const actions = {
    // ...
};

const mutations = {
    // ...
};

const getters = {
    // ...
};

// 最終要匯出
export default {
    state,
    actions,
    mutations,
    getters,
    namespaced: true, // 多模組的時候,當有相同名稱的變數或方法才能區隔開
};

在 component 裡面,會透過 mapGetters, mapActions 兩個方法存取 state 資料:

import { mapGetters, mapActions } from 'vuex';
export default {
    computed: {
        // 第一個參數是 module 檔案名稱
        ...mapGetters('auth', ['userToken']),
    },
    actions: {
        ...mapActions('auth', ['setUserToken']),
    },
    methods: {
        async updateProfile() {
            try {
                const requestData = { /* ... */ };
                const headers = {
                    Authorization: `Bearer ${this.userToken}`,
                };
                const { data } = await this.$axios.post('/user/edit', requestData, { headers });
                const tokenNeedToUpdate = data.token !== this.userToken;
                if (tokenNeedToUpdate) {
                    this.setUserToken(data.token);
                }
            } catch (error) {
                // ...
            }
        }
    },
}

Cookie

如同一開始說的,Vuex 資料是會揮發的,如果希望將資料保存一段時間,可以採用 cookie 的方式。 個人習慣使用 js-cookie 套件存取 cookie,另外,雖然我們可以在任何地方存取 cookie 不過為了方便管理,習慣上會一樣寫在 Vuex store module 裡面。

上面 JWT token 範例如果要將 token 存入 cookie 則可以依下列方式改寫:

  1. 在 cmd 執行下列指令,安裝套件
npm i js-cookie
  1. 在 module (auth.js) 中引入套件
import Cookies from 'js-cookie';
  1. mutations 加入儲存 cookie 的部分:
const mutations = {
    ['UPDATE_JWT_TOKEN'](state, token) {
        state.jwtToken = token;
        Cookies.set('auth-token', token);
    },
};
  1. getters 加入取的 cookie 的部分:
const getters = {
    userToken(state) {
        return state.jwtToken || Cookies.get('auth-token');
    },
};

nuxtServerInit

在 Cookie 的例子中,當 Vuex store 的資料揮發時,應該是所有資料都揮發,因此在 getters 取資料時只會存取 cookie 資料,這樣的做法就失去 Vuex store 的意義。

還好,Nuxt 在 index module 的 actions 增加了一個 nuxtServerInit 方法。此方法是當 server 端 (node.js) 被執行時,會呼叫的方法。他主要的功能是 「從 request 中取得資料後並從新加入到 state 當中」,下面是利用 nuxtServerInit 將上面例子接續改寫的方法:

  1. 在 cmd 中執行下列指令安裝 cookieparser 套件。
npm i cookieparser
  1. store 底下建立 index module (index.js)

  2. 將原本 auth module 的內容複寫到 index module 中

  3. 引入 cookieparser

const cookieparser = process.server ? require('cookieparser') : undefined;
  1. actions 當中加入 nuxtServerInit 方法,其中 nuxtServerInit 的兩個參數都是自動注入的。
const actions = {
    nuxtServerInit({ commit }, { req }) {
        const hasCookieInReq = !!req.headers.cookie;
        if (hasCookieInReq) {
            try {
                const allCookies = cookieparser.parse(req.headers.cookie);
                const token = allCookies['auth-token'];
                commit('UPDATE_JWT_TOKEN', token);
            } catch (error) {
                // ...
            }
        }
    },
};
  1. getters 移除 cookie 的部份
const getters = {
    userToken(state) {
        return state.jwtToken;
    },
}

有些網站變動頻率很低的常數資料,可以透過 Vuex 記住,當然考慮到 Vuex 資料在重新載入頁面或重新拜訪網站的時候會揮發,因此我們搭配了 Cookie。同時利用 Nuxt 本身在 index module 中所新增的 nuxtServerInit 方法,讓我們可以在伺服器中取得 cookie 中的資料,並重新加到 state 之中。

其實至此,Nuxt 主要要注意的項目也差不多了 (總覺得一切太突然 XD),明天要介紹如何撰寫 Nuxt plugin,讓 api 的使用更方便!


上一篇
Day 22. 話說 Nuxt 的 VueRouter 呢?
下一篇
Day 24. Plugin - 懶人救星
系列文
RRR撞到不負責之 Laravel + Nuxt.js 踩坑全紀錄31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言