原打算在介紹完 page component 之後繼續介紹個 layout component 和 <slot>
,但因為這兩個相對簡單所以還是不要打醬油,直接跳到 Vuex 和 Cookie 了。
Vuex 和 Cookie 非常類似,都是將網站 (app) 的一些資料或狀態暫存下來,這樣當頁面切換之後,部分資料跟狀態可以再被使用,不需要重新發送 request 跟 API 要資料。其中,兩者最大的不同是,當使用者重新整理畫面或是重新拜訪網站,Vuex 裡面的資料會消失,而 cookie 不會,所以通常會依需求交互使用兩者!
在 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) {
// ...
}
}
},
}
如同一開始說的,Vuex 資料是會揮發的,如果希望將資料保存一段時間,可以採用 cookie 的方式。 個人習慣使用 js-cookie
套件存取 cookie,另外,雖然我們可以在任何地方存取 cookie 不過為了方便管理,習慣上會一樣寫在 Vuex store module 裡面。
上面 JWT token 範例如果要將 token 存入 cookie 則可以依下列方式改寫:
npm i js-cookie
import Cookies from 'js-cookie';
const mutations = {
['UPDATE_JWT_TOKEN'](state, token) {
state.jwtToken = token;
Cookies.set('auth-token', token);
},
};
const getters = {
userToken(state) {
return state.jwtToken || Cookies.get('auth-token');
},
};
在 Cookie 的例子中,當 Vuex store 的資料揮發時,應該是所有資料都揮發,因此在 getters
取資料時只會存取 cookie 資料,這樣的做法就失去 Vuex store 的意義。
還好,Nuxt 在 index
module 的 actions 增加了一個 nuxtServerInit
方法。此方法是當 server 端 (node.js) 被執行時,會呼叫的方法。他主要的功能是 「從 request 中取得資料後並從新加入到 state 當中」,下面是利用 nuxtServerInit
將上面例子接續改寫的方法:
cookieparser
套件。npm i cookieparser
在 store
底下建立 index module (index.js
)
將原本 auth module 的內容複寫到 index module 中
引入 cookieparser
const cookieparser = process.server ? require('cookieparser') : undefined;
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) {
// ...
}
}
},
};
const getters = {
userToken(state) {
return state.jwtToken;
},
}
有些網站變動頻率很低的常數資料,可以透過 Vuex
記住,當然考慮到 Vuex 資料在重新載入頁面或重新拜訪網站的時候會揮發,因此我們搭配了 Cookie
。同時利用 Nuxt 本身在 index module 中所新增的 nuxtServerInit
方法,讓我們可以在伺服器中取得 cookie 中的資料,並重新加到 state
之中。
其實至此,Nuxt 主要要注意的項目也差不多了 (總覺得一切太突然 XD),明天要介紹如何撰寫 Nuxt plugin,讓 api 的使用更方便!