一个网站一旦涉及到多用户,就很难从 Cookies 中逃脱,Vue SSR 的 cookies 也真算是遇到的一个不小的问题,从开始玩 SSR 开始到现在,一共想出了3种方案,从最早的把 Cookies 注入到 state 中,到把 Cookies 注入到 global,到现在的将 Cookies 注入到组件的 asyncData 方法.
随着 Vue 的升级,第一种方案已经不再适用,第二种也有不少的限制,于是想到第三种方案,下来就说说具体实现的方法:
第一种方案
第一种方案已经不再适用,这里不再细说
第二种方案
思路: 将 cookies 注入到 ssr 的 context里,然后在请求 api 时读取,再追加到 axios 的header 里
1,首先在 server.js 里将 cookies 加到 context里
之后,Vue 会把 context 加到 global.__VUE_SSR_CONTEXT__
2,在 api.js 里读取 cookies
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 中
步骤2:
在entry-server.js里,将cookies作为参数传给 asyncData 方法
步骤3:
在组件里,把 cookies 做为参数给 Vuex 的 actions
步骤4:
在 Vuex 里将 cookies 做为参数给 api
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 里
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 中
步骤2:
在entry-server.js里,将cookies作为参数传给 api.setCookies 方法,api.setCookies 是什么东西后面会有
步骤3:
重写 api.js
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,或者你有什么更好的方法,欢迎讨论
原文链接:https://www.f2er.com/vue/35139.html