Once a website involves multiple users, it is difficult to escape from cookies. Vue SSR cookies are really a big problem. From the beginning of playing SSR to the present, a total of 3 solutions have been come up with, from the earliest injecting cookies into state, to the injecting cookies into global, to the current asyncData method for injecting cookies into components.
With the upgrade of Vue, the first solution is no longer applicable, and the second option has many restrictions. So I thought of the third option and let me talk about the specific implementation method:
The first solution
The first solution is no longer applicable, so I won't go into details here
The second solution
Idea: Inject cookies into the context of ssr, then read them when requesting API, and then append them to the header of axios
1. First add cookies to the context
const context = { title: 'Cast', description: 'Cast', url: , cookies: } (context, (err, html) => { if (err) { return errorHandler(err) } (html) })
After that, Vue will add context to global.__VUE_SSR_CONTEXT__
2, read cookies in
import axios from 'axios' import qs from 'qs' import md5 from 'md5' import config from './config-server' const SSR = global.__VUE_SSR_CONTEXT__ const cookies = || {} const parseCookie = cookies => { let cookie = '' (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: + url, data: (data), timeout: , headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', cookie } }) return res }, }
Why can this be done?
By default, Vue for each rendering, the bundle renderer creates a new V8 context and re-executes the entire bundle. The application code is isolated from the server process, so each accessed user context is independent and will not affect each other.
However, starting from [email protected], in the createBundleRenderer method option, the runInNewContext option is added. Using runInNewContext: false, the bundle code will run in the same global context as the server process, so we can no longer place cookies in global, because this will let all users share the same cookies.
Why not do this now?
Then let's continue to set runInNewContext to true, wouldn't it be fine? Of course, it's OK, but recreating the context and executing the entire bundle is still quite expensive, especially when the application is large.
Taking my own blog as an example, before, only 5 routing components were rendered, and the rps of loadtest had about 50, but later, after adding the 12 routing components in the background to SSR, rps was directly reduced to single digits...
So the third solution has emerged
The third solution
Idea: Inject cookies as parameters into the component's asyncData method, and then pass cookies to the API by passing parameters. I have to say that this method is very troublesome, but this is a better method that I can think of.
Step 1:
Or inject cookies into context
const context = { title: 'Cast', url: , cookies: , } (context, (err, html) => { if (err) { return handleError(err) } (html) })
Step 2:
In, pass cookies as parameters to the asyncData method
((({asyncData}) => asyncData && asyncData({ store, route: , cookies: , isServer: true, isClient: false }))).then(() => { = = .NODE_ENV === 'production' resolve(app) }).catch(reject)
Step 3:
In the component, use cookies as parameters to Vuex's actions
export default { name: 'frontend-index', async asyncData({store, route, cookies}, config = { page: 1}) { = cookies await ('frontend/article/getArticleList', config) } // ..... }
Step 4:
Use cookies as parameters to the api in Vuex
import api from '~api' const state = () => ({ lists: { data: [], hasNext: 0, page: 1, path: '' }, }) const actions = { async ['getArticleList']({commit, state}, config) { // vuex as temporary cache if ( > 0 && === && === 1) { return } let cookies if () { cookies = delete } const { data: { data, code} } = await ('frontend/article/list', {...config, cache: true}, cookies) if (data && code === 200) { commit('receiveArticleList', { ...config, ...data, }) } }, } const mutations = { ['receiveArticleList'](state, {list, hasNext, hasPrev, page, path}) { if (page === 1) { list = [].concat(list) } else { list = (list) } = { data: list, hasNext, hasPrev, page, path } }, } const getters = { } export default { namespaced: true, state, actions, mutations, getters }
It is important to note here that state must be initialized with the function return value, otherwise it will cause all users to share state.
Step 5:
Receive cookies in the API and add them to the headers of axios
import axios from 'axios' import qs from 'qs' import config from './config-server' const parseCookie = cookies => { let cookie = '' (cookies).forEach(item => { cookie+= item + '=' + cookies[item] + '; ' }) return cookie } export default { get(url, data, cookies = {}) { const cookie = parseCookie(cookies) return axios({ method: 'get', url: + url, data: (data), timeout: , headers: { 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', cookie } }) }, }
The fourth solution
Step 1:
Or inject cookies into context
const context = { title: 'Cast', url: , cookies: , } (context, (err, html) => { if (err) { return handleError(err) } (html) })
Step 2:
In, pass cookies as parameters to the method, what will happen after
() // This sentence((({asyncData}) => asyncData && asyncData({ store, route: , cookies: , isServer: true, isClient: false }))).then(() => { // ... }
Step 3:
Rewrite
import axios from 'axios' import qs from 'qs' import config from './config-server' const parseCookie = cookies => { let cookie = '' (cookies).forEach(item => { cookie+= item + '=' + cookies[item] + '; ' }) return cookie } export default { api: null, cookies: {}, setCookies(value) { value = value || {} = value = ({ baseURL: , headers: { 'X-Requested-With': 'XMLHttpRequest', cookie: parseCookie(value) }, timeout: , }) }, post(url, data) { if (!) () return ({ method: 'post', url, data: (data), headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', } }).then(res => { return res }) }, get(url, params) { if (!) () return ({ method: 'get', url, params, }).then(res => { return res }) } }
Step 4:
There is no step 4, just introduce the API call
If you haven't reencapsulated axios, you can also omit the fifth step and just give cookies to axios in the fourth part
Solution 2 Specific examples:/lincenying/mmf-blog-vue2-ssr
Solution 3 Specific Examples:/lincenying/mmf-blog-vue2-pwa-ssr
Solution 4 Specific Examples:/lincenying/mmf-blog-vue2-pwa-ssr
In summary, if you don’t have a big project, just use Plan 2 directly. The project has many pages, and most of the pages are the same for every user. You can consider Plan 3, or if you have any better methods, welcome to discuss
Vue SSR requires SEO and the content seen by each user is consistent. With the cache, it will be a very good experience...
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.