最后说说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 相关文章推荐
写的htc的数据表格
Jan 20 Javascript
JavaScript-世界上误解最深的语言分析
Aug 12 Javascript
基于jQuery的Tab选项框效果代码(插件)
Mar 01 Javascript
jquery加载页面的方法(页面加载完成就执行)
Jun 21 Javascript
javascript关于运动的各种问题经典总结
Apr 27 Javascript
vue组件中点击按钮后修改输入框的状态实例代码
Apr 14 Javascript
jQuery实现键盘回车搜索功能
Jul 25 jQuery
angular4 如何在全局设置路由跳转动画的方法
Aug 30 Javascript
Angular中支持SCSS的方法
Nov 18 Javascript
jQuery实现经典的网页3D轮播图封装功能【附源码下载】
Feb 15 jQuery
Layui实现数据表格中鼠标悬浮图片放大效果,离开时恢复原图的方法
Sep 11 Javascript
JS获取当前时间戳方法解析
Aug 29 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
PHP:风雨欲来 路在何方?
2006/10/09 PHP
利用PHP实现智能文件类型检测的实现代码
2011/08/02 PHP
php结合ACCESS的跨库查询功能
2015/06/12 PHP
php把文件设置为插件的技巧方法
2020/02/03 PHP
jQuery Ajax文件上传(php)
2009/06/16 Javascript
ASP Json Parser修正版
2009/12/06 Javascript
jQuery学习笔记之DOM对象和jQuery对象
2010/12/22 Javascript
基于jquery的一行代码轻松实现拖动效果
2010/12/28 Javascript
JQuery 返回布尔值Is()条件判断方法代码
2012/05/14 Javascript
jquery异步跨域访问代码
2013/06/28 Javascript
php读取sqlite数据库入门实例代码
2014/06/25 Javascript
JS与jQuery实现隔行变色的方法
2016/09/09 Javascript
初识简单却不失优雅的Vue.js
2016/09/12 Javascript
JS 在数组指定位置插入/删除数据的方法
2017/01/12 Javascript
jQuery实现验证表单密码一致性及正则表达式验证邮箱、手机号的方法
2017/12/05 jQuery
微信小程序渲染性能调优小结
2019/07/30 Javascript
vue获取验证码倒计时组件
2019/08/26 Javascript
小程序的上传文件接口的注意要点解析
2019/09/17 Javascript
ES6函数实现排它两种写法解析
2020/05/13 Javascript
在vue中实现嵌套页面(iframe)
2020/07/30 Javascript
[01:02:07]Liquid vs Newbee 2019国际邀请赛小组赛 BO2 第一场 8.15
2019/08/16 DOTA
对于Python的框架中一些会话程序的管理
2015/04/20 Python
windows系统下Python环境的搭建(Aptana Studio)
2017/03/06 Python
Python的爬虫框架scrapy用21行代码写一个爬虫
2017/04/24 Python
Python入门_条件控制(详解)
2017/05/16 Python
详解Python 模拟实现生产者消费者模式的实例
2017/08/10 Python
使用Python脚本zabbix自定义key监控oracle连接状态
2019/08/28 Python
解析PyCharm Python运行权限问题
2020/01/08 Python
python库skimage给灰度图像染色的方法示例
2020/04/27 Python
python针对Oracle常见查询操作实例分析
2020/04/30 Python
Python3 ffmpeg视频转换工具使用方法解析
2020/08/10 Python
团日活动总结模板
2014/06/25 职场文书
英语课前三分钟演讲稿
2014/08/19 职场文书
党在我心中演讲稿
2014/09/02 职场文书
Python编程中内置的NotImplemented类型的用法
2022/03/23 Python
Java字符缓冲流BufferedWriter
2022/04/09 Java/Android