Home  >  Article  >  Web Front-end  >  Analyze issues related to Cookies of Vue SSR

Analyze issues related to Cookies of Vue SSR

不言
不言Original
2018-06-30 17:09:491747browse

This article mainly introduces the Cookies issue of Vue SSR. The content is quite good. I will share it with you now and give it as a reference.

Once a website involves multiple users, it is difficult to escape from cookies. The cookies of Vue SSR are really a big problem. From the beginning of playing SSR to now, I have come up with a total of 3 solutions, from the earliest one to inject Cookies into the state, to the first one to inject Cookies into global, to the current one to inject Cookies into the asyncData method of the component.

With the upgrade of Vue, the first solution has been It is no longer applicable. The second option also has many limitations, so I thought of the third option. Let’s talk about the specific implementation method:

The first option

The first solution is no longer applicable, I won’t go into details here

The second solution

Idea: Inject cookies into ssr context, then read it when requesting the api, and then append it to the header of axios

1, first add cookies to the context in server.js

const context = {
 title: 'M.M.F 小屋',
 description: 'M.M.F 小屋',
 url: req.url,
 cookies: req.cookies
}
renderer.renderToString(context, (err, html) => {
 if (err) {
  return errorHandler(err)
 }
 res.end(html)
})

After that, Vue will add context to global.__VUE_SSR_CONTEXT__

2, and read cookies in api.js

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 = 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
 },
}

Why can this be done?

By default, Vue for each rendering, the bundle renderer will create a new V8 context and re-execute 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.

But starting from Vue@2.3.0, runInNewContext is added to the options of the createBundleRenderer method. option, use runInNewContext: false, the bundle code will run in the same global context as the server process, so we can no longer put cookies in global, because this will cause all users to share the same cookies.

Why not do this now?

Then let’s continue setting runInNewContext to true, wouldn’t it? Of course it is possible, but re-creating the context and executing the entire bundle is still quite expensive , especially when the application is very large.

Take my own blog as an example. Before, I only rendered 5 routing components, and the rps of loadtest was about 50. But later, I added 12 routing components in the background. After adding SSR, the rps dropped directly to single digits...

So the third solution appeared

The third solution

Idea: Inject Cookies as parameters into the asyncData method of the component, and then use the method of passing parameters to pass the cookies to the API. I have to say that this method is very troublesome, but this is the better thing I can think of. Method

Step 1:

Still in server.js, inject cookies into context

const context = {
 title: 'M.M.F 小屋',
 url: req.url,
 cookies: req.cookies,
}
renderer.renderToString(context, (err, html) => {
 if (err) {
  return handleError(err)
 }
 res.end(html)
})

Step 2 :

In entry-server.js, pass cookies as parameters to the asyncData method

Promise.all(matchedComponents.map(({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)

Step 3:

In the component, use cookies as parameters for Vuex actions

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

Step 4:

Use cookies as parameters in Vuex For api

import api from '~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'](state, {list, hasNext, hasPrev, page, path}) {
  if (page === 1) {
   list = [].concat(list)
  } else {
   list = state.lists.data.concat(list)
  }
  state.lists = {
   data: list, hasNext, hasPrev, page, path
  }
 },
}

const getters = {

}

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

, you must pay attention here. The state must be initialized with the function return value, otherwise it will cause all users to share the 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 = ''
 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',
   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
   }
  })
 },
}

The fourth option

Step 1:

Still in server.js, inject cookies into context

const context = {
  title: 'M.M.F 小屋',
  url: req.url,
  cookies: req.cookies,
}
renderer.renderToString(context, (err, html) => {
  if (err) {
    return handleError(err)
  }
  res.end(html)
})

Step 2 :

In entry-server.js, pass cookies as parameters to the api.setCookies method. What is api.setCookies followed by

api.setCookies(context.cookies) // 这一句
Promise.all(matchedComponents.map(({asyncData}) => asyncData && asyncData({
 store,
 route: router.currentRoute,
 cookies: context.cookies,
 isServer: true,
 isClient: false
}))).then(() => {
 // ...
}

Step 3:

Rewrite api.js

import axios from 'axios'
import qs from 'qs'
import config from './config-server'

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)
      },
      timeout: config.timeout,
    })
  },
  post(url, data) {
    if (!this.api) this.setCookies()
    return this.api({
      method: 'post',
      url,
      data: qs.stringify(data),
      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',
      url,
      params,
    }).then(res => {
      return res
    })
  }
}

Step 4:

No more step 4 , just introduce the api call directly

If you have not re-packaged axios, you can also omit the fifth step and directly give the cookies to axios in the fourth part

Option 2 is specific Example: https://github.com/lincenying/mmf-blog-vue2-ssr

Option 3 Specific example: https://github.com/lincenying/mmf-blog-vue2-pwa-ssr

Specific example of option 4: https://github.com/lincenying/mmf-blog-vue2-pwa-ssr

In summary, if your project is not big, just use option 2. , the project has many pages, and most of the pages are the same for every user, you can consider option 3, or if you have any better methods, welcome to discuss

Vue SSR needs SEO, and every user The content you see is consistent. With caching, it will be a very good experience...

The above is the entire content of this article. I hope it will be helpful to everyone's learning. More related Please pay attention to the PHP Chinese website for content!

Related recommendations:

About the method of Vue2 SSR caching Api data

vue2.0 project implements routing jump Method introduction

The above is the detailed content of Analyze issues related to Cookies of Vue SSR. For more information, please follow other related articles on the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn