最后说说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 相关文章推荐
js判断变量是否空值的代码
Oct 26 Javascript
jquery autocomplete自动完成插件的的使用方法
Aug 07 Javascript
js 创建快捷方式的代码(fso)
Nov 19 Javascript
Prototype的Class.create函数解析
Sep 22 Javascript
背景图跟随鼠标移动的Mootools插件实现代码
Dec 12 Javascript
JS实现定时页面弹出类似QQ新闻的提示框
Nov 07 Javascript
jquery选择符快速提取web表单数据示例
Mar 27 Javascript
jQuery easyui的validatebox校验规则扩展及easyui校验框validatebox用法
Jan 18 Javascript
微信小程序自定义组件之可清除的input组件
Jul 17 Javascript
vue this.reload 方法 配置
Sep 12 Javascript
详解小程序如何改变onLoad的执行时机
Nov 01 Javascript
React Hooks 实现和由来以及解决的问题详解
Jan 17 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
帅气的琦玉老师
2020/03/02 日漫
PHP 基于文件头的文件类型验证类函数
2012/05/01 PHP
php使用curl访问https示例分享
2014/01/17 PHP
php教程之phpize使用方法
2014/02/12 PHP
php自定义时间转换函数示例
2016/12/07 PHP
基于jQuery的可以控制左右滚动及自动滚动效果的代码
2010/07/25 Javascript
javascript与webservice的通信实现代码
2010/12/25 Javascript
Extjs优化(一)删除冗余代码提高运行速度
2013/04/15 Javascript
JS 获取浏览器和屏幕宽高等信息的实现思路及代码
2013/07/31 Javascript
Jquery显示、隐藏元素以及添加删除样式
2013/08/09 Javascript
JavaScript运行时库属性一览表
2014/03/14 Javascript
使用FlexiGrid实现Extjs表格效果方法分享
2014/12/16 Javascript
javascript属性访问表达式用法分析
2015/04/25 Javascript
JS中处理时间之setUTCMinutes()方法的使用
2015/06/12 Javascript
Bootstrap布局之栅格系统详解
2016/06/13 Javascript
Angularjs结合Bootstrap制作的一个TODO List
2016/08/18 Javascript
微信小程序 MINA文件结构
2016/10/17 Javascript
jquery网页日历显示控件calendar3.1使用详解
2016/11/24 Javascript
基于Vue.js实现简单搜索框
2020/03/26 Javascript
快速将Vue项目升级到webpack3的方法步骤
2017/09/14 Javascript
Angular 5.0 来了! 有这些大变化
2017/11/15 Javascript
Vue服务器渲染Nuxt学习笔记
2018/01/31 Javascript
vue+webpack实现异步组件加载的方法
2018/02/03 Javascript
详解小程序原生使用ES7 async/await语法
2018/08/06 Javascript
vue+koa2搭建mock数据环境的详细教程
2020/05/18 Javascript
[00:57]深扒TI7聊天轮盘语音出处5
2017/05/11 DOTA
详解Python3.6的py文件打包生成exe
2018/07/13 Python
python组合无重复三位数的实例
2018/11/13 Python
ProBikeKit新西兰:自行车套件,跑步和铁人三项装备
2017/04/05 全球购物
自我评价是什么
2014/01/04 职场文书
商超业务员岗位职责
2014/03/12 职场文书
护士自我鉴定总结
2014/03/24 职场文书
标准毕业生自荐信
2014/06/24 职场文书
营业用房租赁协议书
2014/11/26 职场文书
巧用 -webkit-box-reflect 倒影实现各类动效(小结)
2021/04/22 HTML / CSS
「玫瑰之王的葬礼」舞台剧主视觉图公开
2022/03/21 日漫