最后说说Vue2 SSR 的 Cookies 问题


Posted in Javascript onMay 25, 2018

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

从 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

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

Javascript 相关文章推荐
javascript KeyDown、KeyPress和KeyUp事件的区别与联系
Dec 03 Javascript
浅析jQuery中常用的元素查找方法总结
Jul 04 Javascript
Css3制作变形与动画效果
Jul 24 Javascript
jQuery基于json与cookie实现购物车的方法
Apr 15 Javascript
什么是JavaScript注入攻击?
Sep 14 Javascript
AngularJS实现给动态生成的元素绑定事件的方法
Dec 14 Javascript
JavaScript中捕获与冒泡详解及实例
Feb 03 Javascript
关于axios不能使用Vue.use()浅析
Jan 12 Javascript
通过jQuery学习js类型判断的技巧
May 27 jQuery
bootstrap-treeview实现多级树形菜单 后台JSON格式如何组织?
Jul 26 Javascript
vue+springboot图片上传和显示的示例代码
Feb 14 Javascript
javascript实现京东快递单号的查询效果
Nov 30 Javascript
详解webpack4多入口、多页面项目构建案例
May 25 #Javascript
js中的 || 与 && 运算符详解
May 24 #Javascript
vue axios整合使用全攻略
May 24 #Javascript
vue路由拦截及页面跳转的设置方法
May 24 #Javascript
使用Vue自定义指令实现Select组件
May 24 #Javascript
详解Vue单元测试case写法
May 24 #Javascript
微信小程序通过保存图片分享到朋友圈功能
May 24 #Javascript
You might like
用来解析.htgroup文件的PHP类
2012/09/05 PHP
php随机输出名人名言的代码
2012/10/07 PHP
php获取英文姓名首字母的方法
2015/07/13 PHP
PHP实现适用于文件内容操作的分页类
2016/06/15 PHP
php微信开发之关注事件
2018/06/14 PHP
Javascript处理DOM元素事件实现代码
2012/05/23 Javascript
angularJS结合canvas画图例子
2015/02/09 Javascript
深入理解JavaScript系列(34):设计模式之命令模式详解
2015/03/03 Javascript
JS+CSS实现Div弹出窗口同时背景变暗的方法
2015/03/04 Javascript
javascript常用函数(2)
2015/11/05 Javascript
AngularJS基础 ng-readonly 指令简单示例
2016/08/02 Javascript
浅谈JavaScript中变量和函数声明的提升
2016/08/09 Javascript
JavaScript操作文件_动力节点Java学院整理
2017/06/30 Javascript
vue.js获得当前元素的文字信息方法
2018/03/09 Javascript
Vue 中axios配置实例详解
2018/07/27 Javascript
vue 使某个组件不被 keep-alive 缓存的方法
2018/09/21 Javascript
用jQuery将JavaScript对象转换为querystring查询字符串的方法
2018/11/12 jQuery
小程序获取当前位置加搜索附近热门小区及商区的方法
2019/04/08 Javascript
使用webpack4编译并压缩ES6代码的方法示例
2019/04/24 Javascript
解决layer图标icon不加载的问题
2019/09/04 Javascript
[00:53]TI3正赛第三天 DK怒破A队不败金身 现场国旗飘扬热血激昂
2013/08/10 DOTA
不可错过的十本Python好书
2017/07/06 Python
Python简单实现阿拉伯数字和罗马数字的互相转换功能示例
2018/04/17 Python
Python3.5 处理文本txt,删除不需要的行方法
2018/12/10 Python
Python Web框架之Django框架文件上传功能详解
2019/08/16 Python
Python 限定函数参数的类型及默认值方式
2019/12/24 Python
keras训练浅层卷积网络并保存和加载模型实例
2020/07/02 Python
python如何用matplotlib创建三维图表
2021/01/26 Python
HTML5中canvas中的beginPath()和closePath()的重要性
2018/08/24 HTML / CSS
椰子猫砂:CatSpot
2018/08/27 全球购物
描述JSP和Servlet的区别、共同点、各自应用的范围
2012/10/02 面试题
vue路由实现登录拦截
2021/03/24 Vue.js
门卫岗位职责
2013/11/15 职场文书
感恩教师主题班会
2015/08/12 职场文书
创业计划书之川味火锅店
2019/09/02 职场文书
mysql查询的控制语句图文详解
2021/04/11 MySQL