浅谈Vue SSR 的 Cookies 问题

前端之家收集整理的这篇文章主要介绍了浅谈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

步骤4:

在 Vuex 里将 cookies 做为参数给 api

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

const actions = {
async 'getArticleList' {
// 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,并且每个用户看到的内容都是一致的,配合缓存,将是一个非常好的体验...

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

原文链接:https://www.f2er.com/vue/35139.html

猜你在找的Vue相关文章