最后说说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的IE和Firefox兼容性汇编
Jul 01 Javascript
让iframe自适应高度(支持XHTML,支持FF)
Jul 24 Javascript
javascript 遍历验证所有文本框的值
Aug 27 Javascript
解决jquery的.animate()函数在IE6下的问题
Dec 03 Javascript
node.js中的socket.io入门实例
Apr 26 Javascript
用js替换除数字与逗号以外的所有字符的代码
Jun 07 Javascript
javascript中动态函数用法实例分析
May 14 Javascript
javaScript中定义类或对象的五种方式总结
Dec 04 Javascript
JSONP基础知识详解
Mar 19 Javascript
详解如何使用webpack打包Vue工程
May 27 Javascript
基于vue 开发中出现警告问题去除方法
Jan 25 Javascript
Vue-cli assets SubDirectory及PublicPath区别详解
Aug 18 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 microtime获取浮点的时间戳
2010/02/21 PHP
php判断页面是否是微信打开的示例(微信打开网页)
2014/04/25 PHP
php获取微信共享收货地址的方法
2017/12/21 PHP
js直接编辑当前cookie的脚本
2008/09/14 Javascript
jquery 层次选择器siblings与nextAll的区别介绍
2013/08/02 Javascript
使用jQuery判断浏览器滚动条位置的方法
2016/05/30 Javascript
深入学习js瀑布流布局
2016/10/14 Javascript
微信小程序通过api接口将json数据展现到小程序示例
2017/01/20 Javascript
老生常谈angularjs中的$state.go
2017/04/24 Javascript
JS自定义函数实现时间戳转换成date的方法示例
2017/08/27 Javascript
Angularjs实现页面模板清除的方法
2018/07/20 Javascript
jQuery实现模拟搜索引擎的智能提示功能简单示例
2019/01/27 jQuery
移动端自适应flexible.js的使用方法(不用三大框架,仅写一个单html页面使用)推荐
2019/04/02 Javascript
如何提升vue.js中大型数据的性能
2019/06/21 Javascript
Vue实现购物车详情页面的方法
2019/08/20 Javascript
layui 阻止图片上传的实例(before方法)
2019/09/26 Javascript
JavaScript实现图片上传并预览并提交ajax
2019/09/30 Javascript
解决vant的Toast组件时提示not defined的问题
2020/11/11 Javascript
在Python中用keys()方法返回字典键的教程
2015/05/21 Python
Mac中升级Python2.7到Python3.5步骤详解
2017/04/27 Python
python tkinter窗口最大化的实现
2019/07/15 Python
Python使用Tkinter实现滚动抽奖器效果
2020/01/06 Python
Python 如何查找特定类型文件
2020/08/17 Python
基于CentOS搭建Python Django环境过程解析
2020/08/24 Python
Python collections.deque双边队列原理详解
2020/10/05 Python
可以随进度显示不同颜色的css3进度条分享
2014/04/11 HTML / CSS
Infababy英国:婴儿推车、Travel System婴儿车和婴儿汽车座椅销售
2018/05/23 全球购物
Snapfish爱尔兰:在线照片打印和个性化照片礼品
2018/09/17 全球购物
初一家长会邀请函
2014/01/31 职场文书
优秀企业获奖感言
2014/02/01 职场文书
保安部任务及岗位职责
2014/02/25 职场文书
股票投资建议书
2014/05/19 职场文书
村干部群众路线整改措施思想汇报
2014/10/12 职场文书
2015年财务试用期工作总结
2014/12/24 职场文书
2019求职信:应届生求职信范文
2019/04/24 职场文书
Redis过期数据是否会被立马删除
2022/07/23 Redis