最后说说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 相关文章推荐
图片完美缩放
Sep 07 Javascript
iframe的onload在Chrome/Opera中执行两次Bug的解决方法
Mar 17 Javascript
初学Jquery插件制作 在SageCRM的查询屏幕隐藏部分行的功能
Dec 26 Javascript
javascript 日期时间 转换的方法
Feb 21 Javascript
php跨域调用json的例子
Nov 13 Javascript
javacript使用break内层跳出外层循环分析
Jan 12 Javascript
PassWord输入框代码分享
Jun 07 Javascript
Angularjs中使用指令绑定点击事件的方法
Mar 30 Javascript
JavaScript事件处理程序详解
Sep 19 Javascript
微信小程序返回上一页传参并刷新过程解析
Dec 13 Javascript
在vue中使用console.log无效的解决
Aug 09 Javascript
再也不怕 JavaScript 报错了,怎么看怎么处理都在这儿
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
php curl获取网页内容(IPV6下超时)的解决办法
2013/07/16 PHP
php实现根据词频生成tag云的方法
2015/04/17 PHP
浅析PHP关键词替换的类(避免重复替换,保留与还原原始链接)
2015/09/22 PHP
php版微信公众号接口实现发红包的方法
2016/10/14 PHP
PHP对XML内容进行修改和删除实例代码
2016/10/26 PHP
Swoole4.4协程抢占式调度器详解
2019/05/23 PHP
基于laravel Request的所有方法详解
2019/09/29 PHP
jquery中eq和get的区别与使用方法
2011/04/14 Javascript
js获取 type=radio 值的方法
2014/05/09 Javascript
深入探讨JavaScript、JQuery屏蔽网页鼠标右键菜单及禁止选择复制
2014/06/10 Javascript
手机端 HTML5使用photoswipe.js仿微信朋友圈图片放大效果
2016/08/25 Javascript
JavaScript基于Dom操作实现查找、修改HTML元素的内容及属性的方法
2017/01/20 Javascript
浅谈在Vue-cli里基于axios封装复用请求
2017/11/06 Javascript
js操作table中tr的顺序实现上移下移一行的效果
2018/11/22 Javascript
微信小程序顶部导航栏滑动tab效果
2019/01/28 Javascript
element-ui 中使用upload多文件上传只请求一次接口
2019/07/19 Javascript
javascript 构建模块化开发过程解析
2019/09/11 Javascript
vue中对象数组去重的实现
2020/02/06 Javascript
python学习笔记:字典的使用示例详解
2014/06/13 Python
python类和函数中使用静态变量的方法
2015/05/09 Python
Pycharm设置utf-8自动显示方法
2019/01/17 Python
Python脚本按照当前日期创建多级目录
2019/03/01 Python
Python Web程序搭建简单的Web服务器
2019/07/31 Python
项目资料员岗位职责
2013/12/10 职场文书
餐饮业的创业计划书范文
2013/12/26 职场文书
教师岗位职责范本
2013/12/29 职场文书
餐厅考勤管理制度
2014/01/28 职场文书
建筑设计专业求职自我评价
2014/03/02 职场文书
网络管理专业求职信
2014/03/15 职场文书
旺仔牛奶广告词
2014/03/20 职场文书
交通事故协议书
2014/04/15 职场文书
社区戒毒工作方案
2014/06/04 职场文书
小学新教师个人总结
2015/02/05 职场文书
铁人观后感
2015/06/16 职场文书
六五普法先进个人主要事迹材料
2015/11/03 职场文书
Python中第三方库Faker的使用详解
2022/04/02 Python