nuxt.js 缓存实践


Posted in Javascript onJune 25, 2018

nuxt 是基于 vue 的 ssr 解决方案,可以是使用vue语法完成前后端的同构。

然而在与传统纯字符串拼接的 ssr 方案相比,性能就没那么好了, nuxt 需要在服务端生成虚拟 dom ,然后再序列化出HTML字符串,我们常说 nodejs 的高性能指的是异步IO操作频繁的场景而非CPU操作密集的场景,毕竟 nodejs 是运行在单线程下的,在涉及到高并发的场景下,性能就会有所下降,可以考虑采用合理的缓存策略

nuxt 的缓存可以分为 组件级别缓存 , API级别缓存 以及 页面级别缓存

组件级别的缓存

配置项 nuxt.config.js 的配置大概长这样子:

const LRU = require('lru-cache')
module.exports = {
 render: {
  bundleRenderer: {
   cache: LRU({
    max: 1000,           // 最大的缓存个数
    maxAge: 1000 * 60 * 15    // 缓存15分钟
   })
  }
 }
}

并不是说配了该项就实现了组件级别的缓存,还需要在需做缓存的 vue 组件上增加 name 以及 serverCacheKey 字段,以确定缓存的唯一键值,比如:

export default {
 name: 'AppHeader',
 props: ['type'],
 serverCacheKey: props => props.type
}

上述组件会根据父组件传下来的 type 值去做缓存,键值是: AppHeader::${props.type} ,由此,新的请求到来时,只要父组件传下来的 type 属性之前处理过,就可以复用之前的渲染缓存结果,以增进性能

从该例子可以看出,如果该组件除了依赖父组件的 type 属性,还依赖于别的属性, serverCacheKey 这里也要做出相应的改变,因此,如果组件依赖于很多的全局状态,或者,依赖的状态取值非常多,意味需要缓存会被频繁被设置而导致溢出,其实就没有多大意义了,在 lru-cache 的配置中,设置的最大缓存个数是1000,超出部分就会被清掉

其次,不应该缓存可能对渲染上下文产生副作用的子组件,比如,组件的 created beforeCreated 的钩子在服务端也会走,组件被缓存后就不会执行了,这些可能影响到渲染上下文的地方也要小心,更多内容请参考:组件级别缓存

一般来说,比较适合的场景是 v-for 大量数据的渲染,因为循环操作比较耗cpu

API级别的缓存

在服务端渲染的场景中,往往会将请求放在服务端去做,渲染完页面再返回给浏览器,而有些接口是可以去做缓存的,比如,不依赖登录态且不依赖过多参数的接口或者是单纯获取配置数据的接口等,接口的处理也是需要时间的,对接口的缓存可以加快每个请求的处理速度,更快地释放掉请求,从而增进性能

api的请求使用 axios , axios 即可以在服务端使用也可是在浏览器使用,代码大概长这样子

import axios from 'axios'
import md5 from 'md5'
import LRU from 'lru-cache'

// 给api加3秒缓存
const CACHED = LRU({
 max: 1000,
 maxAge: 1000 * 3
})

function request (config) {
 let key
 // 服务端才加缓存,浏览器端就不管了
 if (config.cache && !process.browser) {
  const { params = {}, data = {} } = config
  key = md5(config.url + JSON.stringify(params) + JSON.stringify(data))
  if (CACHED.has(key)) {
   // 缓存命中
   return Promise.resolve(CACHED.get(key))
  }
 }
 return axios(config)
  .then(rsp => {
   if (config.cache && !process.browser) {
    // 返回结果前先设置缓存
    CACHED.set(key, rsp.data)
   }
   return rsp.data
  })
}

使用上跟平时使用 axios 还是一样的,就多加了个 cache 的属性标识是否需要在服务端做缓存

const api = {
 getGames: params => request({
  url: '/gameInfo/gatGames',
  params,
  cache: true
 })
}

页面级别的缓存

在不依赖于登录态以及过多参数的情况下,如果并发量很大,可以考虑使用页面级别的缓存, 在 nuxt.config.js 增加 serverMiddleware 属性

const nuxtPageCache = require('nuxt-page-cache')

module.exports = {
 serverMiddleware: [
  nuxtPageCache.cacheSeconds(1, req => {
   if (req.query && req.query.pageType) {
    return req.query.pageType
   }
   return false
  })
 ]
}

上面的例子根据链接后面的参数 pageType 去做缓存,如果链接后面有 pageType 参数,就做缓存,缓存时间为1秒,也就是说在1秒内相同的 pageType 请求,服务端只会执行一次完整的渲染

nuxt-page-cache 参考了route-cache ,写得比较简陋,你也可以重新封装下,nuxt最终返回数据是使用 res.end(html, 'utf-8') ,页面级别的缓存大概的思想如下:

const LRU = require('lru-cache')

let cacheStore = new LRU({
 max: 100,     // 设置最大的缓存个数
 maxAge: 200
})

module.exports.cacheSeconds = function (secondsTTL, cacheKey) {
 // 设置缓存的时间
 const ttl = secondsTTL * 1000
 return function (req, res, next) {
  // 获取缓存的key值
  let key = req.originalUrl
  if (typeof cacheKey === 'function') {
   key = cacheKey(req, res)
   if (!key) { return next() }
  } else if (typeof cacheKey === 'string') {
   key = cacheKey
  }

  // 如果缓存命中,直接返回
  const value = cacheStore.get(key)
  if (value) {
   return res.end(value, 'utf-8')
  }

  // 缓存原先的end方案
  res.original_end = res.end

  // 重写res.end方案,由此nuxt调用res.end实际上是调用该方法,
  res.end = function () {
   if (res.statusCode === 200) {
    // 设置缓存
    cacheStore.set(key, data, ttl)
   }
   // 最终返回结果
   res.original_end(data, 'utf-8')
  }
 }
}

如果缓存命中,直接将原先的计算结果返回,大大提供了性能

总结

在高并发的情况下可以考虑使用缓存,而缓存策略的使用需要视场景而定,这里不再赘述,还可以考虑使用pm2开启集群模式去管理我们的进程,从而满足更高的并发。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
禁止你的左键复制实用技巧
Jan 04 Javascript
JavaScript实现找出字符串中第一个不重复的字符
Sep 03 Javascript
JS中产生标识符方式的演变
Jun 12 Javascript
Bootstrap每天必学之日期控制
Mar 07 Javascript
URL的参数中有加号传值变为空格的问题(URL特殊字符)
Nov 04 Javascript
Vue2学习笔记之请求数据交互vue-resource
Feb 23 Javascript
微信小程序城市定位的实现实例(获取当前所在国家城市信息)
May 17 Javascript
小程序开发中如何使用async-await并封装公共异步请求的方法
Jan 20 Javascript
详解关于Vuex的action传入多个参数的问题
Feb 22 Javascript
js验证密码强度解析
Mar 18 Javascript
javascript使用正则表达式实现注册登入校验
Sep 23 Javascript
解决Vue keep-alive 调用 $destory() 页面不再被缓存的情况
Oct 30 Javascript
vue2.0使用v-for循环制作多级嵌套菜单栏
Jun 25 #Javascript
浅谈super-vuex使用体验
Jun 25 #Javascript
使用vue-router beforEach实现判断用户登录跳转路由筛选功能
Jun 25 #Javascript
使用vue的transition完成滑动过渡的示例代码
Jun 25 #Javascript
JS实现仿微信支付弹窗功能
Jun 25 #Javascript
vue.js 实现输入框动态添加功能
Jun 25 #Javascript
vue-router重定向不刷新问题的解决
Jun 25 #Javascript
You might like
PHP中Session的概念
2006/10/09 PHP
PHP Document 代码注释规范
2009/04/13 PHP
php INI配置文件的解析实现分析
2011/01/04 PHP
国产PHP开发框架myqee新手快速入门教程
2014/07/14 PHP
php微信公众平台开发(四)回复功能开发
2016/12/06 PHP
PHP 实现文件压缩解压操作的方法
2019/06/14 PHP
JavaScript设计模式之策略模式实例
2014/10/10 Javascript
网站基于flash实现的Banner图切换效果代码
2014/10/14 Javascript
JavaScript分秒倒计时器实现方法
2015/02/02 Javascript
js实现下拉框效果(select)
2017/03/28 Javascript
兼容浏览器的js事件绑定函数(详解)
2017/05/09 Javascript
JS实现简单抖动效果
2017/06/01 Javascript
说说AngularJS中的$parse和$eval的用法
2017/09/14 Javascript
angular使用md5,CryptoJS des加密的方法
2019/06/03 Javascript
原生js通过一行代码实现简易轮播图
2019/06/05 Javascript
ECharts地图绘制和钻取简易接口详解
2019/07/12 Javascript
layui.tree组件的使用以及搜索节点功能的实现
2019/09/26 Javascript
jquery实现上传文件进度条
2020/03/26 jQuery
vue中activated的用法
2021/01/03 Vue.js
[00:37]2016完美“圣”典风云人物:AMS宣传片
2016/12/06 DOTA
[01:52]PWL S2开团时刻第四期——DOTA2成语故事
2020/12/03 DOTA
浅析python 中__name__ = '__main__' 的作用
2014/07/05 Python
Python中使用 Selenium 实现网页截图实例
2014/07/18 Python
利用python批量修改word文件名的方法示例
2017/10/17 Python
快速了解Python开发中的cookie及简单代码示例
2018/01/17 Python
TensorFlow搭建神经网络最佳实践
2018/03/09 Python
使用pandas将numpy中的数组数据保存到csv文件的方法
2018/06/14 Python
在python 中实现运行多条shell命令
2019/01/07 Python
Python实现树莓派摄像头持续录像并传送到主机的步骤
2020/11/30 Python
移动端Web页面的CSS3 flex布局快速上手指南
2016/05/31 HTML / CSS
约瑟夫·特纳男装:Joseph Turner
2017/10/10 全球购物
德国2018年度最佳在线药房:Bodfeld Apotheke
2019/11/04 全球购物
国外的一些J2EE面试题一
2012/10/13 面试题
向国旗敬礼活动总结范文2014
2014/09/27 职场文书
常务副总经理岗位职责
2015/02/02 职场文书
详解SQL报错盲注
2022/07/23 SQL Server