最后说说Vue2 SSR 的 Cookies 问题

本来想前面写点什么的, 还是算了, 直接说思路吧.

从 Vue2.3 版本后, SSR 的 cookies, 就变成一个无比麻烦的问题, 具体请访问官网文档: https://ssr.vuejs.org/zh/api.html#runinnewcontext

之前也说不少的思路, 可是都觉得不怎么好用, 虽然都能解决问题, 今天再说一种思路

因为 Vue2.3 以后, bundle 代码将与服务器进程在同一个 global 上下文中运行, 所以不能再将 cookies 丢到 global 给 api 使用, 否则就会出现 cookies 污染

Vue2.3 以后, 我们需要为每个请求创建一个新的根 Vue 实例, 同样的, router、store 也需要, 所以, 我们的思路也在此, 将封装后的 api 注入到这 3 个实例当中去, 保证每个请求的 api 都是独立, 那么就剩一个问题, 注入到哪个实例里面去!?

api 请求用到最多的两个地方就是: 组件和 vuex 的 actions 里, 这两个地方都有 store 的影子, 所以, 注入到 store 中, 毫无疑问是最好的

那么下面就来操作下:

1. 修改 api, 让 api 文件导出一个工厂函数

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

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

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

把 cookies 当参数传进工厂函数, 给 axios 使用

示例文件1: src/api/index-server.js

示例文件2: src/api/index-client.js

2. 修改 server.js 文件, 将 cookies 注入 renderer 的 上下文中

// 前后代码略
  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)
    if (!isProd) {
      console.log(`whole request: ${Date.now() - s}ms`)
    }
  })
// 前后代码略

示例文件: server.js

3. 修改服务端入口文件

import { createApp } from './app'
import { api } from '~api'
export default function(context) {
  return new Promise((resolve, reject) => {
    const s = Date.now()
    const { app, router, store } = createApp()
    const url = context.url
    const fullPath = router.resolve(url).route.fullPath
    if (fullPath !== url) {
      reject({ url: fullPath })
    }
    router.push(url)
    router.onReady(() => {
      const matchedComponents = router.getMatchedComponents()
      if (!matchedComponents.length) {
        reject({ code: 404 })
      }
      // 注意这里, 在步骤2中, context里已经带有cookies了
      // 创建一个新的api实例, 并把cookies传进去
      // 同时注入store和根状态中
      // 注入 store 中, 可以方便在组件中用
      // 注入 根状态中, 可以方便在 vuex 的 actions 中用
      store.$api = store.state.$api = api(context.cookies)
      Promise.all(
        matchedComponents.map(
          ({ asyncData }) =>
            asyncData &&
            asyncData({
              store,
              route: router.currentRoute,
              cookies: context.cookies,
              isServer: true,
              isClient: false
            })
        )
      )
        .then(() => {
          console.log(`data pre-fetch: ${Date.now() - s}ms`)
          context.state = store.state
          context.isProd = process.env.NODE_ENV === 'production'
          resolve(app)
        })
        .catch(reject)
    }, reject)
  })
}

示例文件: src/entry-server.js

4. 修改客户端入口文件

import api from '~api'
// 前后代码略
const { app, router, store } = createApp()

if (window.__INITIAL_STATE__) {
  store.replaceState(window.__INITIAL_STATE__)
  // 客户端就没必要用工厂函数了, 用也可以, 但是需注意, api里的属性必须和服务端的保持一致
  store.$api = store.state.$api = api
}
// 前后代码略

示例文件: src/entry-client.js

5. 在 vuex 的 actions 中使用

const actions = {
  async ['getArticleList'](
    {
      commit,
      state,
      rootState: { $api } // 这里就是前面注入的api
    },
    config
  ) {
    const {
      data: { data, code }
    } = await $api.get('frontend/article/list', { ...config, cache: true })
    if (data && code === 200) {
      commit('receiveArticleList', {
        ...config,
        ...data
      })
    }
  }
}

示例文件: src/store/modules/frontend-article.js

6. 在组件中使用

methods: {
    async recover(id) {
      const {
        data: { code, message }
      } = await this.$store.$api.get('frontend/comment/recover', { id })
      if (code === 200) {
        this.$store.commit('global/comment/recoverComment', id)
      }
    }
}

示例文件: src/components/frontend-comment.vue

至此, 全文结束, 完整代码, 请参考: https://github.com/lincenying/mmf-blog-vue2-pwa-ssr

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

(0)

相关推荐

  • 浅谈Vue SSR 的 Cookies 问题

    一个网站一旦涉及到多用户, 就很难从 Cookies 中逃脱, Vue SSR 的 cookies 也真算是遇到的一个不小的问题, 从开始玩 SSR 开始到现在, 一共想出了3种方案, 从最早的把 Cookies 注入到 state 中, 到把 Cookies 注入到 global, 到现在的将 Cookies 注入到组件的 asyncData 方法. 随着 Vue 的升级, 第一种方案已经不再适用, 第二种也有不少的限制, 于是想到第三种方案, 下来就说说具体实现的方法: 第一种方案 第一种方

  • 最后说说Vue2 SSR 的 Cookies 问题

    本来想前面写点什么的, 还是算了, 直接说思路吧. 从 Vue2.3 版本后, SSR 的 cookies, 就变成一个无比麻烦的问题, 具体请访问官网文档: https://ssr.vuejs.org/zh/api.html#runinnewcontext 之前也说不少的思路, 可是都觉得不怎么好用, 虽然都能解决问题, 今天再说一种思路 因为 Vue2.3 以后, bundle 代码将与服务器进程在同一个 global 上下文中运行, 所以不能再将 cookies 丢到 global 给 a

  • 详解Vue2 SSR 缓存 Api 数据

    本文介绍了Vue2 SSR 缓存 Api 数据,分享给大家,具体如下: 1. 安装缓存依赖: lru-cache npm install lru-cache --dev 2. api 配置文件 config-server.js var LRU = require('lru-cache') let api if (process.__API__) { api = process.__API__ } else { api = process.__API__ = { api: 'http://loca

  • Vue2 SSR渲染根据不同页面修改 meta

    本文主要介绍了Vue2 SSR渲染根据不同页面修改 meta,分享给大家,具体如下: 注意: 经过测试, vue-meta 会导致内存泄漏, 请慎用- 以现在 vue2 的 服务端渲染模式, 都是通过 webpack 生成 html 模版文件(或者直接在 server.js 里拼接), 然后通过fs.readFileSync 读取该文件, 再通过 res.end 输出, 这样就造成 meta 修改很麻烦 这时候我们可以借助 vue-meta 来管理, 下面以官方的vue-hackernews-2

  • 详解vue-meta如何让你更优雅的管理头部标签

    在 Vue SPA 应用中,如果想要修改HTML的头部标签,或许,你会在代码里,直接这么做: // 改下title document.title = 'what?' // 引入一段script let s = document.createElement('script') s.setAttribute('src', './vconsole.js') document.head.appendChild(s) // 修改meta信息,或者给html标签添加属性... // 此处省略一大坨代码...

  • vue项目实现记住密码到cookie功能示例(附源码)

    本文介绍了vue项目实现记住密码到cookie功能示例,分享给大家,具体如下: 登陆页面 实现功能: 1.记住密码勾选,点登陆时,将账号和密码保存到cookie,下次登陆自动显示到表单内 2.不勾选,点登陆时候则清空之前保存到cookie的值,下次登陆需要手动输入 大体思路就是通过存/取/删cookie实现的:每次进入登录页,先去读取cookie,如果浏览器的cookie中有账号信息,就自动填充到登录框中,存cookie是在登录成功之后,判断当前用户是否勾选了记住密码,如果勾选了,则把账号信息存

  • vue项目中api接口管理总结

    默认vue项目中已经使用vue-cli生成,安装axios,基于element-ui开发,axiosconfig目录和api目录是同级,主要记录配置的相关. 1. 在axiosconfig目录下的axiosConfig.js import Vue from 'vue' import axios from 'axios' import qs from 'qs' import { Message, Loading } from 'element-ui' // 响应时间 axios.defaults.

  • 详解Vue.js项目API、Router配置拆分实践

    前后端分离开发方式前端拥有更高的控制权 随着前端框架技术的飞速发展,Router这个概念也被迅速普及到前端项目中,在早期前后的没有分离的时期下,并没有明确的路由概念,前端页面跳转大多是通过后端进行请求转发的,比如在Spring MVC项目中,进行一个页面跳转如下(画红线部分): 前端需要一个超链接,链接的href=/manager,这样这个超链接被转发到scs/waitFollowed路径指定的页面. 前后的分离后,前端页面跳转的方式发生了变化,不再需要后端处理了,数据交换方式也改变了,由此前端

  • 详解Vue SSR( Vue2 + Koa2 + Webpack4)配置指南

    正如Vue官方所说,SSR配置适合已经熟悉 Vue, webpack 和 Node.js 开发的开发者阅读.请先移步ssr.vuejs.org了解手工进行SSR配置的基本内容. 从头搭建一个服务端渲染的应用是相当复杂的.如果您有SSR需求,对Webpack及Koa不是很熟悉,请直接使用NUXT.js. 本文所述内容示例在 Vue SSR Koa2 脚手架 : https://github.com/yi-ge/Vue-SSR-Koa2-Scaffold 我们以撰写本文时的最新版:Vue 2,Web

  • 详解如何使用Vue2做服务端渲染

    花费了一个月时间,终于在新养车之家项目中成功部署了vue2服务端渲染(SSR),并且使用上了Vuex 负责状态管理,首屏加载时间从之前4G网络下的1000ms,提升到了现在500-700ms之间,SSR的优势有很多,现在让我来跟你细细道来. 技术栈 服务端:Nodejs(v6.3) 前端框架 Vue2.1.10 前端构建工具:webpack2.2 && gulp 代码检查:eslint 源码:es6 前端路由:vue-router2.1.0 状态管理:vuex2.1.0 服务端通信:axi

随机推荐