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 相关文章推荐
flexigrid 类似ext grid的JS表格代码
Jul 17 Javascript
javascript下利用arguments实现string.format函数
Aug 24 Javascript
网络之美 JavaScript中Get和Set访问器的实现代码
Sep 19 Javascript
jquery 实现滚动条下拉时无限加载的简单实例
Jun 01 Javascript
JS实现颜色梯度与渐变效果完整实例
Dec 30 Javascript
JS实现旋转木马式图片轮播效果
Jan 18 Javascript
package.json文件配置详解
Jun 15 Javascript
vue forEach循环数组拿到自己想要的数据方法
Sep 21 Javascript
微信小程序+云开发实现欢迎登录注册
May 24 Javascript
vue.js实现二级菜单效果
Oct 19 Javascript
详解小程序BackgroundAudioManager踩坑之旅
Dec 08 Javascript
浅谈vue中document.getElementById()拿到的是原值的问题
Jul 26 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
写一个用户在线显示的程序
2006/10/09 PHP
PHP页面转UTF-8中文编码乱码的解决办法
2015/10/20 PHP
PHP redis实现超迷你全文检索
2017/03/04 PHP
PHP-FPM 设置多pool及配置文件重写操作示例
2019/10/02 PHP
AutoSave/自动存储功能实现
2007/03/24 Javascript
Jquery之Ajax运用 学习运用篇
2011/09/26 Javascript
JavaScript高级程序设计 阅读笔记(十四) js继承机制的实现
2012/08/14 Javascript
关于jQuery $.isNumeric vs. $.isNaN vs. isNaN
2013/04/15 Javascript
JavaScript常用脚本汇总(一)
2015/03/04 Javascript
jQuery随机密码生成的方法
2015/03/09 Javascript
javascript实现一个数值加法函数
2015/06/26 Javascript
jQuery时间轴插件使用详解
2015/07/16 Javascript
js仿手机页面文件下拉刷新效果
2016/10/14 Javascript
BootStrap Validator对于隐藏域验证和程序赋值即时验证的问题浅析
2016/12/01 Javascript
Nuxt.js踩坑总结分享
2018/01/18 Javascript
js点击事件的执行过程实例分析【冒泡与捕获】
2020/04/11 Javascript
浅谈vue 二级路由嵌套和二级路由高亮问题
2020/08/06 Javascript
vue+element table表格实现动态列筛选的示例代码
2021/01/14 Vue.js
[49:40]2018DOTA2亚洲邀请赛小组赛 A组加赛 TNC vs Newbee
2018/04/03 DOTA
python文件读写并使用mysql批量插入示例分享(python操作mysql)
2014/02/17 Python
Python3写入文件常用方法实例分析
2015/05/22 Python
python提取页面内url列表的方法
2015/05/25 Python
解决Scrapy安装错误:Microsoft Visual C++ 14.0 is required...
2017/10/01 Python
Python安装图文教程 Pycharm安装教程
2018/03/27 Python
TensorFlow2.1.0安装过程中setuptools、wrapt等相关错误指南
2020/04/08 Python
Python爬虫实例——scrapy框架爬取拉勾网招聘信息
2020/07/14 Python
HTML5 Canvas旋转动画的2个代码例子(一个旋转的太极图效果)
2014/04/10 HTML / CSS
塔吉特百货公司官网:Target
2017/04/27 全球购物
美国单身专业人士在线约会网站:EliteSingles
2019/03/19 全球购物
印度电子产品购物网站:Vijay Sales
2021/02/16 全球购物
店长岗位的工作内容
2013/11/12 职场文书
运动会开幕式主持词
2014/03/28 职场文书
政府班子四风问题整改措施思想汇报
2014/10/08 职场文书
期末复习计划
2015/01/19 职场文书
史上最牛的辞职信
2015/02/28 职场文书
oracle设置密码复杂度及设置超时退出的功能
2022/06/28 Oracle