Laravel + SSR nuxt: 如何保存登录状态

基于 Laravel + Nuxt3 的在线 Demo

Laravel + 客户端渲染的 Vue 确实写起来比较快,但是我们有些时候确实很需要 SEO,比如说电商、门户网站、论坛;而且对于大型项目,相比起客户端渲染,服务器端渲染带来的首屏加载时间上的优势确实是碾压的。

但是很多同学卡在了 SSR下如何保存登录状态 这个问题下面,即如何像传统的 PHP/JSP 页面那样,刷新页面后仍保有登录状态


准备工作

首先准备 2 个接口,一个是

GET http://example.com/api/auth/user

用户获取当前登录用户信息

POST http://example.com/api/auth/tokens

用于请求授权

这里有一个包含 GitHub OAuth 的完整例子 👉 laravel-nuxt3-api,并且测试覆盖率是 100%
Laravel + SSR nuxt: 如何保存登录状态


开始 SSR

  • Nuxt3

Nuxt3 中,可以很自由地创建一个 server only 的脚本,在 initial 整个 Nuxt App 时,可以在渲染 html 前执行这个 server only 的脚本,并在渲染 html 时使用其返回的结果。

我们可以在 plugins 下面创建一个 *.server.[js|ts] 的 server only 的脚本,

plugins/init.server.js

import { defineNuxtPlugin } from '#app';
import { useAuth } from '~/store/auth.js';
function cookieFromRequestHeaders (key) {
    const headers = useRequestHeaders(['cookie']);
    if ('cookie' in headers) {
        const cookie = headers.cookie.split(';').find(
            c => c.trim().startsWith(`${key}=`)
        );
        if (cookie) {
            return cookie.split('=')[1];
        }
    }
    return '';
}
export default defineNuxtPlugin(async (nuxtApp) => {
    const token = cookieFromRequestHeaders('token');
    if (token) {
        const auth = useAuth(nuxtApp.$pinia);
        auth.setToken(token);
        await auth.fetchUser();
    }
});

store/auth.js 定义管理全局状态的 pinia,并在其中发送异步请求

import { defineStore } from 'pinia';
import { useCustomFetch } from '~/composables/useCustomFetch.js';
import cookie from 'js-cookie';
export const useAuth = defineStore('auth', {
    state: () => ({
        _token: '',
        _user: {},
    }),
    getters: {
        token() {
            return this._token;
        },
        user() {
            return this._user;
        },
    },
    actions: {
        async fetchUser() {
            const { data, error } = await useCustomFetch('/auth/user');
            if (error.value) {
                this.reset();
            }
            else {
                this.setUser(data.value);
            }
        },
        // ...
    },
});

注意: Nuxt3 中不推荐使用 axios 等第三方的 http 包,而是使用其内置的 useFetch()

完整的例子 👉 laravel-nuxt3-web

  • Nuxt2

Nuxt2 中,不同于 Nuxt3,Nuxt2 只能在 store/index.js 中的 nuxtServerInit() 中运行 server only 的脚本(但是这里我们可以使用 axios)

store/index.js

export const actions = {
    nuxtServerInit({ commit, dispatch, route }, { req }){
        const token = cookieFromRequest(req, 'token');
        if (!!token) {
            commit('auth/setToken', token);
        }
    }
};

store/auth.js

import Cookie from 'js-cookie';

export const state = () => ({
    user: null,
    token: null
});

export const getters = {
    user: state => state.user,
    token: state => state.token,
    check: state => state.user !== null
};

export const mutations = {
    setToken(state, token){
        state.token = token;
    },
    fetchUserSuccess(state, user){
        state.user = user;
    },
    fetchUserFailure(state){
        state.user = null;
    },
    logout(state){
        state.token = null;
        state.user = null;
    },
    updateUser(state, { user }){
        state.user = user;
    }
}

export const actions = {
    saveToken({ commit }, { token, remember }){
        commit('setToken', token);
        Cookie.set('token', token);
    },
    async fetchUser({ commit }){
        try{
            const { data } = await this.$axios.get('/auth/user');
            commit('fetchUserSuccess', data);
        }catch(e){
            Cookie.remove('token');
            commit('fetchUserFailure');
        }
    },
    // ...
}

更完整的代码 👉 Nuxt middleware to check if user is logged in not working

本作品采用《CC 协议》,转载必须注明作者和本文链接
《L02 从零构建论坛系统》
以构建论坛项目 LaraBBS 为线索,展开对 Laravel 框架的全面学习。应用程序架构思路贴近 Laravel 框架的设计哲学。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!