最后说说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 相关文章推荐
js下写一个事件队列操作函数
Jul 19 Javascript
Jquery动态进行图片缩略的原理及实现
Aug 13 Javascript
在jquery中combobox多选的不兼容问题总结
Dec 24 Javascript
ECMAScript6函数剩余参数(Rest Parameters)
Jun 12 Javascript
JavaScript代码轻松实现网页内容禁止复制(代码简单)
Oct 23 Javascript
浅析JS获取url中的参数实例代码
Jun 14 Javascript
Ajax+FormData+javascript实现无刷新表单信息提交
Oct 24 Javascript
JavaScript ES6中export、import与export default的用法和区别
Mar 14 Javascript
vue2.0中click点击当前li实现动态切换class
Jun 21 Javascript
vue实现弹框遮罩点击其他区域弹框关闭及v-if与v-show的区别介绍
Sep 29 Javascript
node错误处理与日志记录的实现
Dec 24 Javascript
小程序云开发教程如何使用云函数实现点赞功能
May 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
苏联队长,苏联超人蝙蝠侠,这些登场的“山寨”英雄真的很严肃
2020/04/09 欧美动漫
php+mysql+jquery实现简易的检索自动补全提示功能
2017/04/15 PHP
jquery 实现的全选和反选
2009/04/15 Javascript
jquery获取tr中控件值并操作tr实现思路
2013/03/27 Javascript
如何使用jquery动态加载js,css文件实现代码
2013/04/03 Javascript
Jquery 跨域访问 Lightswitch OData Service的方法
2013/09/11 Javascript
js动态控制table的tr、td增加及删除的具体实现
2014/04/30 Javascript
页面js遇到乱码问题的解决方法是和无法转码的情况
2014/04/30 Javascript
js数组中如何随机取出一个值
2014/06/13 Javascript
jQuery实现倒计时按钮功能代码分享
2014/09/03 Javascript
通过Tabs方法基于easyUI+bootstrap制作工作站
2016/03/28 Javascript
基于JavaScript实现购物网站商品放大镜效果
2016/09/06 Javascript
利用js编写响应式侧边栏
2016/09/17 Javascript
Angular中ng-options下拉数据默认值的设定方法
2017/06/21 Javascript
JSON在Javascript中的使用(eval和JSON.parse的区别)详细解析
2017/09/05 Javascript
vue在使用ECharts时的异步更新和数据加载详解
2017/11/22 Javascript
多个vue子路由文件自动化合并的方法
2019/09/03 Javascript
jQuery cookie的公共方法封装和使用示例
2020/06/01 jQuery
最全vue的vue-amap使用高德地图插件画多边形范围的示例代码
2020/07/17 Javascript
typescript配置alias的详细步骤
2020/08/12 Javascript
Vue 修改网站图标的方法
2020/12/31 Vue.js
[42:52]IG vs VGJ.T 2018国际邀请赛小组赛BO2 第二场 8.18
2018/08/19 DOTA
[47:10]完美世界DOTA2联赛PWL S3 LBZS vs Rebirth 第二场 12.16
2020/12/18 DOTA
Python正则获取、过滤或者替换HTML标签的方法
2016/01/28 Python
JS设计模式之责任链模式实例详解
2018/02/03 Python
python实现批量解析邮件并下载附件
2018/06/19 Python
pytorch如何冻结某层参数的实现
2020/01/10 Python
俄罗斯最大的香水和化妆品网上商店:Randewoo
2020/11/05 全球购物
客服专员岗位职责
2014/02/28 职场文书
自强之星事迹材料
2014/05/12 职场文书
体育专业求职信
2014/07/16 职场文书
法制演讲稿
2014/09/10 职场文书
2014年大学生党员自我评议
2014/09/22 职场文书
离婚协议书范本(2014版)
2014/09/28 职场文书
2014年电厂工作总结
2014/12/04 职场文书
导游词之湖北梁子湖
2019/11/07 职场文书