Vue、uniApp、微信小程序、Html5等实现数缓存
此文章带你实现前端缓存,利用时间戳封装一个类似于Redis可以添加过期时间的缓存工具
不仅可以实现对缓存数据设置过期时间,还可以自定义是否需要对缓存数据进行加密处理
工具介绍说明
对缓存数据进行非对称加密处理

对必要数据进行缓存,并设置过期时间,并设置是否需要对缓存数据进行加密处理

其他工具

1、
过期时间:封装好的工具,可以添加数据的缓存时长(过期时间)
2、是否加密:可以根据需求,是否需要对缓存的数据进行加密处理
3、加密解密:只需要简单配置,就可以实现缓存数据的加密解密功能
4、长期有效:对于不需要过期的数据,只要不手动清理可以长期有效
5、扩展性强:可以根据自己的需求直接进行代码的调整,简单易懂易扩展
本地缓存
1、
Vue、Html5的数据缓存可以存放到localStorage、cookie或者sessionStorage中
2、uniApp、和微信小程序的数据缓存可以使用自带的APIuni.setStorageSync
缓存的缺点
1、无论
localStorage、cookie、sessionStorage还是uni.setStorageSync都没有过期时间的限制
2、不能对数据进行实时的加解密处理,或者加解密扩展性差
3、对于大量旧的缓存数据还要手动处理,不能实时的去除旧数据
简单封装
代码以
uniApp为例,先实现可以添加过期时间的功能
缓存工具类:cacheUtil.js
const expiration = 1000 * 60 * 60 * 24 * 7; // 默认有效期7天
const keyPrefix = 'KEY_'; // 缓存key的统一前缀
import { isEmpty, isNumberStr, trimAllBlank } from './utils.js';/*** 添加缓存* @param {String} key 缓存的key* @param {Object} val 缓存的val* @param {Number} expire 过期时间(单位:秒)* @returns {Boolean} 成功 | 失败*/
export const setCache = (key, val, expire) => {if (isEmpty(key)) return false;expire = expire && isNumberStr(expire) ? Number(trimAllBlank(expire)) : 0;expire = Date.now() + (expire <= 0 ? expiration : (expire * 1000));let param = JSON.stringify({data: val, // 缓存的数据expire: expire // 过期时间});key = keyPrefix + key; // 缓存的keyuni.setStorageSync(key, param); // 同步缓存return true;
}/*** 获取缓存的数据* @param {String} key 缓存key* @returns {Object}*/
export const getCache = (key) => {if (isEmpty(key)) return {};key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = value.expire || '';if (isEmpty(expire) || !isNumberStr(expire)) {return value.data || '';}expire = Number(expire) || 0;if (expire < Date.now()) {removeKeys(key);return {};}}return value.data;
}/*** 获取缓存有效时长,单位:秒* @param {String} key 缓存的key* @param {Number} unit 0:毫秒,1:秒,2:分钟,默认毫秒值* @returns {Number} 有效时长*/
export const getExpire = (key, unit) => {if (isEmpty(key)) return 0;key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = Number(value.expire || 0);if (unit === 1) return expire / 1000;if (unit === 2) return expire / 1000 / 60;return expire;}return 0;
}/*** 给缓存设置过期时间* @param {String} key 缓存数据的key* @param {Number} expire 过期时长(单位:秒)*/
export const setExpire = (key, expire) => {let value = getCache(key) || {};setCache(key, value, expire);
}/*** 删除缓存数据* @param keys 删除缓存,多个key之间使用,隔开* @returns {Number} 删除条数*/
export const removeKeys = (...keys) => {if (isEmpty(keys)) return 0;keys.forEach((key, index) => {key = keyPrefix + key;uni.removeStorageSync(key); // 同步删除});return keys.length;
}/*** 清空所有缓存*/
export const clearAll = () => {uni.clearStorageSync(); // 同步清除
}
数据加解密
我这里使用的使用非对称加密,根据个人项目需求可进行调整
加解密工具类:jsencryptUtil.js
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
const encryptor = new JSEncrypt();
const publicKey = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALg
CXvS1JPTluyIqr3eie4mqkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQ==
-----END PUBLIC KEY-----`;
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALgCXvS1JPTluyIqr3eie4m
qkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQJABNYdbvX2nbC8vxoYBLal
wO9kTFfhLH++O9k6Hiop2k5wjbl7FlOBUr7iEeFxdYeuT0JcNyLVeGphKZAS9HlR
0QIhALWpLQaV/UFD/E3lTtkCd890Hn6noLKCOxMRcwtx22oxAiEA2ublehWoGwA5
F+GL9s7B9AgMOwzX0Z43F4fzCfAwCF8CIGYhR6QVPT2tKDLvAWY14f/q654PReQ2
HVo6DDDaAR5xAiEAgyFMGD0+7aXNjcO8D4Y944yqnvkLk/N+NydmbO6oJ2sCIAzn
bQCPl4yK81yVTdUA0fMKWRZn3vJ2C11AaemSs42C
-----END RSA PRIVATE KEY-----`;/*** 使用公钥进行加密处理* @param {string} txt 明文字符串*/
export function encrypt(txt) {// 设置公钥encryptor.setPublicKey(publicKey); // 对数据进行加密return encryptor.encrypt(txt);
}/*** 使用私钥进行解密处理* @param {string} enc 密文字符串*/
export function decrypt(enc) {// 设置私钥encryptor.setPrivateKey(privateKey);// 对数据进行解密return encryptor.decrypt(enc);
}
完善缓存工具
const expiration = 1000 * 60 * 60 * 24 * 7; // 默认有效期7天
const keyPrefix = 'KEY_'; // 缓存key的统一前缀
import { isEmpty, isNumberStr, trimAllBlank } from './utils.js';
import { encrypt, decrypt } from './jsencryptUtil.js';/*** 添加缓存* @param {String} key 缓存的key* @param {Object} val 缓存的val* @param {Number} expire 过期时间(单位:秒)* @param {Boolean} enc 是否需要加密处理:true | false* @returns {Boolean} 成功 | 失败*/
export const setCache = (key, val, expire, enc) => {if (isEmpty(key)) return false;expire = expire && isNumberStr(expire) ? Number(trimAllBlank(expire)) : 0;expire = Date.now() + (expire <= 0 ? expiration : (expire * 1000));let param = JSON.stringify({data: val, // 缓存的数据expire: expire // 过期时间});if (enc === true) param = encrypt(param );key = keyPrefix + key; // 缓存的keyuni.setStorageSync(key, param); // 同步缓存return true;
}/*** 获取缓存的数据* @param {String} key 缓存key* @param {Boolean} dec 是否需要解密处理:true | false* @returns {Object}*/
export const getCache = (key, dec) => {if (isEmpty(key)) return {};key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (dec === true) value = decrypt(value);if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = value.expire || '';if (isEmpty(expire) || !isNumberStr(expire)) {return value.data || '';}expire = Number(expire) || 0;if (expire < Date.now()) {removeKeys(key);return {};}}return value.data;
}/*** 删除缓存数据* @param keys 删除缓存,多个key之间使用,隔开* @returns {Number} 删除条数*/
export const removeKeys = (...keys) => {if (isEmpty(keys)) return 0;keys.forEach((key, index) => {key = keyPrefix + key;uni.removeStorageSync(key); // 同步删除});return keys.length;
}/*** 清空所有缓存*/
export const clearAll = () => {uni.clearStorageSync(); // 同步清除
}
这样就可以对缓存的数据进行加密处理了
每次都要传入一个参数来确定是否需要加密或者解密处理,太繁琐了
能不能不需要我来传是否需要解密呢?根据加密参数进行判断?答案时可以的
接下来,我们先处理一下加解密工具,对于很长很长的明文数据进行加密时,加密工具会报错
下面对加密工具进行改造,改造完成之后,再来进一步完善缓存工具,不用每次都传入解密标志
加密太长报错
使用
JSEncrypt加密数据时,对于比较长的字符串,加密时会出现异常报错,并提示数据太大
解决方法:需要对加密工具进行一次改造处理
修改加密工具
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
const encryptor = new JSEncrypt();
const publicKey = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALg
CXvS1JPTluyIqr3eie4mqkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQ==
-----END PUBLIC KEY-----`;
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALgCXvS1JPTluyIqr3eie4m
qkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQJABNYdbvX2nbC8vxoYBLal
wO9kTFfhLH++O9k6Hiop2k5wjbl7FlOBUr7iEeFxdYeuT0JcNyLVeGphKZAS9HlR
0QIhALWpLQaV/UFD/E3lTtkCd890Hn6noLKCOxMRcwtx22oxAiEA2ublehWoGwA5
F+GL9s7B9AgMOwzX0Z43F4fzCfAwCF8CIGYhR6QVPT2tKDLvAWY14f/q654PReQ2
HVo6DDDaAR5xAiEAgyFMGD0+7aXNjcO8D4Y944yqnvkLk/N+NydmbO6oJ2sCIAzn
bQCPl4yK81yVTdUA0fMKWRZn3vJ2C11AaemSs42C
-----END RSA PRIVATE KEY-----`;/*** 使用公钥进行加密处理* @param {string} txt 明文字符串*/
export function encrypt(txt) {encryptor.setPublicKey(publicKey); // 设置公钥if (txt.length > 100) {let result = "";while (txt && txt.length > 0) {let dl = txt.slice(0, 100);txt = txt.slice(100);if (!result) {result = result + encryptor.encrypt(dl);} else {result = result + "~" + encryptor.encrypt(dl);}}return result;}return encryptor.encrypt(txt); // 对数据进行加密
}/*** 使用私钥进行解密处理* @param {string} enc 密文字符串*/
export function decrypt(enc) {// 设置私钥encryptor.setPrivateKey(privateKey);if (enc.includes("~")) {let result = "";let arr = enc.split("~");for (let i = 0; i < arr.length; i++) {if (arr[i] && arr[i].length > 0) {result = result + encryptor.decrypt(arr[i]);}}return result;}return encryptor.decrypt(enc);
}
改造缓存工具
const expiration = 1000 * 60 * 60 * 24 * 7; // 默认有效期7天
const keyPrefix = 'KEY_'; // 缓存key的统一前缀
import { isEmpty, isNumberStr, trimAllBlank } from './util.js';
import { encrypt, decrypt } from './jsencryptUtil.js';/*** 添加缓存* @param {String} key 缓存的key* @param {Object} val 缓存的val* @param {Number} expire 过期时间(单位:秒)* @param {Boolean} enc 是否需要加密处理:true | false* @returns {Boolean} 成功 | 失败*/
export const setCache = (key, val, expire, enc) => {if (isEmpty(key)) return false;expire = expire && isNumberStr(expire) ? Number(trimAllBlank(expire)) : 0;expire = Date.now() + (expire <= 0 ? expiration : (expire * 1000));enc = enc === true;let param = JSON.stringify({data: (enc ? encrypt(val) : val), // 缓存的数据encrypt: enc, // 数据加密标志expire: expire // 过期时间});key = keyPrefix + key; // 缓存的keyuni.setStorageSync(key, param); // 同步缓存return true;
}/*** 获取缓存的数据* @param {String} key 缓存key* @returns {Object}*/
export const getCache = (key) => {if (isEmpty(key)) return {};key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = value.expire || '';let dec = value.encrypt == true;if (dec) value.data = decrypt(value.data);if (isEmpty(expire) || !isNumberStr(expire)) {return value.data || '';}expire = Number(expire) || 0;if (expire < Date.now()) {removeKeys(key);return {};}}return value;
}/*** 删除缓存数据* @param keys 删除缓存,多个key之间使用,隔开* @returns {Number} 删除条数*/
export const removeKeys = (...keys) => {if (isEmpty(keys)) return 0;keys.forEach((key, index) => {key = keyPrefix + key;uni.removeStorageSync(key); // 同步删除});return keys.length;
}/*** 清空所有缓存*/
export const clearAll = () => {uni.clearStorageSync(); // 同步清除
}
到此,缓存工具就完成了,只要在缓存时传入是否需要加密的参数就可以了,解密时只需要传入key就可以了,不需要再额外传入解密参数
最终完整代码
util工具
util.js
/*** 判断数据是否为空* @param {*} input 原数据* @return {Boolean}*/
export const isEmpty = (input) => {if (Object.prototype.toString.call(input) === '[object Object]') {let vals = Object.values(input) || [];let num = 0;for (let val of vals) {val = (!val ? '' : JSON.stringify(val)).trim().replace(/\s*/g, "");num = !val ? num + 1 : num;}return vals.length === num;}if (typeof input !== 'string') input = (!input ? '' : JSON.stringify(input)).trim().replace(/\s*/g, "");return !input || input.length === 0 || (['[]', '{}', 'NaN', 'null', 'undefined'].includes(input));
}/*** 去除特殊字符,包括但不限于空格、制表符\t、换行符\r、回车符\n* @param {string} input 原数据* @return {string}*/
export const trimAllBlank = (input) => {input = typeof input === 'string' ? input.trim() : JSON.stringify(input);return (input || '').replace(/\s*/g, "");
}/*** 是否数字* @param {string} str 字符串* @returns {boolean}*/
export const isNumberStr = (str) => {str = isEmpty(str) ? '' : str;return /^[+-]?(0|([1-9]\d*))(\.\d+)?$/g.test(str);
}
加密工具
jsencryptUtil.js
import JSEncrypt from 'jsencrypt/bin/jsencrypt.min';
const publicKey = `-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALg
CXvS1JPTluyIqr3eie4mqkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQ==
-----END PUBLIC KEY-----`;
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJtV6S0G6O9LpOVx/riiZzNk1gAl1ALgCXvS1JPTluyIqr3eie4m
qkVdFROjNpCxvtXkRtisB9PlmU9YOPTt8C8CAwEAAQJABNYdbvX2nbC8vxoYBLal
wO9kTFfhLH++O9k6Hiop2k5wjbl7FlOBUr7iEeFxdYeuT0JcNyLVeGphKZAS9HlR
0QIhALWpLQaV/UFD/E3lTtkCd890Hn6noLKCOxMRcwtx22oxAiEA2ublehWoGwA5
F+GL9s7B9AgMOwzX0Z43F4fzCfAwCF8CIGYhR6QVPT2tKDLvAWY14f/q654PReQ2
HVo6DDDaAR5xAiEAgyFMGD0+7aXNjcO8D4Y944yqnvkLk/N+NydmbO6oJ2sCIAzn
bQCPl4yK81yVTdUA0fMKWRZn3vJ2C11AaemSs42C
-----END RSA PRIVATE KEY-----`;/*** 使用公钥进行加密处理* @param {string} txt 明文字符串*/
export function encrypt(txt) {const encryptor = new JSEncrypt();encryptor.setPublicKey(publicKey); // 设置公钥if (txt.length > 100) {let result = "";while (txt && txt.length > 0) {let dl = txt.slice(0, 100);txt = txt.slice(100);if (!result) {result = result + encryptor.encrypt(dl);} else {result = result + "~" + encryptor.encrypt(dl);}}return result;}return encryptor.encrypt(txt); // 对数据进行加密
}/*** 使用私钥进行解密处理* @param {string} enc 密文字符串*/
export function decrypt(enc) {const encryptor = new JSEncrypt();encryptor.setPrivateKey(privateKey);if (enc.includes("~")) {let result = "";let arr = enc.split("~");for (let i = 0; i < arr.length; i++) {if (arr[i] && arr[i].length > 0) {result = result + encryptor.decrypt(arr[i]);}}return result;}return encryptor.decrypt(enc);
}
缓存工具
cacheUtil.js
const expiration = 1000 * 60 * 60 * 24 * 7; // 默认有效期7天
const keyPrefix = 'KEY_'; // 缓存key的统一前缀
import { isEmpty, isNumberStr, trimAllBlank } from './util.js';
import { encrypt, decrypt } from './jsencryptUtil.js';/*** 添加缓存* @param {String} key 缓存的key* @param {Object} val 缓存的val* @param {Number} expire 过期时间(单位:秒)* @param {Boolean} enc 是否需要加密处理:true | false* @returns {Boolean} 成功 | 失败*/
export const setCache = (key, val, expire, enc) => {if (isEmpty(key)) return false;expire = expire && isNumberStr(expire) ? Number(trimAllBlank(expire)) : 0;expire = Date.now() + (expire <= 0 ? expiration : (expire * 1000));enc = enc === true;let param = JSON.stringify({data: (enc ? encrypt(val) : val), // 缓存的数据encrypt: enc, // 数据加密标志expire: expire // 过期时间});key = keyPrefix + key; // 缓存的keyuni.setStorageSync(key, param); // 同步缓存return true;
}/*** 获取缓存的数据* @param {String} key 缓存key* @returns {Object}*/
export const getCache = (key) => {if (isEmpty(key)) return {};key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = value.expire || '';let dec = value.encrypt == true;if (dec) value.data = decrypt(value.data);if (isEmpty(expire) || !isNumberStr(expire)) {return value.data || '';}expire = Number(expire) || 0;if (expire < Date.now()) {removeKeys(key);return {};}}return value;
}/*** 获取缓存有效时长,单位:秒* @param {String} key 缓存的key* @param {Number} unit ms:毫秒,s:秒,m:分钟,默认毫秒值* @returns {Number} 有效时长*/
export const getExpire = (key, unit) => {if (isEmpty(key)) return 0;key = keyPrefix + key;let value = uni.getStorageSync(key) || '';if (value.includes('data') && value.includes('expire')) {value = JSON.parse(value) || {};let expire = Number(value.expire || 0);unit = unit.toLowerCase();if (unit === "s") return expire / 1000;if (unit === "m") return expire / 1000 / 60;return expire;}return 0;
}/*** 给缓存设置过期时间* @param {String} key 缓存数据的key* @param {Number} expire 过期时长(单位:秒)*/
export const setExpire = (key, expire) => {let value = getCache(key) || {};setCache(key, value, expire);
}/*** 删除缓存数据* @param keys 删除缓存,多个key之间使用,隔开* @returns {Number} 删除条数*/
export const removeKeys = (...keys) => {if (isEmpty(keys)) return 0;keys.forEach((key, index) => {key = keyPrefix + key;uni.removeStorageSync(key); // 同步删除});return keys.length;
}/*** 清空所有缓存*/
export const clearAll = () => {uni.clearStorageSync(); // 同步清除
}
其他语言改造
vue:vue中使用时,需要将uni.xxxStorage(...)替换为localStorage或者sessionStorage
html5:和vue一样,需要将uni.xxxStorage(...)替换为localStorage或者sessionStorage
微信小程序:需要将uni.xxxStorage(...)替换为wx.xxxStorage(...)
localStorage和sessionStorage的区别:
sessionStorage:只在本次会话有效,关闭浏览器数据将被删除
localStorage:如果不手动删除,则长期有效,关闭浏览器数据也不会被清除
相关文章:
Vue、uniApp、微信小程序、Html5等实现数缓存
此文章带你实现前端缓存,利用时间戳封装一个类似于Redis可以添加过期时间的缓存工具 不仅可以实现对缓存数据设置过期时间,还可以自定义是否需要对缓存数据进行加密处理 工具介绍说明 对缓存数据进行非对称加密处理 对必要数据进行缓存,并…...
如何将ArcGIS工程文件迁移到ArcGIS Pro内
当你刚接触ArcGIS Pro的时候,尝试新建一个工程文件会发现工程文件的后缀已经改变,那么以前在ArcGIS内辛苦制作的工程文件是否就不能在ArcGIS Pro内使用了,答案是否定的,对此Esri也给出了解决方案,这里为大家介绍一下迁…...
Jenkins基础篇--添加用户和用户权限设置
添加用户 点击系统管理,点击管理用户,然后点击创建用户(Create User) 用户权限管理 点击系统管理,点击全局安全配置,找到授权策略,选择安全矩阵,配置好用户权限后,点击…...
C语言基础内容(七)——第08章_C语言常用函数
文章目录 第08章_C语言常用函数本章专题脉络1、字符串相关函数1.1 字符串的表示方式1.2 两种方式的区别1.2 字符串常用函数strlen()strcpy()strncpy()strcat()strncat()strcmp()strlwr()/strupr()1.3 基本数据类型和字符串的转换基本数据类型 -> 字符串字符串 -> 基本数据…...
CRM系统针对销售管理有哪些功能?如何帮助销售效率增长?
从长远来看,有效的CRM管理系统可以帮助您的企业达到甚至超过收入目标。现代大多数企业都依靠CRM系统来管理其销售周期并增加收入。但是,当大多数人提到CRM时,他们指的是使能够改善业务关系并轻松管理不断团队的软件或工具。合格的CRM系统能够…...
基于Pixhawk和ROS搭建自主无人车(一):底盘控制篇
参考 ArduPilot Development超维空间科技乐迪MiniPix车船使用说明书 1. 硬件篇 1.1 底盘构成一览 1.2 底盘接线示意 2. 软件篇 2.1 APM 固件下载 pixhawk 是硬件平台,PX4 是 pixhawk 的原生固件,APM(Ardupilot Mega)是硬件平台…...
部署 Spring Boot 应用中文文档
本文为官方文档直译版本。原文链接 部署 Spring Boot 应用中文文档 引言部署到云Cloud Foundry与服务绑定 KubernetesKubernetes 容器生命周期 HerokuOpenShift亚马逊网络服务(AWS)AWS Elastic Beanstalk使用 Tomcat 平台使用 Java SE 平台 总结 CloudCa…...
【数据库原理】(23)实际应用中的查询优化方法
一.基于索引的优化 索引是数据库查询优化的关键工具之一。合理地使用索引可以显著提高查询速度,降低全表扫描的成本。以下是建立和使用索引的一些基本原则和最佳实践。 索引的建立与使用原则 数据量规模与查询频率: 值得建立索引的表通常具有较多的记录࿰…...
MySQL中datetime和timestamp的区别
datetime和timestamp的区别 相同点: 存储格式相同 datetime和timestamp两者的时间格式都是YYYY-MM-DD HH:MM:SS 不同点: 存储范围不同. datetime的范围是1000-01-01到9999-12-31. 而timestamp是从1970-01-01到2038-01-19, 即后者的时间范围很小. 与时区关系. datetime是存储…...
2024年如何使用WordPress构建克隆Udemy市场
您想创建像 Udemy 这样的学习管理 (LMS) 网站吗?最好的学习管理系统工具LifterLMS将帮助您制作像Udemy市场这样的 LMS 网站。 目录 Udemy市场是什么? 创建 Udemy 克隆所需的几项强制性技术: 步骤 1) 注册您的域名 步骤 2) 获取虚拟主…...
(leetcode)Z字形变换 -- 模拟算法
个人主页:Lei宝啊 愿所有美好如期而遇 题目链接 . - 力扣(LeetCode) 输入描述 string convert(string s, int numRows),输入一个字符串s,以及一个行数numRows,将字符串按照这个行数进行Z字形排列&…...
STM32--基于STM32F103的MAX30102心率血氧测量
本文介绍基于STM32F103ZET6MAX30102心率血氧测量0.96寸OLED(7针)显示(完整程序代码见文末链接) 一、简介 MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器…...
Qt/C++音视频开发63-设置视频旋转角度/支持0-90-180-270度旋转/自定义旋转角度
一、前言 设置旋转角度,相对来说是一个比较小众的需求,如果视频本身带了旋转角度,则解码播放的时候本身就会旋转到对应的角度显示,比如手机上拍摄的视频一般是旋转了90度的,如果该视频文件放到电脑上打开,一些早期的播放器可能播放的时候是躺着的,因为早期播放器设计的…...
Python电能质量扰动信号分类(五)基于CNN-Transformer的一维信号分类模型
目录 往期精彩内容: 引言 1 数据集制作与加载 1.1 导入数据 1.2 制作数据集 2 CNN-Transformer分类模型和超参数选取 2.1定义CNN-Transformer分类模型 2.2 设置参数,训练模型 3 模型评估 3.1 准确率、精确率、召回率、F1 Score 3.2 十分类混淆…...
基于Vue组合式API的实用工具集
简介 今天,给大家分享一个很实用的工具库 VueUse,它是基于 Vue Composition Api,也就是组合式API。支持在Vue2和Vue3项目中进行使用,据说是目前世界上Star最高的同类型库之一。 图片 官方地址:https://vueuse.org/ 中文地址:https://www.vueusejs.com/ github:https…...
065:vue中将一维对象数组转换为二维对象数组
第065个 查看专栏目录: VUE ------ element UI 专栏目标 在vue和element UI联合技术栈的操控下,本专栏提供行之有效的源代码示例和信息点介绍,做到灵活运用。 (1)提供vue2的一些基本操作:安装、引用,模板使…...
mysql 字符串分割
目录 前言substring_indexsubstring_index 特性字符串分割 前言 略 substring_index 正向截取字符串 mysql> select substring_index(www.baidu.com,.,1); ---------------------------------------- | substring_index(www.baidu.com,.,1) | -------------------------…...
解决Windows11 “我们无法设置移动热点”
目录 问题复现解决办法①启动网络适配器②打开移动热点③共享网络连接④连接移动热点总结 问题复现 因为交换机上网口限制,开发环境暂时没有WIFI设备,只有一根网线和一台笔记本电脑。于是开启笔记本电脑的WiFi共享服务。结果提示 “我们无法设置移动热点…...
python tcp socket中实现SSL/TLS认证
SSL/TLS介绍 官话说SSL是安全套接层(secure sockets layer),TLS是SSL的继任者,叫传输层安全(transport layer security)。 说白点,就是在明文的上层和TCP层之间加上一层加密,这样就保证上层信息传输的安全。如HTTP协议是明文传输…...
SQL-修改表操作
🎉欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克🍹 ✨博客主页:小小恶斯法克的博客 🎈该系列文章专栏:重拾MySQL 🍹文章作者技术和水平很有限,如果文中出现错误&am…...
PHP和Node.js哪个更爽?
先说结论,rust完胜。 php:laravel,swoole,webman,最开始在苏宁的时候写了几年php,当时觉得php真的是世界上最好的语言,因为当初活在舒适圈里,不愿意跳出来,就好比当初活在…...
在rocky linux 9.5上在线安装 docker
前面是指南,后面是日志 sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo sudo dnf install docker-ce docker-ce-cli containerd.io -y docker version sudo systemctl start docker sudo systemctl status docker …...
【ROS】Nav2源码之nav2_behavior_tree-行为树节点列表
1、行为树节点分类 在 Nav2(Navigation2)的行为树框架中,行为树节点插件按照功能分为 Action(动作节点)、Condition(条件节点)、Control(控制节点) 和 Decorator(装饰节点) 四类。 1.1 动作节点 Action 执行具体的机器人操作或任务,直接与硬件、传感器或外部系统…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合
在汽车智能化的汹涌浪潮中,车辆不再仅仅是传统的交通工具,而是逐步演变为高度智能的移动终端。这一转变的核心支撑,来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒(T-Box)方案:NXP S32K146 与…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现指南针功能
指南针功能是许多位置服务应用的基础功能之一。下面我将详细介绍如何在HarmonyOS 5中使用DevEco Studio实现指南针功能。 1. 开发环境准备 确保已安装DevEco Studio 3.1或更高版本确保项目使用的是HarmonyOS 5.0 SDK在项目的module.json5中配置必要的权限 2. 权限配置 在mo…...
java高级——高阶函数、如何定义一个函数式接口类似stream流的filter
java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用(Math::max) 2 函数接口…...
