最后说说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 相关文章推荐
25个非常棒的jQuery滑块插件和教程小结
Sep 02 Javascript
javascript中setAttribute兼容性用法分析
Dec 12 Javascript
Bootstrap警告(Alerts)的实现方法
Mar 22 Javascript
AngularJS基于http请求实现下载php生成的excel文件功能示例
Jan 23 Javascript
Vue组件通信的四种方式汇总
Feb 08 Javascript
vue.js使用3DES加密的方法示例
May 18 Javascript
jQuery事件委托代码实践详解
Jun 21 jQuery
基于vue+uniapp直播项目实现uni-app仿抖音/陌陌直播室功能
Nov 12 Javascript
vue使用swiper实现中间大两边小的轮播图效果
Nov 24 Javascript
Vue 打包的静态文件不能直接运行的原因及解决办法
Nov 19 Vue.js
梳理一下vue中的生命周期
Dec 30 Vue.js
node中使用shell脚本的方法步骤
Mar 23 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/07/13 国漫
PHP 读取文件内容代码(txt,js等)
2009/12/06 PHP
Thinkphp中import的几个用法详细介绍
2014/07/02 PHP
PHP的mysqli_stmt_init()函数讲解
2019/01/24 PHP
JavaScript面向对象编程
2008/03/02 Javascript
jquery ajax 同步异步的执行 return值不能取得的解决方案
2012/01/08 Javascript
javascript加号"+"的二义性说明
2013/03/04 Javascript
JSON序列化与解析原生JS方法且IE6和chrome测试通过
2013/09/05 Javascript
javascript实现全局匹配并替换的方法
2015/04/27 Javascript
JS获取input file绝对路径的方法(推荐)
2016/08/02 Javascript
jQuery插件ajaxFileUpload使用详解
2017/01/10 Javascript
jQuery对table表格进行增删改查
2020/12/22 Javascript
Angular1.x复杂指令实例详解
2017/03/01 Javascript
引入JavaScript时alert弹出框显示中文乱码问题
2017/09/16 Javascript
简单介绍react redux的中间件的使用
2018/04/06 Javascript
vuex 解决报错this.$store.commit is not a function的方法
2018/12/17 Javascript
JavaScript闭包相关知识解析
2019/10/19 Javascript
Vue自定义表单内容检查rules实例
2020/10/30 Javascript
[36:14]DOTA2上海特级锦标赛D组小组赛#1 EG VS COL第二局
2016/02/28 DOTA
python3.4用循环往mysql5.7中写数据并输出的实现方法
2017/06/20 Python
利用Django模版生成树状结构实例代码
2019/05/19 Python
python 计算数据偏差和峰度的方法
2019/06/29 Python
python类的实例化问题解决
2019/08/31 Python
python开发实例之Python的Twisted框架中Deferred对象的详细用法与实例
2020/03/19 Python
python实现梯度法 python最速下降法
2020/03/24 Python
python math模块的基本使用教程
2021/01/16 Python
html5实现多图片预览上传及点击可拖拽控件
2018/03/15 HTML / CSS
canvas小画板之平滑曲线的实现
2020/08/12 HTML / CSS
英国巧克力贸易公司:Chocolate Trading Company
2017/03/21 全球购物
New Era英国官网:美国棒球帽品牌
2018/03/21 全球购物
应届毕业生求职信范文分享
2013/12/26 职场文书
关于人生的感言
2014/01/17 职场文书
医院领导班子查摆问题对照检查材料思想汇报
2014/10/08 职场文书
2019奶茶店创业计划书范本!
2019/07/15 职场文书
Window server 2012 R2 AD域的组策略相关设置
2022/04/28 Servers
Python开发简易五子棋小游戏
2022/05/02 Python