最后说说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实现自定义tooltip示例代码
Feb 12 Javascript
实现图片预加载的三大方法及优缺点分析
Nov 19 Javascript
Javascript基础教程之数据类型 (数值 Number)
Jan 18 Javascript
JS扩展方法实例分析
Apr 15 Javascript
JS实现CheckBox复选框全选全不选功能
May 06 Javascript
浅谈JavaScript中的Math.atan()方法的使用
Jun 14 Javascript
javascript中$(function() {});写与不写有哪些区别
Aug 10 Javascript
JS中取二维数组中最大值的方法汇总
Apr 17 Javascript
canvas实现环形进度条效果
Mar 23 Javascript
为JQuery EasyUI 表单组件增加焦点切换功能的方法
Apr 13 jQuery
jQuery Datatable 多个查询条件自定义提交事件(推荐)
Aug 24 jQuery
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
Sep 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页面执行时间,数据库读写次数,函数调用次数等(THINKphp)
2013/06/03 PHP
php接口隔离原则实例分析
2019/11/11 PHP
JavaScript 5 新增 Array 方法实现介绍
2012/02/06 Javascript
JS 两个字符串时间的天数差计算
2013/08/25 Javascript
Jquery getJSON方法详细分析
2013/12/26 Javascript
Bootstrap每天必学之基础排版
2015/11/20 Javascript
实用jquery操作表单元素的简单代码
2016/07/04 Javascript
JS实现Ajax的方法分析
2016/12/20 Javascript
javascript实现简单的ajax封装示例
2016/12/28 Javascript
AngularJS中$http使用的简单介绍
2017/03/17 Javascript
vue上传图片组件编写代码
2017/07/26 Javascript
js弹性势能动画之抛物线运动实例详解
2017/07/27 Javascript
angular中的cookie读写方法
2017/08/02 Javascript
详解vue2.0监听属性的使用心得及搭配计算属性的使用
2018/07/18 Javascript
jQuery选择器之基本选择器用法实例分析
2019/02/19 jQuery
Vue事件修饰符native、self示例详解
2019/07/09 Javascript
JS常用正则表达式超全集(密码强度校验,金额校验,IE版本,IPv4,IPv6校验)
2020/02/03 Javascript
Python提示[Errno 32]Broken pipe导致线程crash错误解决方法
2014/11/19 Python
Python中实现常量(Const)功能
2015/01/28 Python
Python tkinter实现的图片移动碰撞动画效果【附源码下载】
2018/01/04 Python
Python使用 Beanstalkd 做异步任务处理的方法
2018/04/24 Python
python 求一个列表中所有元素的乘积实例
2019/06/11 Python
python文件读写代码实例
2019/10/21 Python
Python手动或自动协程操作方法解析
2020/06/22 Python
python实现最短路径的实例方法
2020/07/19 Python
汽车技术服务与营销专业在籍生自荐信
2013/09/28 职场文书
自我鉴定思想方面
2013/10/07 职场文书
中国央视网签名寄语
2014/01/18 职场文书
经典安踏广告词
2014/03/21 职场文书
认购协议书范本
2014/04/22 职场文书
法定代表人身份证明书
2014/09/10 职场文书
2014年手术室工作总结
2014/11/26 职场文书
学期个人自我总结
2015/02/13 职场文书
2015社区健康教育工作总结
2015/05/20 职场文书
丧事主持词
2015/07/02 职场文书
Python函数中的不定长参数相关知识总结
2021/06/24 Python