差點就要開天窗了...,今天整理之前的介紹,實際用 Laravel 和 Nuxt 做了最雛形的網站包含了「註冊」、「登入」和「登出」功能,有興趣的鐵人大大可以到
GitLab
看完整的 code ! 今天主要是 Nuxt 部分的介紹。
一開始我們先準備好下列的 page components:
page component
在
register.vue
和
login.vue
當中,除了昨天介紹過的表單寫法之外,主要的重點在
asyncData
的部份,當頁面載入的時候要先檢查是否已經存有 token (即登入的狀態),若已經登入就要跳轉至首頁:
async asyncData({ store, redirect }) {
const hasToken = store.getters['hasToken'];
if (hasToken) {
redirect('/');
// register 和 login 註冊的表單
在 login.vue
的 asyncData
裡面,用來判斷受否已經登入以及登入者的角色並用來跳轉頁面:
export default {
name: 'index',
async asyncData({ app, store, redirect }) {
const hasToken = store.getters.userToken;
if (hasToken) {
const isManager = store.getters['user/isManager'];
const isUser = store.getters['user/isUser'];
if (isManager) {
redirect('/manager');
if (isUser) {
redirect('/user');
return {};
剛剛上面有用到 Vuex store module 的資料,所以建立下面的 store module:
module
為了方便控管,同時也是藉由 nuxtServerInit
方法儲存 cookie 相關資料。在今天的例子只有 userToken
一個資料:
const state = () => {
return {
userToken: undefined,
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('SET_USER_TOKEN', token);
} catch (error) {
// ...
// ...
在 user.js
的 getters 中,除了取得儲存的 user 同時也特別增加了 hasUser
,這是用來確認目前的 store 裡面有沒有資料,假若頁面可以取得 token 但卻沒有 user 資料,代表 Vuex 的資料揮發了,需要透過 API 重新取得資料。
const state = () => {
return {
user: {
name: undefined,
email: undefined,
role: undefined,
// ...
const getters = {
hasUser(state) {
return !!state.user.role;
// ...
由於任一個 page component 都可能受到 Vuex 資料揮發的影響,因此我們在 middleware
底下加入 auth.js
,並且將它掛在 nuxt.config.js
的 router
屬性下,這樣載入任何一頁都會先檢查是否存在 「有 userToken 但是沒有 user」 的情況:
import { validators } from '@/configs/api/user';
export default async ({ app, store }) => {
const hasToken = store.getters.hasToken;
const hasUser = store.getters['user/hasUser'];
if (hasToken) {
if (!hasUser) {
try {
const { data } = await app.$api(validators.AccessUserRequest, { userId: null });
await store.dispatch('user/setUser', data);
} catch (error) {
await store.dispatch('removeUserToken');
順帶一提,因為 JWT 是有時效性的,所以假若 token 已經過期,無法取得 user 資料時,我們會將 userToken 清除,讓使用者可以再次登入。
至目前為止「註冊」和「登入」功能完成了,但「路由依角色跳轉」功能只完成一半 (首頁的部份),我們很常會直接打上特定網址瀏覽頁面,所以另一半是要撰寫在 pages/manager
底下的頁只有管理者可以瀏覽,pages/user
則相反,所以我們增加下列資檔案:
page components:
在上述兩個 middlewares 中,我們都只檢驗個別角色而已,同時只要有不符合的情況一律轉到 pages/index.vue
,這樣假設未來系統角色變多了,實際判斷要跳轉的角色首頁都在 pages/index.vue
做就可以了。
export default async ({ store, redirect }) => {
const hasToken = store.getters.hasToken;
const isUser = store.getters['user/isUser'];
if (!(hasToken && isUser)) {
redirect('/');
最後我們來補上「登出」功能就可以收工啦! 因為不論是管理者或是一般使用者都要有登出功能,在 pages/manager.vue
和 pages/user.vue
各寫一次也可以,但同樣的假設未來角色變多就很麻煩,所以我們可以把「登出」寫在 layouts
,這樣在 neast page components 要修改的幅度就少很多了!
下面是 layout/baseLayout.vue
<template>
<Row class="base-layout">
<Row class="head">
<!-- ... -->
<button class="logout-btn" @click="logoutAction">登出</button>
<Row class="body">
<nuxt />
</template>
<script>
import { mapActions } from 'vuex';
import { validators } from '@/configs/api/user';
export default {
name: 'BaseLayout',
data() {
return {
storeUser: undefined,
methods: {
...mapActions(['removeUserToken']),
async logoutAction() {
try {
const { status, code } = await this.$api(validators.LogoutRequest, {});
const successLogout = status && code === 200;
if (successLogout) {
this.removeUserToken();
this.redirectToIndex();
} catch (error) {
redirectToIndex() {
this.$router.push({ name: 'index' });
async asyncData({ store }) {
const storeUser = store.getters['user/storeUser'];
return {
storeUser,
</script>
pages/manager.vue
和 pages/user.vue
各加入 layouts
的設定:
// ...
layout: 'baseLayout',