最后说说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 相关文章推荐
非常强大的 jQuery.AsyncBox 弹出对话框插件
Aug 29 Javascript
根据选择不同的下拉值出现相对应的文本输入框
Aug 01 Javascript
一个判断抢购时间是否到达的简单的js函数
Jun 23 Javascript
Javascript中3种实现继承的方法和代码实例
Aug 12 Javascript
EasyUI中datagrid在ie下reload失败解决方案
Mar 09 Javascript
jQuery实现图片加载完成后改变图片大小的方法
Mar 29 Javascript
jQuery获取复选框被选中数量及判断选择值的方法详解
May 25 Javascript
jQuery插件实现文件上传功能(支持拖拽)
Aug 27 Javascript
JavaScript中offsetWidth的bug及解决方法
May 17 Javascript
JS实现自定义状态栏动画文字效果示例
Oct 12 Javascript
JavaScript中window和document用法详解
Jul 28 Javascript
Handtrack.js库实现实时监测手部运动(推荐)
Feb 08 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
把从SQL中取出的数据转化成XMl格式
2006/10/09 PHP
PHP的preg_match匹配字符串长度问题解决方法
2014/05/03 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(四)
2014/06/23 PHP
PHP获取mysql数据表的字段名称和详细信息的方法
2014/09/27 PHP
phpmyadmin中禁止外网使用的方法
2014/11/04 PHP
php获取文件类型和文件信息的方法
2015/07/10 PHP
网上抓的一个特效
2007/05/11 Javascript
js函数中onmousedown和onclick的区别和联系探讨
2013/05/19 Javascript
JS控制阿拉伯数字转为中文大写示例代码
2013/09/04 Javascript
轻松实现js图片预览功能
2016/01/18 Javascript
JavaScript高仿支付宝倒计时页面及代码实现
2016/10/21 Javascript
React中jquery引用的实现方法
2017/09/12 jQuery
详解vue-cli 接口代理配置
2017/12/13 Javascript
JavaScript学习笔记之DOM操作实例分析
2019/01/08 Javascript
vue cli3 配置proxy代理无效的解决
2019/10/30 Javascript
Node.js API详解之 Error模块用法实例分析
2020/05/14 Javascript
.netcore+vue 实现压缩文件下载功能
2020/09/24 Javascript
[01:12:40]DOTA2-DPC中国联赛 正赛 DLG vs XG BO3 第三场 1月25日
2021/03/11 DOTA
一个超级简单的python web程序
2014/09/11 Python
在Django框架中编写Context处理器的方法
2015/07/20 Python
将pandas.dataframe的数据写入到文件中的方法
2018/12/07 Python
python 轮询执行某函数的2种方式
2020/05/03 Python
Python使用内置函数setattr设置对象的属性值
2020/10/16 Python
python中的yield from语法快速学习
2020/11/06 Python
意大利香水和彩妆护肤品购物网站:Ditano
2017/08/13 全球购物
GAP阿联酋官网:GAP UAE
2017/11/30 全球购物
美国机场停车位预订:About Airport Parking
2018/03/26 全球购物
澳大利亚最大的网上油画销售画廊:Direct Art Australia
2018/04/15 全球购物
Android面试宝典
2013/08/06 面试题
linux面试题参考答案(7)
2014/07/24 面试题
青年文明号复核材料
2014/02/11 职场文书
小学感恩教育活动总结
2014/07/07 职场文书
2015年党风廉政建设责任书
2015/01/29 职场文书
2015年英语教研组工作总结
2015/05/23 职场文书
孟佩杰观后感
2015/06/17 职场文书
《分一些蚊子进来》读后感3篇
2020/01/09 职场文书