浅谈Vue SSR 的 Cookies 问题

一个网站一旦涉及到多用户,就很难从 Cookies 中逃脱,Vue SSR 的 cookies 也真算是遇到的一个不小的问题,从开始玩 SSR 开始到现在,一共想出了3种方案,从最早的把 Cookies 注入到 state 中,到把 Cookies 注入到 global,到现在的将 Cookies 注入到组件的 asyncData 方法.

随着 Vue 的升级,第一种方案已经不再适用,第二种也有不少的限制,于是想到第三种方案,下来就说说具体实现的方法:

第一种方案

第一种方案已经不再适用,这里不再细说

第二种方案

思路: 将 cookies 注入到 ssr 的 context里,然后在请求 api 时读取,再追加到 axios 的header 里

1,首先在 server.js 里将 cookies 加到 context里

{ if (err) { return errorHandler(err) } res.end(html) })

之后,Vue 会把 context 加到 global.__VUE_SSR_CONTEXT__

2,在 api.js 里读取 cookies

const SSR = global.VUE_SSR_CONTEXT
const cookies = SSR.cookies || {}
const parseCookie = cookies => {
let cookie = ''
Object.keys(cookies).forEach(item => {
cookie+= item + '=' + cookies[item] + '; '
})
return cookie
}

export default {
async post(url,data) {
const cookie = parseCookie(cookies)
const res = await axios({
method: 'post',url: config.api + url,data: qs.stringify(data),timeout: config.timeout,headers: {
'X-Requested-With': 'XMLHttpRequest','Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',cookie
}
})
return res
},}

为什么可以这么做?

认情况下,Vue 对于每次渲染,bundle renderer 将创建一个新的 V8 上下文并重新执行整个 bundle。应用程序代码与服务器进程隔离,所以每个访问的用户上下文都是独立的,不会互相影响.

但是从Vue@2.3.0开始,在createBundleRenderer方法的选项中,添加了runInNewContext选项,使用 runInNewContext: false,bundle 代码将与服务器进程在同一个 global 上下文中运行,所以我们不能再将 cookies 放在 global,因为这会让所有用户共用同一个 cookies.

为什么现在不这么做?

那我们继续将runInNewContext设置成true,不就好了吗? 当然也是可以的,但是重新创建上下文并执行整个 bundle 还是相当昂贵的,特别是当应用很大的时候.

以我自己的博客为例,之前只有渲染 5 个路由组件,loadtest 的 rps,有 50 左右,但是后来把后台的 12 个路由组件也加到 SSR 后,rps 直接降到了个位数...

所以出现了现在的第三种方案

第三种方案

思路: 将 Cookies 作为参数注入到组件的asyncData方法,然后用传参数的方法把 cookies 传给 api,不得不说这种方法很麻烦,但是这是个人能想到的比较好的方法

步骤1:

还是在 server.js 里,把 cookies 注入到 context 中

{ if (err) { return handleError(err) } res.end(html) })

步骤2:

在entry-server.js里,将cookies作为参数传给 asyncData 方法

asyncData && asyncData({ store,route: router.currentRoute,cookies: context.cookies,isServer: true,isClient: false }))).then(() => { context.state = store.state context.isProd = process.env.NODE_ENV === 'production' resolve(app) }).catch(reject)

步骤3:

在组件里,把 cookies 做为参数给 Vuex 的 actions

rush:js;"> export default { name: 'frontend-index',async asyncData({store,route,cookies},config = { page: 1}) { config.cookies = cookies await store.dispatch('frontend/article/getArticleList',config) } // ..... }

步骤4:

在 Vuex 里将 cookies 做为参数给 api

const state = () => ({
lists: {
data: [],hasNext: 0,page: 1,path: ''
},})

const actions = {
async ['getArticleList']({commit,state},config) {
// vuex 作为临时缓存
if (state.lists.data.length > 0 && config.path === state.lists.path && config.page === 1) {
return
}
let cookies
if (config.cookies) {
cookies = config.cookies
delete config.cookies
}
const { data: { data,code} } = await api.get('frontend/article/list',{...config,cache: true},cookies)
if (data && code === 200) {
commit('receiveArticleList',{
...config,...data,})
}
},}

const mutations = {
'receiveArticleList' {
if (page === 1) {
list = [].concat(list)
} else {
list = state.lists.data.concat(list)
}
state.lists = {
data: list,path
}
},}

const getters = {

}

export default {
namespaced: true,state,actions,mutations,getters
}

这里一定要注意,state 一定要用函数返回值来初始化 state,不然会导致所有用户共用 state

步骤5:

在 api 里接收 cookies,并加到 axios 的 headers 里

const parseCookie = cookies => {
let cookie = ''
Object.keys(cookies).forEach(item => {
cookie+= item + '=' + cookies[item] + '; '
})
return cookie
}

export default {
get(url,data,cookies = {}) {
const cookie = parseCookie(cookies)
return axios({
method: 'get',cookie
}
})
},}

第四种方案

步骤1:

还是在 server.js 里,把 cookies 注入到 context 中

{ if (err) { return handleError(err) } res.end(html) })

步骤2:

在entry-server.js里,将cookies作为参数传给 api.setCookies 方法,api.setCookies 是什么东西后面会有

asyncData && asyncData({ store,isClient: false }))).then(() => { // ... }

步骤3:

重写 api.js

const parseCookie = cookies => {
let cookie = ''
Object.keys(cookies).forEach(item => {
cookie+= item + '=' + cookies[item] + '; '
})
return cookie
}

export default {
api: null,cookies: {},setCookies(value) {
value = value || {}
this.cookies = value
this.api = axios.create({
baseURL: config.api,headers: {
'X-Requested-With': 'XMLHttpRequest',cookie: parseCookie(value)
},})
},post(url,data) {
if (!this.api) this.setCookies()
return this.api({
method: 'post',url,headers: {
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',}
}).then(res => {
return res
})
},get(url,params) {
if (!this.api) this.setCookies()
return this.api({
method: 'get',params,}).then(res => {
return res
})
}
}

步骤4:

没有步骤4了,直接引入 api 调用即可

如果你没有将 axios 重新封装,那么也可以把第五步省略,直接在第四部把 cookies 给 axios 即可

方案 2 具体实例:

方案 3 具体实例:

方案 4 具体实例:

综上,如果你项目不大,还是直接用方案 2 吧,项目有很多页面,并且大部分页面是每个用户都一样的,可以考虑方案 3,或者你有什么更好的方法,欢迎讨论

Vue SSR 对需要 SEO,并且每个用户看到的内容都是一致的,配合缓存,将是一个非常好的体验...

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程之家。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


可以通过min-width属性来设置el-table-column的最小宽度。以下是一个示例: <template> <el-table :data="tableData"> <el-table-column prop="item_no&q
yarn dev,当文件变动后,会自动重启。 yanr start不会自动重启 nodemon会监听文件变动,跟yarn dev和yarn start无关。
ref 用于创建一个对值的响应式引用。这个值可以是原始值(如数字或字符串)或对象。当 ref 的值发生变化时,Vue 会自动更新 DOM 或任何其他使用该 ref 的响应式依赖。 使用示例: import { ref } from 'vue'; const count = ref(0
通过修改 getWK005 函数来实现这一点。这里的 query 参数就是发送 GET 请求时的查询参数。你可以将需要的条件作为 query 对象的属性传递进去。比如,如果你想要按照特定的条件查询信息,你可以在调用 getWK005 函数时传递这些条件。例如: getWK005({ conditio
<el-form-item label="入库类型" prop="mt_type"> <el-select v-model="form.mt_type" filterable placeholder="请选择&q
API 变动 样式类名变化: 一些组件的样式类名有所变动,可能需要更新你的自定义样式。 事件名和属性名变化: 某些组件的事件名和属性名发生了变化,需要检查 Element Plus 文档 以了解详细信息。 使用 setup 函数: 在 Vue 3 中,可以使用 Composition API(如 s
安装和引入方式 Element UI (Vue 2): // main.js import Vue from 'vue'; import ElementUI from 'element-ui'; import 'element-ui/lib/theme-cha
排查400 (Bad Request)和解决这个问题,可以按照以下步骤进行: 检查URL和端点:确保URL http://127.0.0.1:8008/basicApp/BS037HModel/ 是正确的,并且该端点在服务器上存在。 检查请求参数:确认发送请求时的任何参数都是正确的,包括查询参数、请
在 Vue.js 中,<template> 标签是一种特殊的标签,它用于定义组件的模板,但不会直接渲染为 HTML 元素。它的主要用途是在编写组件和使用插槽时提供灵活的模板定义。以下是关于 <template> 标签的一些关键概念和使用示例。 基本用法 组件模板:在单文件组件
el-config-provider是Element Plus库中的一个组件,用于提供全局的配置。它是Element Plus在2.0版本中引入的新组件。 el-config-provider组件的作用是允许你在应用程序中统一配置Element Plus的一些全局属性和样式,这些配置将被应用于所有E
onMounted 是一个生命周期钩子函数,在组件挂载到 DOM 后运行。在这里你可以执行需要在组件可用后进行的操作,比如获取数据、设置订阅或初始化第三方库。 使用示例: import { onMounted } from 'vue'; onMounted(() => { //
mt_qty: (this.temp.id && this.temp.mt_qty) ? this.temp.mt_qty : event.wo_wip,在这个修正后的代码中,使用了条件三元运算符来判断 this.temp.id 是否存在且 mt_qty 是否已被赋值。如果条件成立,
Axios是一个基于Promise的易用、简洁且高效的HTTP请求插件,可以用于浏览器和Node.js。首先执行yarn命令安装依赖,安装成功时在package.json文件的dependencies下多出了Axios及其版本号,笔者写此书时,安装的版本为0.26.1,如所示。 yarn add a
async 关键字用于声明一个异步函数,这个函数会返回一个 Promise 对象。与 await 关键字配合使用时,可以在异步函数中暂停代码执行,直到 Promise 解决或拒绝,从而使异步代码的处理更简单和同步化。 使用 async 的示例 下面是一个完整的 Vue 3 组件示例,展示了如何使用
Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表了一个异步操作的最终完成(或失败)及其结果值。在处理异步操作时,Promise 提供了一种更干净、更可读的方式来管理回调函数。 Promise 的状态 一个 Promise 对象有三种状态: Pending(进行中):初始
在 JavaScript 中,await 是一个用于处理异步操作的关键字。它只能在 async 函数内部使用,并且用于等待一个 Promise 对象的解析。在 Vue 3 中,await 关键字常用于在组合式 API 的 setup 函数中处理异步操作,比如数据获取。 使用 await 的示例 以下
引入样式 Element UI (Vue 2): import 'element-ui/lib/theme-chalk/index.css'; Element Plus (Vue 3): import 'element-plus/dist/index.css';
-D和-S区别 安装的环境不同 -D是--save-dev的简写,会安装在开发环境中(production)中的devPendencies中 -S是--save的简写,会安装在生产环境中(development)中的dependencies中
Element-plus的徽章组件el-badge Element Plus 是一个基于 Vue.js 的 UI 组件库,它提供了一系列的常用 UI 组件供开发者使用。其中,徽章组件(el-badge)是其中之一。 徽章组件(el-badge)可以在其他元素上展示一个小圆点或者一个数字,用于标记某种
vscode element-plus/lib/theme-chalk/index.css报错路径找不到 import { createApp } from 'vue' import './style.css' import App from './App.v