当前位置: 首页 > news >正文

Vue、uniApp、微信小程序、Html5等实现数缓存

此文章带你实现前端缓存,利用时间戳封装一个类似于Redis可以添加过期时间的缓存工具
不仅可以实现对缓存数据设置过期时间,还可以自定义是否需要对缓存数据进行加密处理

工具介绍说明

对缓存数据进行非对称加密处理
在这里插入图片描述
对必要数据进行缓存,并设置过期时间,并设置是否需要对缓存数据进行加密处理
在这里插入图片描述
其他工具
在这里插入图片描述

1、过期时间:封装好的工具,可以添加数据的缓存时长(过期时间)
2、是否加密:可以根据需求,是否需要对缓存的数据进行加密处理
3、加密解密:只需要简单配置,就可以实现缓存数据的加密解密功能
4、长期有效:对于不需要过期的数据,只要不手动清理可以长期有效
5、扩展性强:可以根据自己的需求直接进行代码的调整,简单易懂易扩展

本地缓存

1、VueHtml5 的数据缓存可以存放到localStoragecookie 或者 sessionStorage
2、uniApp、和 微信小程序 的数据缓存可以使用自带的API uni.setStorageSync

缓存的缺点

1、无论localStoragecookiesessionStorage 还是 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等实现数缓存

此文章带你实现前端缓存&#xff0c;利用时间戳封装一个类似于Redis可以添加过期时间的缓存工具 不仅可以实现对缓存数据设置过期时间&#xff0c;还可以自定义是否需要对缓存数据进行加密处理 工具介绍说明 对缓存数据进行非对称加密处理 对必要数据进行缓存&#xff0c;并…...

如何将ArcGIS工程文件迁移到ArcGIS Pro内

当你刚接触ArcGIS Pro的时候&#xff0c;尝试新建一个工程文件会发现工程文件的后缀已经改变&#xff0c;那么以前在ArcGIS内辛苦制作的工程文件是否就不能在ArcGIS Pro内使用了&#xff0c;答案是否定的&#xff0c;对此Esri也给出了解决方案&#xff0c;这里为大家介绍一下迁…...

Jenkins基础篇--添加用户和用户权限设置

添加用户 点击系统管理&#xff0c;点击管理用户&#xff0c;然后点击创建用户&#xff08;Create User&#xff09; 用户权限管理 点击系统管理&#xff0c;点击全局安全配置&#xff0c;找到授权策略&#xff0c;选择安全矩阵&#xff0c;配置好用户权限后&#xff0c;点击…...

C语言基础内容(七)——第08章_C语言常用函数

文章目录 第08章_C语言常用函数本章专题脉络1、字符串相关函数1.1 字符串的表示方式1.2 两种方式的区别1.2 字符串常用函数strlen()strcpy()strncpy()strcat()strncat()strcmp()strlwr()/strupr()1.3 基本数据类型和字符串的转换基本数据类型 -> 字符串字符串 -> 基本数据…...

CRM系统针对销售管理有哪些功能?如何帮助销售效率增长?

从长远来看&#xff0c;有效的CRM管理系统可以帮助您的企业达到甚至超过收入目标。现代大多数企业都依靠CRM系统来管理其销售周期并增加收入。但是&#xff0c;当大多数人提到CRM时&#xff0c;他们指的是使能够改善业务关系并轻松管理不断团队的软件或工具。合格的CRM系统能够…...

基于Pixhawk和ROS搭建自主无人车(一):底盘控制篇

参考 ArduPilot Development超维空间科技乐迪MiniPix车船使用说明书 1. 硬件篇 1.1 底盘构成一览 1.2 底盘接线示意 2. 软件篇 2.1 APM 固件下载 pixhawk 是硬件平台&#xff0c;PX4 是 pixhawk 的原生固件&#xff0c;APM&#xff08;Ardupilot Mega&#xff09;是硬件平台…...

部署 Spring Boot 应用中文文档

本文为官方文档直译版本。原文链接 部署 Spring Boot 应用中文文档 引言部署到云Cloud Foundry与服务绑定 KubernetesKubernetes 容器生命周期 HerokuOpenShift亚马逊网络服务&#xff08;AWS&#xff09;AWS Elastic Beanstalk使用 Tomcat 平台使用 Java SE 平台 总结 CloudCa…...

【数据库原理】(23)实际应用中的查询优化方法

一.基于索引的优化 索引是数据库查询优化的关键工具之一。合理地使用索引可以显著提高查询速度&#xff0c;降低全表扫描的成本。以下是建立和使用索引的一些基本原则和最佳实践。 索引的建立与使用原则 数据量规模与查询频率: 值得建立索引的表通常具有较多的记录&#xff0…...

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) 网站吗&#xff1f;最好的学习管理系统工具LifterLMS将帮助您制作像Udemy市场这样的 LMS 网站。 目录 Udemy市场是什么&#xff1f; 创建 Udemy 克隆所需的几项强制性技术&#xff1a; 步骤 1) 注册您的域名 步骤 2) 获取虚拟主…...

(leetcode)Z字形变换 -- 模拟算法

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 题目链接 . - 力扣&#xff08;LeetCode&#xff09; 输入描述 string convert(string s, int numRows)&#xff0c;输入一个字符串s&#xff0c;以及一个行数numRows&#xff0c;将字符串按照这个行数进行Z字形排列&…...

STM32--基于STM32F103的MAX30102心率血氧测量

本文介绍基于STM32F103ZET6MAX30102心率血氧测量0.96寸OLED&#xff08;7针&#xff09;显示&#xff08;完整程序代码见文末链接&#xff09; 一、简介 MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块。它集成了一个红光LED和一个红外光LED、光电检测器、光器…...

Qt/C++音视频开发63-设置视频旋转角度/支持0-90-180-270度旋转/自定义旋转角度

一、前言 设置旋转角度,相对来说是一个比较小众的需求,如果视频本身带了旋转角度,则解码播放的时候本身就会旋转到对应的角度显示,比如手机上拍摄的视频一般是旋转了90度的,如果该视频文件放到电脑上打开,一些早期的播放器可能播放的时候是躺着的,因为早期播放器设计的…...

Python电能质量扰动信号分类(五)基于CNN-Transformer的一维信号分类模型

目录 往期精彩内容&#xff1a; 引言 1 数据集制作与加载 1.1 导入数据 1.2 制作数据集 2 CNN-Transformer分类模型和超参数选取 2.1定义CNN-Transformer分类模型 2.2 设置参数&#xff0c;训练模型 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联合技术栈的操控下&#xff0c;本专栏提供行之有效的源代码示例和信息点介绍&#xff0c;做到灵活运用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安装、引用&#xff0c;模板使…...

mysql 字符串分割

目录 前言substring_indexsubstring_index 特性字符串分割 前言 略 substring_index 正向截取字符串 mysql> select substring_index(www.baidu.com,.,1); ---------------------------------------- | substring_index(www.baidu.com,.,1) | -------------------------…...

解决Windows11 “我们无法设置移动热点”

目录 问题复现解决办法①启动网络适配器②打开移动热点③共享网络连接④连接移动热点总结 问题复现 因为交换机上网口限制&#xff0c;开发环境暂时没有WIFI设备&#xff0c;只有一根网线和一台笔记本电脑。于是开启笔记本电脑的WiFi共享服务。结果提示 “我们无法设置移动热点…...

python tcp socket中实现SSL/TLS认证

SSL/TLS介绍 官话说SSL是安全套接层(secure sockets layer)&#xff0c;TLS是SSL的继任者&#xff0c;叫传输层安全(transport layer security)。 说白点&#xff0c;就是在明文的上层和TCP层之间加上一层加密&#xff0c;这样就保证上层信息传输的安全。如HTTP协议是明文传输…...

SQL-修改表操作

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…...

后进先出(LIFO)详解

LIFO 是 Last In, First Out 的缩写&#xff0c;中文译为后进先出。这是一种数据结构的工作原则&#xff0c;类似于一摞盘子或一叠书本&#xff1a; 最后放进去的元素最先出来 -想象往筒状容器里放盘子&#xff1a; &#xff08;1&#xff09;你放进的最后一个盘子&#xff08…...

黑马Mybatis

Mybatis 表现层&#xff1a;页面展示 业务层&#xff1a;逻辑处理 持久层&#xff1a;持久数据化保存 在这里插入图片描述 Mybatis快速入门 ![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/6501c2109c4442118ceb6014725e48e4.png //logback.xml <?xml ver…...

Qt Http Server模块功能及架构

Qt Http Server 是 Qt 6.0 中引入的一个新模块&#xff0c;它提供了一个轻量级的 HTTP 服务器实现&#xff0c;主要用于构建基于 HTTP 的应用程序和服务。 功能介绍&#xff1a; 主要功能 HTTP服务器功能&#xff1a; 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

MySQL用户和授权

开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务&#xff1a; test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

python执行测试用例,allure报乱码且未成功生成报告

allure执行测试用例时显示乱码&#xff1a;‘allure’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&#xfffd;ⲿ&#xfffd;&#xfffd;&#xfffd;Ҳ&#xfffd;&#xfffd;&#xfffd;ǿ&#xfffd;&am…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

MacOS下Homebrew国内镜像加速指南(2025最新国内镜像加速)

macos brew国内镜像加速方法 brew install 加速formula.jws.json下载慢加速 &#x1f37a; 最新版brew安装慢到怀疑人生&#xff1f;别怕&#xff0c;教你轻松起飞&#xff01; 最近Homebrew更新至最新版&#xff0c;每次执行 brew 命令时都会自动从官方地址 https://formulae.…...