最后说说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增删元素的代码
Feb 14 Javascript
js中符号转意问题示例探讨
Aug 19 Javascript
浅谈JS中逗号运算符的用法
Jun 12 Javascript
node.js实现博客小爬虫的实例代码
Oct 08 Javascript
Bootstrap超大屏幕的实现代码
Mar 22 Javascript
使用webpack搭建react开发环境的方法
May 15 Javascript
node app 打包工具pkg的具体使用
Jan 17 Javascript
Node.js API详解之 querystring用法实例分析
Apr 29 Javascript
原生js实现购物车
Sep 23 Javascript
Nuxt的动态路由和参数校验操作
Nov 09 Javascript
详解vue 组件注册
Nov 20 Vue.js
JS常用跨域方法实现原理解析
Dec 09 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
Discuz! Passport 通行证整合
2008/03/27 PHP
我的php学习笔记(毕业设计)
2012/02/21 PHP
PHP以指定字段为索引返回数据库所取的数据数组
2013/06/30 PHP
PHP网页游戏学习之Xnova(ogame)源码解读(五)
2014/06/23 PHP
浅谈PHP解析URL函数parse_url和parse_str
2014/11/11 PHP
Yii安装与使用Excel扩展的方法
2016/07/13 PHP
PHP+AJAX 投票器功能
2017/11/11 PHP
清空上传控件input file的值
2010/07/03 Javascript
基于jquery实现漂亮的动态信息提示效果
2011/08/02 Javascript
js判断undefined变量类型使用typeof
2013/06/03 Javascript
JS预览图像将本地图片显示到浏览器上
2013/08/25 Javascript
浅谈JavaScript中null和undefined
2015/07/09 Javascript
js和jquery实现监听键盘事件示例代码
2020/06/24 Javascript
Javascript生成全局唯一标识符(GUID,UUID)的方法
2016/02/27 Javascript
JS简单循环遍历json数组的方法
2016/04/22 Javascript
JS组件系列之MVVM组件 vue 30分钟搞定前端增删改查
2017/04/28 Javascript
基于vue实现一个神奇的动态按钮效果
2019/05/15 Javascript
Vue使用富文本编辑器Vue-Quill-Editor(含图片自定义上传服务、清除复制粘贴样式等)
2020/05/15 Javascript
解决Nuxt使用axios跨域问题
2020/07/06 Javascript
详解Python中for循环的使用方法
2015/05/14 Python
Python中文竖排显示的方法
2015/07/28 Python
对python中的控制条件、循环和跳出详解
2019/06/24 Python
基于python中__add__函数的用法
2019/11/25 Python
html5指南-2.如何操作document metadata
2013/01/07 HTML / CSS
HTML5注册表单的自动聚焦与占位文本示例代码
2013/07/19 HTML / CSS
韩国著名的在线综合购物网站:Akmall
2016/08/07 全球购物
美国玛丽莎收藏奢华时尚商店:Marissa Collections
2016/11/21 全球购物
介绍一下Linux文件的记录形式
2013/09/29 面试题
浅谈react路由传参的几种方式
2021/03/23 Javascript
就业推荐表自我鉴定
2014/03/21 职场文书
网吧最新创业计划书范文
2014/03/27 职场文书
专科应届毕业生求职信
2014/06/04 职场文书
大学运动会通讯稿
2015/07/18 职场文书
2019年特色火锅店的创业计划书模板
2019/08/28 职场文书
安装配置mysql及Navicat prenium的详细流程
2021/06/10 MySQL
SpringBoot连接MySQL获取数据写后端接口的操作方法
2021/11/02 MySQL