【Nuxt】03 Nuxt2 页面缓存、组件缓存、api缓存

基于nuxt.js的服务端渲染项目可以通过缓存来优化的场景有以下几点:

优化点参考文档及思路优化场景/条件特别说明检测方法
1. 页面缓存vue官方文档页面内容不是用户特定(即对于相同的 URL,总是为所有用户渲染相同的内容)一般来说,一个页面在服务端做了持久化缓存,那么对应页面的存在的api缓存,组件缓存也就没有意义了,对于页面缓存与api缓存同时存在的情况下(有可能存在),api缓存的时间应该比页面缓存的时间小,这样是为了让api响应的内容保持最新1、代码本地测试:在asyncData中打印测试日志,页面缓存后,刷新页面后服务端不会输出测试日志;2、比较html页面加载的DOMContentLoaded时间,刷新页面可以看到缓存后的值比首次页面加载(未缓存)的值要小
2. api缓存在axios请求与响应拦截器中去做接口响应内容不是用户特定(即对于相同的api接口URL,即总是为所有用户响应相同的内容)一般请求方式为GET的api请求比较首次请求与缓存后的api接口响应的时间
3. 组件缓存nuxtjs官网文档 vue 官网文档不依赖与全局状态,对渲染上下文不产生副作用的子组件要缓存的组件name值必须唯一,serverCacheKey根据某个prop的值作为唯一key检测方法同页面缓存检测方法一致,这个可能几乎察觉不到
4. asyncData函数优化Promise.all该函数中请求api接口数超过1个,多的甚至达到10,20多个,这种情况我们不能使用async await,请求完一个再接着请求下一个(同步请求接口);如果有10个接口需要请求,每个接口平均响应1s,那么至少需要10s才会响应html页面;如果使用Promise.all异步请求10个接口,那么最快接近1s响应html页面;asyncData函数会在服务端执行代码,因此一定要做好容错处理;另外如果该函数代码一直未执行完,那么页面首次响应将会被挂起,一直处于加载中对于页面首次加载,该函数执行耗时越短,页面响应时间就越短(页面加载越快)

1、页面缓存功能模块实现

我们在项目根目录中创建一个文件 ~/serverMiddleware/page-cache.js

import LRUCache from 'lru-cache'const cache = new LRUCache({maxAge: 1000 * 60 * 2, // 有效期2分钟max: 1000 // 最大缓存数量
})export default function(req, res, next) {// 本地开发环境不做页面缓存if (process.env.NODE_ENV !== 'development') {try {const cacheKey = req.urlconst cacheData = cache.get(cacheKey)if (cacheData) {return res.end(cacheData, 'utf8')}const originalEnd = res.endres.end = function(data) {cache.set(cacheKey, data)originalEnd.call(res, ...arguments)}} catch(error) {// console.log(`page-cache-middleware: ${error}`)next()}}next()
}

2、api缓存功能模块实现

我们在项目根目录中分别创建两个文件 ~/plugins/axios/createCacheKey.js 与 ~/plugins/axios/cache.js ;特别坑的一点是nuxt.js开发环境cache.js插件代码在页面刷新,路由切换都相当于首次运行,因此你会发现缓存功能失效,只有在 process.env.NODE_ENV === ‘production’ 生产环境中测试有效

// ~/plugins/axios/createCacheKey.jsimport md5 from 'md5'/*** 根据请求配置,是否是请求拦截器 创建缓存key* @param {Object} config* @param {Boolean} isRequest */export default function createCacheKey(config = {},isRequest = false
) {const {url,data,params,method,baseURL,} = config || {}let commonUrl = url/*** request拦截器中config.url是未拼接baseURL的,response拦截器中response.config.url是拼接过baseURL的,* 为了保持统一,使用统一拼接baseURL的commonUrl;注意下面的if条件判断*/if (isRequest && !commonUrl.match(baseURL) && !commonUrl.match(/^https?/)) {commonUrl = !!baseURL.match(/.+\/$/) ? `${baseURL.replace(/\/$/, '')}${url}` : `${baseURL}${url}`}// 根据请求指令,url,body体,参数生成规则const rule = `method=${method}-url=${commonUrl}-data=${JSON.stringify(data || {})}-params=${JSON.stringify(params || {})}`// md5加密return md5(rule)
}
// ~/plugins/axios/cache.js
import LRUCache from 'lru-cache'
import axios from 'axios'
import globalConfig from '../../global-config'
import createCacheKey from './createCacheKey'const cache = new LRUCache({maxAge: 1000 * 60, // 有效期60秒,如果存在页面缓存,api缓存的时间应该比页面缓存的时间小,这样是为了让api响应的内容保持最新max: 1000 // 最大缓存数量
})/*** matchCacheCondition 是否满足持久化缓存条件:服务端运行时 && 非本地开发环境 && api请求为get请求方式* @param {Object} config 请求配置*/
function matchCacheCondition(config = {}) {return process.server && process.env.NODE_ENV !== 'development' && config.method.toLowerCase() === 'get'
}/*** 如果所有页面都启用了缓存,api缓存就没有必要了*/
export default function({ $axios, redirect }) {$axios.interceptors.request.use(config => {const { baseUrl } = globalConfigconfig.baseURL = baseUrl[process.env.environment] || baseUrl['other']// 不满足缓存条件直接return configif (!matchCacheCondition(config)) {return config}const cacheKey = createCacheKey(config, true)const cacheData = cache.get(cacheKey)if (cacheData) {const source = axios.CancelToken.source()config.cancelToken = source.tokensource.cancel({ cacheData, cacheKey, url: config.url })return config}return config})$axios.interceptors.response.use(response => {if (matchCacheCondition(response.config)) {cache.set(createCacheKey(response.config), response)}return response}, (error) => {if (axios.isCancel(error) && matchCacheCondition(response.config)) {// console.log(`当前页面组件asyncData或者fetch函数中被缓存的接口url为:${error.message.url}`)return Promise.resolve(error.message.cacheData)}// 服务端打印api接口请求错误日志if (process.server) {try {const {config: {url},message} = error || {}console.log(`请求url:${url},错误消息:${message}`)} catch(error) {// console.log(error)}}// 服务端,客户端统一reject错误对象,因此页面组件asyncData,fetch函数请求api接口一定要做catch处理return Promise.reject(error)})
}

3、组件缓存

vue官网文档原话:如果 renderer 在组件渲染过程中进行缓存命中,那么它将直接重新使用整个子树的缓存结果。这意味着在以下情况,你不应该缓存组件:

  • 它具有可能依赖于全局状态的子组件。
  • 它具有对渲染上下文产生副作用(side effect)的子组件。
  • 因此,应该小心使用组件缓存来解决性能瓶颈。在大多数情况下,你不应该也不需要缓存单一实例组件。适用于缓存的最常见类型的组件,是在大的 v-for 列表中重复出现的组件。由于这些组件通常由数据库集合(database collection)中的对象驱动,它们可以使用简单的缓存策略:使用其唯一 id,再加上最后更新的时间戳,来生成其缓存键(cache key):
const LRU = require('lru-cache')
module.exports = {render: {bundleRenderer: {cache: LRU({max: 1000, // 缓存队列长度maxAge: 1000 * 60 // 缓存1分钟})}}
}

需要做缓存的 vue 组件, 需增加 name 以及 serverCacheKey 字段,以确定缓存的唯一键值。


export default {name: 'zzZyHome',props: ['type'],serverCacheKey: props => props.type + '::' + props.item.last_updated
}

4、页面组件asyncData函数优化

举一个简单的例子进行优化

{async asyncData({ $axios }) {// 1、增加catch处理,是为了让服务端,客户端运行时不报错,特别是防止服务端运行时不报错,不然页面就挂了// 2、catch函数返回一个resolve空字面量对象的Promise,表明dataPromise1的状态未来始终是resolved状态const dataPromise1 = $axios.get('/api/data1').catch(() => Promise.resolve({}))const dataPromise2 = $axios.get('/api/data2').catch(() => Promise.resolve({}))const dataPromise3 = $axios.get('/api/data3').catch(() => Promise.resolve({}))const dataPromise4 = $axios.get('/api/data4').catch(() => Promise.resolve({}))const dataPromise5 = $axios.get('/api/data5').catch(() => Promise.resolve({}))const dataPromise6 = $axios.get('/api/data6').catch(() => Promise.resolve({}))const dataPromise7 = $axios.get('/api/data7').catch(() => Promise.resolve({}))const dataPromise8 = $axios.get('/api/data8').catch(() => Promise.resolve({}))// 保证apiData有数据const apiData = await new Promise(resolve => {Promise.all([dataPromise1, dataPromise2, dataPromise3, dataPromise4,dataPromise5, dataPromise6, dataPromise7, dataPromise8,]).then(dataGather => {resolve({data1: dataGather[0],data2: dataGather[1],data3: dataGather[2],data4: dataGather[3],data5: dataGather[4],data6: dataGather[5],data7: dataGather[6],data8: dataGather[7],})})})return apiData}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/93163.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式之抽象工厂模式--创建一系列相关对象的艺术(简单工厂、工厂方法、到抽象工厂的进化过程,类图NS图)

目录 概述概念适用场景结构类图 衍化过程业务需求基本的数据访问程序工厂方法实现数据访问程序抽象工厂实现数据访问程序简单工厂改进抽象工厂使用反射抽象工厂反射配置文件衍化过程总结 常见问题总结 概述 概念 抽象工厂模式是一种创建型设计模式,它提供了一种将相…

react create-react-app v5 从零搭建(使用 npm run eject)

前言: 好久没用 create-react-app做项目了,这次为了个h5项目,就几个页面,决定自己搭建一个(ps:mmp 好久没用,搭建的时候遇到一堆问题)。 我之前都是使用 umi 。后台管理系统的项目 使用 antd-…

【算法题】 605. 种花问题

题目: 假设有一个很长的花坛,一部分地块种植了花,另一部分却没有。可是,花不能种植在相邻的地块上,它们会争夺水源,两者都会死去。 给你一个整数数组 flowerbed 表示花坛,由若干 0 和 1 组成&…

常见编写JavaScript代码时容易出现的错误(4)

目录 前言1.未考虑性能问题(Ignoring Performance Concerns)错误示例解决方法 2.未处理依赖关系(Unmanaged Dependencies)错误示例解决方法 3.混淆的命名(Confusing Naming)错误示例解决方法 4.忽略跨浏览器…

PY32F003F18之RTC

一、RTC振荡器 PY32F003F18实时时钟的振荡器是内部RC振荡器,频率为32.768KHz。它也可以使用HSE时钟,不建议使用。HAL库提到LSE振荡器,但PY32F003F18实际上没有这个振荡器。 缺点:CPU掉电后,需要重新配置RTC&#xff…

保姆级 -- Zookeeper超详解

1. Zookeeper 是什么(了解) Zookeeper 是一个 分布式协调服务 的开源框架, 主要用来解决分布式集群中应用系统的一致性问题, 例如怎样避免同时操作同一数据造成脏读的问题. ZooKeeper 本质上是 一个分布式的小文件存储系统 . 提供基于类似于文件系统的目录树方式的数据存储, …

第二十届北京消防展即将开启,汉威科技即将精彩亮相

10月10日~13日,第二十届中国国际消防设备技术交流展览会,将在北京市顺义区中国国际展览中心新馆隆重举行。该展会由中国消防协会举办,是世界三大消防品牌展会之一,本届主题为“助力产业发展,服务消防救援”。届时将有4…

【Java 进阶篇】JDBC(Java Database Connectivity)详解

JDBC(Java Database Connectivity)是 Java 中用于连接和操作数据库的标准 API。它允许 Java 应用程序与不同类型的数据库进行交互,执行查询、插入、更新和删除等操作。本文将详细介绍 JDBC 的各个类及其用法,以帮助您更好地理解和…

【C语言经典100例题-66】(用指针解决)输入3个数a,b,c,按大小顺序输出。

代码&#xff1a; #include<stdio.h> #define _CRT_SECURE_NO_WARNINGS 1//VS编译器使用scanf函数时会报错&#xff0c;所以添加宏定义 swap(p1, p2) int* p1, * p2; {int p;p *p1;*p1 *p2;*p2 p; } int main() {int n1, n2, n3;int* pointer1, * pointer2, * point…

力扣 -- 416. 分割等和子集(01背包问题)

解题步骤&#xff1a; 参考代码&#xff1a; 未优化代码&#xff1a; class Solution { public:bool canPartition(vector<int>& nums) {int nnums.size();int sum0;for(const auto& e:nums){sume;}if(sum%21){return false;}int aimsum/2;//多开一行&#xff…

Linux系统编程基础:进程控制

文章目录 一.子进程的创建操作系统内核视角下的父子进程存在形式验证子进程对父进程数据的写时拷贝 二.进程等待进程非阻塞等待示例: 三.进程替换内核视角下的进程替换过程:综合利用进程控制系统接口实现简单的shell进程 进程控制主要分为三个方面,分别是:子进程的创建,进程等待…

private static final long serialVersionUID = 1L的作用是什么?

1.作用是什么&#xff1f; 当一个类被序列化后&#xff0c;存储在文件或通过网络传输时&#xff0c;这些序列化数据会包含该类的结构信息。当反序列化操作发生时&#xff0c;Java虚拟机会根据序列化数据中的结构信息来还原对象。 但是&#xff0c;如果在序列化之后&#xff0c…

前端两年半,CSDN创作一周年

文章目录 一、机缘巧合1.1、起因1.2、万事开头难1.3、 何以坚持&#xff1f; 二、收获三、日常四、憧憬 五、总结 一、机缘巧合 1.1、起因 最开始接触CSDN&#xff0c;还是因为同专业的同学&#xff0c;将计算机实验课的实验题&#xff0c;记录总结并发在了专业群里。后来正式…

几个推荐程序员养成的好习惯

本文框架 前言case1 不想当然case2 不为了解决问题而解决问题case3 不留问题死角case4 重视测试环节 前言 中秋国庆双节至&#xff0c;旅行or回乡探亲基本是大家的选择&#xff0c;看看风景或陪陪家人确实是个难得的机会。不过我的这次假期选择了闭关&#xff0c;不探亲&#…

【Python基础】常用模块学习:sys|os|pytest

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…

c语言小课设--通讯录(动态内存管理)

前言&#xff1a; 在没学动态内存管理之前&#xff0c;我们用的结构体&#xff0c;数组等都是静态分配内存的&#xff0c;也就是说数组的长度是固定的&#xff0c;但是这并不满足我们的实际需求&#xff0c;所以在通讯录项目里面我就用到了动态内存分布。简单来说&#xff0c;…

第3章-指标体系与数据可视化-3.1.1-Matplotlib绘图库

目录 3.1 Python可视化 3.1.1 Matplotlib绘图库 1. 线图 2. 饼图 3. 条形图 4. 直方图 5.散点图

【最多提取子串数目】python实现-附ChatGPT解析

1.题目 最多提取子串数目 知识点字符串统计Q 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 给定由[a-z] 26个英文小写字母组成的字符串A和B,其中A中可能存在重复字母,B中不会存在重复字母 现从字符串A中按规则挑选一些字母,可以组成字符串B 挑选规则如下: 同一个位置…

Python|OpenCV-如何给目标图像添加边框(7)

前言 本文是该专栏的第7篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 在使用opencv处理图像的时候,会不可避免的对图像的一些具体区域进行一些操作。比如说,想要给目标图像创建一个围绕图像的边框。简单的来说,就是在图片的周围再填充一个粗线框。具体效果,…

k8s-实战——kubeadm二进制编译

文章目录 源码编译获取源码修改证书有效期修改 CA 有效期为 100 年(默认为 10 年)修改证书有效期为 100 年(默认为 1 年)CentOS7.9环境准备centos脚本安装执行脚本脚本内容手动安装验证编译查看编译后的版本信息参考链接脚本修改源码编译 源码编译kubeadm文件、修改证书的默…