最后说说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中的undefined和not defined区别示例介绍
Feb 26 Javascript
使用iframe window的scroll方法控制iframe页面滚动
Mar 05 Javascript
深入理解JS中的substr和substring
Apr 26 Javascript
jQuery过滤特殊字符及JS字符串转为数字
May 26 Javascript
JQuery在循环中绑定事件的问题详解
Jun 02 Javascript
js捕捉键盘事件和按键键值的方法
Oct 10 Javascript
jQuery EasyUI中的日期控件DateBox修改方法
Nov 09 Javascript
JS中检测数据类型的几种方式及优缺点小结
Dec 12 Javascript
jquery Ajax 全局调用封装实例详解
Jan 16 Javascript
vue如何引用其他组件(css和js)
Apr 13 Javascript
jQuery实现简单的手风琴效果
Apr 17 jQuery
jQuery实现的简单前端搜索功能示例
Oct 28 jQuery
详解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 在5.1.* 和5.2.*之间 PDO数据库操作中的不同之处小结
2012/03/07 PHP
php使浏览器直接下载pdf文件的方法
2013/11/15 PHP
php分页原理 分页代码 分页类制作教程
2016/09/23 PHP
PHP正则删除HTML代码中宽高样式的方法
2017/06/12 PHP
Ajax请求PHP后台接口返回信息的实例代码
2018/08/21 PHP
读jQuery之五(取DOM元素)
2011/06/20 Javascript
ajax上传时参数提交不更新等相关问题
2012/12/11 Javascript
JQuery入门——事件切换之toggle()方法应用介绍
2013/02/05 Javascript
javascript操作html控件实例(javascript添加html)
2013/12/02 Javascript
javascript和jquery实现用户登录验证
2016/05/04 Javascript
用js实现放大镜的效果的简单实例
2016/05/23 Javascript
原生JS京东轮播图代码
2017/03/22 Javascript
重新理解JavaScript的六种继承方式
2017/03/24 Javascript
vue项目中使用axios上传图片等文件操作
2017/11/02 Javascript
webpack实用小功能介绍
2018/01/02 Javascript
vue移动端路由切换实例分析
2018/05/14 Javascript
在vue中使用express-mock搭建mock服务的方法
2018/11/07 Javascript
解决LayUI数据表格复选框不居中显示的问题
2019/09/25 Javascript
JS实现电商商品展示放大镜特效
2020/01/07 Javascript
基于element-ui封装表单金额输入框的方法示例
2021/01/06 Javascript
[47:12]TFT vs Secret Supermajor小组赛C组 BO3 第三场 6.3
2018/06/04 DOTA
Python操作串口的方法
2015/06/17 Python
Python实现重建二叉树的三种方法详解
2018/06/23 Python
对pandas数据判断是否为NaN值的方法详解
2018/11/06 Python
python绘制高斯曲线
2021/02/19 Python
HTML5 虚拟键盘出现挡住输入框的解决办法
2017/02/14 HTML / CSS
提供世界各地便宜的机票:Sky-tours
2016/07/21 全球购物
澳大利亚婴儿礼品公司:The Baby Gift Company
2018/11/04 全球购物
抽象类和接口的区别
2012/09/19 面试题
预备党员思想汇报范文
2014/01/11 职场文书
食品安全承诺书
2014/05/22 职场文书
2015年七一建党节慰问信
2015/03/23 职场文书
幼儿园大班开学寄语(2016秋季)
2015/12/03 职场文书
600字作文之感受大自然
2019/11/27 职场文书
postgres之jsonb属性的使用操作
2021/06/23 PostgreSQL
Python用tkinter实现自定义记事本的方法详解
2022/03/31 Python