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

前端性能优化全攻略:JavaScript 优化、DOM 操作、内存管理、资源压缩与合并、构建工具及性能监控

1 为什么需要性能优化?

1.1 性能优化的核心价值:用户体验与业务指标

        性能优化不仅是技术层面的追求,更是直接影响用户体验和业务成败的关键因素。

  • 用户体验(UX)
    • 响应速度:用户期望页面加载时间不超过 3 秒(根据 Google 研究,53% 的移动用户会因加载超过 3 秒而放弃访问)。
    • 流畅性:卡顿、延迟会显著降低用户满意度,例如动画帧率低于 60FPS 时,人眼可感知到不流畅。
    • 交互反馈:即时响应用户操作(如点击、输入)能增强信任感。
  • 业务指标
    • 转化率:性能每提升 1 秒,转化率可能提高 7%(Akamai 数据)。
    • 留存率:加载时间过长会导致用户流失,尤其在移动端。
    • SEO 排名:Google 将页面速度纳入搜索排名算法,性能差可能直接影响流量。

        案例:某电商网站通过优化首屏加载时间从 5 秒降至 1.5 秒,转化率提升 12%。

1.2 JavaScript 性能瓶颈的常见场景

        JavaScript 作为前端交互的核心语言,其性能问题可能出现在多个环节:

  • 代码执行效率低
    • 频繁操作 DOM(如循环中直接修改 innerHTML)。
    • 复杂算法或递归调用导致主线程阻塞。
  • 内存泄漏
    • 闭包、定时器、事件监听未清理,导致内存占用持续增长。
    • 全局变量意外持久化,无法被垃圾回收。
  • 异步处理不当
    • 回调地狱导致代码难以维护,且可能引发竞态条件。
    • 未合理使用 Promise 或 async/await,导致任务排队延迟。
  • 资源加载阻塞
    • 未压缩的 JavaScript 文件体积过大,阻塞页面渲染(尤其是首屏)。
    • 第三方脚本(如广告、分析工具)加载缓慢,拖慢整体性能。

典型场景示例

  • 滚动列表时频繁触发重排(Reflow)和重绘(Repaint)。
  • 动画未使用硬件加速(如 transform),导致帧率下降。

1.3 性能优化的基本原则

        性能优化并非盲目追求极致速度,而是需遵循科学方法论:

  • 80/20法则(帕累托法则)
    • 80% 的性能问题可能由 20% 的代码引起。优先通过性能分析工具(如 Chrome DevTools)定位瓶颈。
    • 示例:优化首屏渲染的关键路径,而非全局代码。
  • 渐进增强(Progressive Enhancement)
    • 确保基础功能在低性能环境下可用,再逐步增强体验。
    • 示例:优先加载核心 CSS 和 JS,非关键资源延迟加载。
  • 权衡取舍
    • 性能 vs 可维护性:避免过度优化导致代码难以维护。
    • 示例:使用 WebAssembly 提升计算性能,但需评估开发成本。
  • 持续监控
    • 性能优化是迭代过程,需结合用户反馈和数据分析持续调整。

原则实践

  • 懒加载(Lazy Loading):仅在用户需要时加载资源(如图片、模块)。
  • 代码分割(Code Splitting):通过 Webpack 等工具将代码拆分为更小的块,按需加载。

2 代码层面的优化

2.1 变量与数据类型优化

2.1.1 避免全局变量污染

  • 问题:全局变量会污染全局命名空间,导致命名冲突和难以追踪的错误。
  • 解决方案
    • 使用立即执行函数表达式(IIFE)或模块化(ES6 模块、CommonJS)封装代码。
    • 避免在全局作用域中声明变量。
// 不推荐:全局变量污染
var counter = 0;// 推荐:使用IIFE封装
(function() {let counter = 0;function increment() {counter++;console.log(counter);}increment(); // 输出1
})();

2.1.2 使用 const 和 let 替代 var

  • 原因
    • var 存在变量提升问题,可能导致意外行为。
    • const 和 let 具有块级作用域,更安全且易维护。
  • 最佳实践
    • 优先使用 const(除非需要重新赋值)。
    • 仅在需要可变变量时使用 let。
// 不推荐:使用var
for (var i = 0; i < 3; i++) {setTimeout(() => console.log(i), 1000); // 输出3次3
}// 推荐:使用let
for (let i = 0; i < 3; i++) {setTimeout(() => console.log(i), 1000); // 输出0, 1, 2
}

2.1.3 合理使用数据类型

  • Number vs BigInt
    • 使用 Number 处理常规数值,BigInt 处理超大整数(注意:BigInt 与 Number 不能直接混用)。
  • String 拼接优化
    • 使用模板字符串(Template Literals)替代 + 拼接,提高可读性。
    • 大量拼接时,使用 Array.join() 替代 +=。
// 不推荐:字符串拼接
let str = '';
for (let i = 0; i < 1000; i++) {str += i; // 每次拼接都会创建新字符串
}// 推荐:使用数组join
let parts = [];
for (let i = 0; i < 1000; i++) {parts.push(i);
}
let str = parts.join('');

2.2 作用域与闭包优化

2.2.1 减少作用域链查找深度

  • 问题:深层作用域链查找会降低性能。
  • 解决方案
    • 将频繁访问的变量缓存为局部变量。
// 不推荐:深层作用域链查找
function process(data) {for (let i = 0; i < data.items.length; i++) {console.log(data.items[i]); // 每次循环都会查找data.items}
}// 推荐:缓存局部变量
function process(data) {let items = data.items;for (let i = 0; i < items.length; i++) {console.log(items[i]);}
}

2.2.2 警惕闭包导致的内存泄漏

  • 问题:闭包会引用外部变量,可能导致内存无法释放。
  • 解决方案
    • 在不需要时手动解除引用(如将变量设为 null)。
function createClosure() {let largeData = new Array(1000000).fill('data');return function() {console.log(largeData.length);};
}let closure = createClosure();
// 使用后手动解除引用
closure = null;

2.3 循环与迭代优化

2.3.1 替代 for...in 的高效迭代方式

  • 问题:for...in 会遍历可枚举属性,包括原型链上的属性,性能较低。
  • 解决方案
    • 使用 for...of(适用于数组和可迭代对象)。
    • 使用 Array.prototype.forEach(适用于数组)。
// 不推荐:for...in遍历数组
let arr = [1, 2, 3];
for (let key in arr) {console.log(arr[key]); // 可能遍历到非数字属性
}// 推荐:for...of
for (let value of arr) {console.log(value);
}

2.3.2 缓存数组长度

  • 原因:每次循环都会重新计算数组长度,影响性能。
// 不推荐:未缓存长度
for (let i = 0; i < arr.length; i++) {// ...
}// 推荐:缓存长度
for (let i = 0, len = arr.length; i < len; i++) {// ...
}

2.3.3 倒序循环减少边界检查

  • 原因:倒序循环(i--)在某些引擎中可能优化边界检查。
for (let i = arr.length - 1; i >= 0; i--) {// ...
}

2.4 函数优化

2.4.1 避免内联函数重复定义

  • 问题:内联函数在每次调用时都会重新创建。
  • 解决方案
    • 将函数提取到外部作用域。
// 不推荐:内联函数
arr.forEach(item => {let process = v => v * 2;console.log(process(item));
});// 推荐:提取函数
function process(v) {return v * 2;
}
arr.forEach(item => console.log(process(item)));

2.4.2 使用箭头函数与绑定 this 的优化

  • 原因:箭头函数不会创建自己的 this,适合在回调中使用。
// 不推荐:使用普通函数绑定this
function Counter() {this.count = 0;setTimeout(function() {console.log(this.count); // undefined}, 1000);
}// 推荐:使用箭头函数
function Counter() {this.count = 0;setTimeout(() => {console.log(this.count); // 0}, 1000);
}

2.4.3 递归改迭代(尾递归优化)

  • 问题:递归可能导致栈溢出,且性能较低。
  • 解决方案
    • 使用迭代替代递归。
    • 在支持尾递归优化的环境中,使用尾递归。
// 不推荐:普通递归
function factorial(n) {if (n === 0) return 1;return n * factorial(n - 1);
}// 推荐:迭代替代递归
function factorial(n) {let result = 1;for (let i = 2; i <= n; i++) {result *= i;}return result;
}

3 异步与事件处理优化

3.1 异步编程的最佳实践

3.1.1 使用 Promise 替代回调地狱

  • 问题:回调地狱(Callback Hell)导致代码难以维护,错误处理复杂。
  • 解决方案:使用 Promise 链式调用,简化异步逻辑。
// 不推荐:回调地狱
fetchData(url, (err, data) => {if (err) return console.error(err);processData(data, (err, result) => {if (err) return console.error(err);console.log(result);});
});// 推荐:使用Promise
fetchData(url).then(data => processData(data)).then(result => console.log(result)).catch(err => console.error(err));

3.1.2 async/await 的合理使用与错误处理

  • 优势:async/await 使异步代码更接近同步逻辑,易读性更强。
  • 最佳实践
    • 使用 try/catch 捕获错误。
    • 避免在循环中直接调用 await(可能导致性能下降)。
// 推荐:使用async/await
async function fetchAndProcess() {try {const data = await fetchData(url);const result = await processData(data);console.log(result);} catch (err) {console.error(err);}
}// 注意:避免在循环中直接await
async function processArray(items) {const results = [];for (const item of items) {// 改用Promise.all优化results.push(processItem(item));}return Promise.all(results);
}

3.2 事件监听与委托

3.2.1 事件委托减少绑定数量

  • 问题:为每个子元素绑定事件会导致性能开销和内存泄漏。
  • 解决方案:利用事件冒泡,在父元素上绑定事件,通过 event.target 识别触发源。
// 不推荐:为每个按钮绑定事件
const buttons = document.querySelectorAll('.btn');
buttons.forEach(btn => {btn.addEventListener('click', handleClick);
});// 推荐:事件委托
document.querySelector('.button-container').addEventListener('click', event => {if (event.target.matches('.btn')) {handleClick(event);}
});

3.2.2 防抖(Debounce)与节流(Throttle)的实现

  • 防抖(Debounce):延迟执行函数,直到一段时间内不再触发。
  • 节流(Throttle):限制函数在一定时间内只执行一次。
  • 应用场景
    • 防抖:搜索框输入、窗口调整大小。
    • 节流:滚动事件、鼠标移动。
// 防抖实现
function debounce(func, delay) {let timeout;return function(...args) {clearTimeout(timeout);timeout = setTimeout(() => func.apply(this, args), delay);};
}// 节流实现
function throttle(func, limit) {let lastFunc;let lastRan;return function(...args) {const context = this;if (!lastRan) {func.apply(context, args);lastRan = Date.now();} else {clearTimeout(lastFunc);lastFunc = setTimeout(function() {if ((Date.now() - lastRan) >= limit) {func.apply(context, args);lastRan = Date.now();}}, limit - (Date.now() - lastRan));}};
}// 使用示例
window.addEventListener('resize', debounce(() => console.log('Resized!'), 300));
window.addEventListener('scroll', throttle(() => console.log('Scrolled!'), 100));

3.3 定时器优化

3.3.1 避免 setTimeout 嵌套

  • 问题:setTimeout 嵌套可能导致回调地狱,逻辑混乱。
  • 解决方案:使用 async/await 或 Promise 链式调用替代。
// 不推荐:setTimeout嵌套
setTimeout(() => {console.log('Step 1');setTimeout(() => {console.log('Step 2');setTimeout(() => {console.log('Step 3');}, 1000);}, 1000);
}, 1000);// 推荐:使用Promise链
function delay(ms) {return new Promise(resolve => setTimeout(resolve, ms));
}async function executeSteps() {console.log('Step 1');await delay(1000);console.log('Step 2');await delay(1000);console.log('Step 3');
}
executeSteps();

3.3.2 使用 requestAnimationFrame 优化动画性能

  • 问题:setTimeout 或 setInterval 用于动画可能导致掉帧或性能不佳。
  • 解决方案:使用 requestAnimationFrame,浏览器会在下一次重绘前调用回调函数。
// 不推荐:使用setInterval
let start;
function animate(timestamp) {if (!start) start = timestamp;const progress = timestamp - start;const element = document.querySelector('.box');element.style.transform = `translateX(${Math.min(progress / 10, 200)}px)`;if (progress < 2000) { // 动画持续2秒setTimeout(animate, 16); // 约60FPS}
}
setTimeout(animate, 16);// 推荐:使用requestAnimationFrame
let start;
function animate(timestamp) {if (!start) start = timestamp;const progress = timestamp - start;const element = document.querySelector('.box');element.style.transform = `translateX(${Math.min(progress / 10, 200)}px)`;if (progress < 2000) {requestAnimationFrame(animate);}
}
requestAnimationFrame(animate);

4 DOM 操作优化

4.1 减少 DOM 操作频率

4.1.1 批量修改 DOM(DocumentFragment)

  • 问题:每次修改 DOM 都会触发重绘或重排,频繁操作会导致性能下降。
  • 解决方案:使用 DocumentFragment 或批量操作,减少 DOM 操作的次数。
// 不推荐:逐个添加节点
const list = document.querySelector('#list');
for (let i = 0; i < 100; i++) {const item = document.createElement('div');item.textContent = `Item ${i}`;list.appendChild(item); // 每次操作都会触发重绘/重排
}// 推荐:使用 DocumentFragment
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {const item = document.createElement('div');item.textContent = `Item ${i}`;fragment.appendChild(item);
}
list.appendChild(fragment); // 一次性插入,减少重绘/重排

4.1.2 使用 innerHTML 的注意事项

  • 优势:innerHTML 可以一次性插入大量 HTML,性能较高。
  • 注意事项
    • 避免插入用户生成的内容,防止 XSS 攻击。
    • 插入复杂 HTML 时,浏览器需要解析字符串,可能影响性能。
// 使用 innerHTML 插入 HTML
const container = document.querySelector('#container');
const htmlString = '<div>Item 1</div><div>Item 2</div>';
container.innerHTML = htmlString;// 安全处理用户输入
function sanitizeInput(input) {const div = document.createElement('div');div.textContent = input; // 转义特殊字符return div.innerHTML;
}
const userInput = '<script>alert("XSS")</script>';
container.innerHTML = `<div>${sanitizeInput(userInput)}</div>`;

4.2 CSS 选择器优化

4.2.1 避免复杂选择器(如通配符 *、深层嵌套)

  • 问题:复杂选择器会增加浏览器解析和匹配的时间。
  • 解决方案
    • 尽量避免使用通配符 * 和深层嵌套选择器。
    • 使用更具体的选择器,减少匹配范围。
/* 不推荐:复杂选择器 */
div ul li a {color: red;
}/* 推荐:更具体的选择器 */
.nav-link {color: red;
}

4.2.2 缓存 DOM 查询结果

  • 问题:重复查询 DOM 会导致性能开销。
  • 解决方案:将频繁使用的 DOM 节点缓存到变量中。
// 不推荐:重复查询
function updateText() {document.querySelector('#myDiv').textContent = 'Updated';document.querySelector('#myDiv').style.color = 'blue';
}// 推荐:缓存查询结果
function updateText() {const myDiv = document.querySelector('#myDiv');myDiv.textContent = 'Updated';myDiv.style.color = 'blue';
}

4.3 虚拟 DOM 与框架优化

4.3.1 React/Vue 中的 key 优化

  • 问题:在列表渲染中,缺少 key 或使用不稳定的 key 会导致组件重渲染或性能下降。
  • 解决方案
    • 使用唯一且稳定的 key,如 ID。
    • 避免使用数组索引作为 key。
// 不推荐:使用索引作为 key
{items.map((item, index) => (<div key={index}>{item.name}</div>
))}// 推荐:使用唯一 ID 作为 key
{items.map(item => (<div key={item.id}>{item.name}</div>
))}

4.3.2 避免不必要的组件重渲染

  • 问题:组件的 props 或 state 变化会触发重渲染,可能导致性能问题。
  • 解决方案
    • 使用 React.memo(React)或 vue 的 computed 属性(Vue)优化渲染。
    • 确保 props 和 state 的变化是必要的。
// 不推荐:每次父组件渲染都会重渲染子组件
function Child({ value }) {console.log('Rendering...');return <div>{value}</div>;
}// 推荐:使用 React.memo 避免不必要的重渲染
const Child = React.memo(({ value }) => {console.log('Rendering...');return <div>{value}</div>;
});

5 内存管理与垃圾回收

5.1 内存泄漏的常见场景

5.1.1 闭包、定时器、事件监听未清理

  • 问题:闭包、定时器、事件监听器等会持有对外部变量的引用,如果未正确清理,会导致内存泄漏。
  • 解决方案
    • 在不需要时手动清理定时器、事件监听器。
    • 避免不必要的闭包引用。
// 不推荐:定时器未清理
function startTimer() {const id = setInterval(() => {console.log('Running');}, 1000);// 缺少 clearInterval(id)
}// 推荐:清理定时器
let id;
function startTimer() {id = setInterval(() => {console.log('Running');}, 1000);
}
function stopTimer() {clearInterval(id);
}// 不推荐:事件监听器未清理
function setupEventListener() {window.addEventListener('resize', handleResize);// 缺少移除监听器
}// 推荐:清理事件监听器
function setupEventListener() {function handleResize() {console.log('Resized');}window.addEventListener('resize', handleResize);return () => window.removeEventListener('resize', handleResize);
}
const removeListener = setupEventListener();
removeListener(); // 清理监听器

5.1.2 全局变量意外持久化

  • 问题:未声明的变量会隐式成为全局变量,导致内存无法释放。
  • 解决方案
    • 使用 let 或 const 声明变量。
    • 避免在全局作用域中定义不必要的变量。
// 不推荐:隐式全局变量
function leakyFunction() {leak = 'This is a leak'; // 未声明,成为全局变量
}// 推荐:使用 let 或 const
function nonLeakyFunction() {const noLeak = 'This is not a leak';
}

5.2 垃圾回收机制

5.2.1 标记清除(Mark-and-Sweep)

  • 原理
    1. 垃圾回收器从根对象(如全局对象)开始,标记所有可达对象。
    2. 清除未标记的对象,释放内存。
  • 优点:能够回收循环引用对象。
  • 缺点:标记清除过程会暂停程序执行(暂停时间取决于堆的大小)。

5.2.2 引用计数(Reference Counting)

  • 原理:每个对象维护一个引用计数,当引用计数为 0 时,对象被回收。
  • 优点:回收及时,不需要暂停程序。
  • 缺点:无法回收循环引用对象。

5.2.3 V8 引擎的垃圾回收

  • V8 引擎使用分代回收策略:
    • 新生代:存活时间短的对象,使用 Scavenge 算法(复制清除)。
    • 老生代:存活时间长的对象,使用 Mark-Sweep 和 Mark-Compact 算法。

5.3 弱引用(WeakMap、WeakSet)的使用

5.3.1 WeakMap

  • 特点
    • 键必须是对象,值可以是任意类型。
    • 不会阻止键被垃圾回收。
  • 使用场景:缓存、私有属性存储。
const weakMap = new WeakMap();
let obj = { name: 'Temp' };
weakMap.set(obj, 'Some value');
console.log(weakMap.get(obj)); // 输出: 'Some value'
obj = null; // 允许垃圾回收

5.3.2 WeakSet

  • 特点
    • 只能存储对象,不能存储值。
    • 不会阻止对象被垃圾回收。
  • 使用场景:存储对象的弱引用集合。
const weakSet = new WeakSet();
let obj = { name: 'Temp' };
weakSet.add(obj);
console.log(weakSet.has(obj)); // 输出: true
obj = null; // 允许垃圾回收

5.4 内存分析工具

5.4.1 Chrome DevTools 内存快照

  • 功能
    • 拍摄堆快照,分析内存使用情况。
    • 检测内存泄漏、对象引用关系。
  • 使用步骤
    1. 打开 Chrome DevTools,进入 "Memory" 面板。
    2. 点击 "Take heap snapshot" 拍摄快照。
    3. 分析快照中的对象,查找未释放的内存。

5.4.2 使用 performance.memory(Node.js 环境)

  • 功能
    • 提供内存使用信息,包括总内存、堆内存等。
if (performance.memory) {console.log('Total JS Heap Size:', performance.memory.totalJSHeapSize);console.log('Used JS Heap Size:', performance.memory.usedJSHeapSize);console.log('JS Heap Size Limit:', performance.memory.jsHeapSizeLimit);
} else {console.log('performance.memory is not supported in this environment.');
}

6 网络请求与资源加载优化

6.1 使用 Webpack/Rollup 进行代码分割

6.1.1 代码分割的意义

  • 问题:单个 JavaScript 文件过大会导致加载时间过长。
  • 解决方案:通过代码分割,将代码拆分为多个小块,按需加载。

6.1.2 Webpack 代码分割

  • 动态导入:使用 import() 实现按需加载。
  • 配置优化:通过 SplitChunksPlugin 自动分割公共代码。
// 动态导入示例
function loadComponent() {import('./component.js').then(module => {const component = module.default;document.body.appendChild(component());});
}// Webpack 配置示例
module.exports = {optimization: {splitChunks: {chunks: 'all', // 自动分割所有模块},},
};

6.1.3 Rollup 代码分割

  • 手动分割:通过 output.manualChunks 配置。
// Rollup 配置示例
export default {input: 'src/main.js',output: {dir: 'dist',format: 'esm',manualChunks(id) {if (id.includes('node_modules')) {return 'vendor'; // 将第三方库打包到 vendor.js}},},
};

6.2 Gzip/Brotli 压缩

6.2.1 Gzip 压缩

  • 原理:通过压缩算法减少文件体积。
server {gzip on;gzip_types text/plain application/javascript text/css;gzip_min_length 1024;
}

6.2.2 Brotli 压缩

  • 优势:比 Gzip 压缩率更高。
server {brotli on;brotli_types text/plain application/javascript text/css;brotli_min_length 1024;
}

6.2.3 压缩效果对比

文件类型原始大小Gzip 压缩后Brotli 压缩后
HTML10 KB2.5 KB2.0 KB
JavaScript100 KB25 KB20 KB
CSS20 KB5 KB4 KB

6.3 懒加载与预加载

6.3.1 懒加载

  • 原理:延迟加载非关键资源,减少初始加载时间。

  • 图片懒加载:使用 loading="lazy" 属性。

<img src="image.jpg" alt="Lazy Loaded Image" loading="lazy">
  • 动态导入懒加载:结合 Webpack 的动态导入。
function loadModule() {import('./module.js').then(module => {module.init();});
}

6.3.2 预加载

  • 原理:提前加载关键资源,提升用户体验。

  • 使用 <link rel="preload">:

<link rel="preload" href="critical.js" as="script">
  • 预加载字体
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin="anonymous">

6.4 图片懒加载(loading="lazy")

6.4.1 优势

  • 减少初始页面加载时间。
  • 节省带宽,提升性能。

6.4.2 浏览器支持

  • 现代浏览器(Chrome、Edge、Firefox 等)已支持 loading="lazy"。
<img src="image.jpg" alt="Image" loading="lazy" onerror="this.onerror=null;this.src='fallback.jpg';">

6.5 预加载关键资源(<link rel="preload">)

6.5.1 使用场景

  • 预加载关键 JavaScript、CSS、字体等资源。
  • 提升首屏渲染速度。

6.5.2 注意事项

  • 避免预加载过多资源,导致带宽浪费。
  • 结合 as 属性指定资源类型:
<link rel="preload" href="styles.css" as="style">
<link rel="preload" href="main.js" as="script">

6.6 Service Worker 与缓存策略

6.6.1 Service Worker 基础

  • 作用:拦截网络请求,实现离线缓存和资源更新。
  • 注册 Service Worker
if ('serviceWorker' in navigator) {navigator.serviceWorker.register('/service-worker.js').then(registration => {console.log('Service Worker registered with scope:', registration.scope);});
}

6.6.2 缓存策略

  • 缓存优先(Cache-First):优先从缓存读取资源。
  • 网络优先(Network-First):优先从网络获取资源,失败时回退到缓存。
  • 示例(Cache-First)
self.addEventListener('fetch', event => {event.respondWith(caches.match(event.request).then(cachedResponse => {return cachedResponse || fetch(event.request);}));
});

6.7 离线缓存与资源更新

6.7.1 离线缓存

  • 实现:通过 Service Worker 缓存关键资源,实现离线访问。
const CACHE_NAME = 'static-cache-v1';
const urlsToCache = ['/index.html', '/styles.css', '/main.js'];self.addEventListener('install', event => {event.waitUntil(caches.open(CACHE_NAME).then(cache => {return cache.addAll(urlsToCache);}));
});

6.7.2 资源更新

  • 问题:缓存资源可能过期,需要更新。
  • 解决方案:使用版本号控制缓存。
const CACHE_NAME = 'static-cache-v2'; // 更新版本号

6.8 缓存失效策略(如版本号控制)

6.8.1 版本号控制

  • 原理:通过修改缓存名称,强制更新缓存。
  • 实现:在 Service Worker 中使用动态版本号。
const VERSION = 'v2'; // 版本号
const CACHE_NAME = `static-cache-${VERSION}`;

6.8.2 哈希值控制

  • 原理:根据文件内容生成哈希值,确保缓存唯一性。
  • 工具:使用 Webpack 的 [hash] 或 [chunkhash]。
output: {filename: '[name].[contenthash].js', // 根据内容生成哈希值
}

相关文章:

前端性能优化全攻略:JavaScript 优化、DOM 操作、内存管理、资源压缩与合并、构建工具及性能监控

1 为什么需要性能优化&#xff1f; 1.1 性能优化的核心价值&#xff1a;用户体验与业务指标 性能优化不仅是技术层面的追求&#xff0c;更是直接影响用户体验和业务成败的关键因素。 用户体验&#xff08;UX&#xff09;&#xff1a; 响应速度&#xff1a;用户期望页面加载时…...

使用 acme.sh 自动更新 SSL 证书的指南

上篇文章讲了一下 如何利用acme.sh来申请ssl&#xff0c;但没有讲3个月到期后 如何续期&#xff0c;续期的时候会碰到什么问题&#xff1f; 1.查看当前的当前签发域名的到期时间 acme.sh list 2.重新申请ssl acme.sh --issue --dns dns_namesilo -d xxx.ai -d *.xxx.ai --dns…...

查看Spring Boot项目所有配置信息的几种方法,包括 Actuator端点、日志输出、代码级获取 等方式,附带详细步骤和示例

以下是查看Spring Boot项目所有配置信息的几种方法&#xff0c;包括 Actuator端点、日志输出、代码级获取 等方式&#xff0c;附带详细步骤和示例&#xff1a; 1. 使用Spring Boot Actuator Actuator是Spring Boot提供的监控和管理工具&#xff0c;包含/configprops端点可查看…...

C++与C

文章目录 C与C命令空间const关键字new/delete表达式引用&#xff08;重点&#xff09;概念引用的本质引用的使用场景引用作为函数的参数引用作为函数的返回值 总结 强制转换函数重载extern "C"默认参数 bool类型inline&#xff08;内联&#xff09;函数异常处理&…...

Nginx​中间件的解析

目录 一、Nginx的核心架构解析 二、Nginx的典型应用场景 三、Nginx的配置优化实践 四、Nginx的常见缺陷与漏洞 一、Nginx的核心架构解析 ​​事件驱动与非阻塞IO模型​​ Nginx采用基于epoll/kq等系统调用的事件驱动机制&#xff0c;通过异步非阻塞方式处理请求&#xff0c;…...

Ansys Zemax | 在 MATLAB 中使用 ZOS-API 的技巧

附件下载 联系工作人员获取附件 本文将介绍一些在MATLAB中使用 ZOS-API 的技巧&#xff0c;以提高您的工作效率并充分利用 ZOS-API 的功能。 简介 OpticStudio开发了应用程序接口 (API) &#xff0c;用户可以使用API与不同的脚本环境进行连接和交互。使用API&#xff0c;用…...

js 生成pdf 并上传文件

js 生成pdf 并上传文件 使用 JsPDF html2Canvas 代码直接使用 注意注释 import JsPDF from jspdf import html2Canvas from html2canvas // 上传文件的方法 import { handleUploadImage } from /utils/uploadQuillEditdownPDF() {// 要打印元素的idconst cloneDom document.…...

刷刷刷刷刷sql题

NSSCTF 【SWPUCTF 2021 新生赛】easy_sql 这题虽然之前做过&#xff0c;但为了学习sql&#xff0c;整理一下就再写一次 打开以后是杰哥的界面 注意到html网页标题的名称是 “参数是wllm” 那就传参数值试一试 首先判断注入类型&#xff08;数字型或字符型&#xff09; 传1 …...

JavaScript 中的 this 及 this 指向的改变方法

在 JavaScript 的世界里&#xff0c;this是一个既强大又容易让人困惑的概念。它的指向在不同的函数调用场景下会动态变化&#xff0c;而call()、apply()和bind()这三个方法则为我们提供了精确控制this指向的能力。本文将从基础概念出发&#xff0c;结合具体案例&#xff0c;带大…...

安卓模拟器绕过检测全解析:雷电、MuMu、蓝叠、逍遥、夜神与WSA完整指南

安卓模拟器绕过检测全解析&#xff1a;雷电、MuMu、蓝叠、逍遥、夜神与WSA完整指南 模拟器过检测合集雷电mumu蓝叠逍遥夜神WSA 转自风车2025 前言 随着手机游戏和应用的普及&#xff0c;越来越多的用户选择在PC上通过模拟器来运行安卓应用。然而&#xff0c;许多应用和游戏为…...

VSCode中安装GitGraph

前提是先安装git&#xff0c;官方下载地址&#xff1a;Git - Downloads 1. 在VSCode中安装GitGraph插件 2. 文件->首选项->设置&#xff0c;打开设置界面&#xff0c;在设置界面搜索git path 3. 打开配置文件配置git安装路径&#xff1a; 4. 打开源代码管理&#xff0c;…...

StartAI「万物迁移」功能设计师实操教程:模特换衣场景应用

一、功能核心优势解析 智能识别与场景融合 基于迁移学习算法&#xff0c;精准定位服装轮廓&#xff08;支持复杂材质如蕾丝、镂空设计&#xff09;&#xff0c;自动匹配目标场景的光影方向与色温。 效率革命 传统PS手动换衣需2-3小时&#xff0c;使用万物迁移可压缩至2-5分…...

【RK3588 嵌入式图形编程】-SDL2-扫雷游戏-放置标记

放置标记 文章目录 放置标记1、概述2、更新Globals.h3、放置标记4、渲染标记5、标记计数6、完整代码7、改进建议8、总结在本文中,我们实现标记放置和跟踪以完成的扫雷游戏项目。 1、概述 在我们扫雷游戏文章系列的最后部分中,我们将添加玩家在可疑的地雷位置放置标记的功能。…...

【Python】Selenium切换网页的标签页的写法(全!!!)

在使用selenium做网站爬取测试的时候&#xff0c;我们经常会遇到一些需要点击的元素&#xff0c;才能点击到我们想要进入的页面&#xff0c; 于是我们就要模拟 不断地 点点点击 鼠标的样子。 这个时候网页上就会有很多的标签页&#xff0c;你的浏览器网页标签栏 be like: 那…...

Spring Boot多环境配置详解

一、为什么需要多环境配置 在实际项目开发中&#xff0c;我们通常需要将应用部署到不同的环境中&#xff0c;比如&#xff1a; 开发环境&#xff08;dev&#xff09; - 开发人员本地开发调试使用测试环境&#xff08;test&#xff09; - 测试人员功能测试使用生产环境&#x…...

进阶篇 第 6 篇:时间序列遇见机器学习与深度学习

进阶篇 第 6 篇&#xff1a;时间序列遇见机器学习与深度学习 (图片来源: Tara Winstead on Pexels) 在上一篇中&#xff0c;我们探讨了如何通过精心的特征工程&#xff0c;将时间序列预测问题转化为机器学习可以处理的监督学习任务。我们学习了如何创建滞后特征、滚动统计特征…...

RHCE 作业二(密钥登录实验)

1.进入ssh主配置文件恢复配置&#xff1a; 2.vim进入ssh子文件夹查看配置 3.重启服务 /etc/ssh/ key结尾或者.pub结尾的文件全部都是密钥 sshd_confg.d目录是服务的子配置文件 ssh_confg.d目录是客户端你的子配置文件 ~/.ssh/ 是当前用户的配置文件 4.服务器和客户端分别…...

android contentProvider 踩坑日记

写此笔记原因 学习《第一行代码》到第8章节实现provider时踩了一些坑&#xff0c;因此记录下来给后来人和自己一个提示&#xff0c;仅此而已。 包含内容 Sqlite数据库CURD内容provider界面provider项目中书籍管理provider实现逻辑用adb shell确认providercontentResolver接收…...

K8s:概念、特点、核心组件与简单应用

一、引言 在当今云计算和容器技术蓬勃发展的时代&#xff0c;Kubernetes&#xff08;简称 K8s&#xff09;已成为容器编排领域的事实标准。它为管理容器化应用提供了高效、可靠的解决方案&#xff0c;极大地简化了应用的部署、扩展和运维过程。无论是小型初创公司还是大型企业…...

基于表面肌电信号sEMG的手势识别——以Ninapro DB1数据集使用CNN网络识别为例

完整代码获取 评论区或者私信留邮箱 接论文辅导&#xff01;中文核心辅导&#xff01;SCI三四区辅导&#xff01; 可接模型改进 任务描述 表面肌电信号( sEMG ) 是一种生物电信号&#xff0c;存在于肌肉神经。 当大脑下达肌肉动作指令&#xff0c;肌肉会产生控制信号&#xff…...

黑盒测试——等价类划分法实验

任务&#xff1a; 设某程序有两个输入&#xff1a;整数x1和整数x2&#xff0c;计算Yf(x1,x2)。x1和x2的取值范围为1< x1<500,1< x2<500。当x1在[1,200) 取值且x2在[1,300] 取值时&#xff0c;Yf(x1,x2) x1x2&#xff1b;当x1在[200,500] 取值且x2在[1,300] 取值时&…...

深度学习4月22笔记

1、过拟合与欠拟合 在训练深层神经网络时&#xff0c;由于模型参数较多&#xff0c;在数据量不足时很容易过拟合。而正则化技术主要就是用于防止过拟合&#xff0c;提升模型的泛化能力(对新数据表现良好)和鲁棒性&#xff08;对异常数据表现良好&#xff09;。 1. 概念认知 …...

【MySQL数据库入门到精通-03 数据类型及案列】

文章目录 一、三类数据类型二、数值类型三、字符串类型四、日期时间类型五、日期时间类型 一、三类数据类型 MySQL中的数据类型有很多&#xff0c;主要分为三类&#xff1a;数值类型、字符串类型、日期时间类型。 二、数值类型 比如&#xff1a; 1). 年龄字段 – 不会出现负数…...

【机器学习】决策树算法中的 “黄金指标”:基尼系数深度剖析

一、基尼系数的基本概念 基尼系数&#xff08;Gini Impurity&#xff09;在决策树分类算法中&#xff0c;是用于衡量数据纯度的重要指标&#xff0c;与信息熵有着相似的功能。在样本集合里&#xff0c;基尼系数代表随机选取一个样本时&#xff0c;该样本被分错的概率 。假设一…...

植被参数遥感反演技术革命!AI+Python支持向量机/随机森林/神经网络/CNN/LSTM/迁移学习在植被参数反演中的实战应用与优化

在全球气候变化与生态环境监测的重要需求下&#xff0c;植被参数遥感反演作为定量评估植被生理状态、结构特征及生态功能的核心技术&#xff0c;正面临诸多挑战。随着遥感技术的发展&#xff0c;数据复杂度不断提升&#xff0c;模型精度的要求也越来越高。同时&#xff0c;多源…...

【AI】SpringAI 第四弹:接入本地大模型 Ollama

Ollama 是一个开源的大型语言模型服务工具。它的主要作用是帮助用户快速在本地运行大模型&#xff0c; 简化了在 Docker 容器内部署和管理大语言模型&#xff08;LLM&#xff09;的过程。 1. 确保Ollama 已经启动 # 查看帮助文档 ollama -h# 自动下载并启动 ollama run deeps…...

C# MP3 伴奏

使用建议&#xff1a; 参数调节指南&#xff1a; 低频人声残留&#xff1a;降低CenterFrequency(800-1500Hz) 高频人声残留&#xff1a;提高CenterFrequency(2500-3500Hz) 消除力度不足&#xff1a;提高EliminationStrength(0.9-1.0) 伴奏失真&#xff1a;降低EliminationSt…...

【springsecurity oauth2授权中心】将硬编码的参数提出来放到 application.yml 里 P3

在application.yml里添加配置 application.yml oauth2:client:id: clientsecret: secretauthentication-method: client_secret_basicgrant-types: authorization_code,refresh_tokenredirect-uris:- http://localhost:8081/login/oauth2/code/client- http://localhost:8081…...

【Ansible】批量管理 Windows自动化运维

一&#xff0c;前期准备 1&#xff0c;控制端&#xff08;Linux&#xff09;的要求 Ansible可以在安装了Python 2&#xff08;2.7版&#xff09;或Python 3&#xff08;3.5及更高版本&#xff09;的任何机器上运行。控制端计算机不支持Windows。 2&#xff0c;客户端&#x…...

AES-128、AES-192、AES-256 简介

AES&#xff08;Advanced Encryption Standard&#xff09; 是一种广泛使用的对称加密算法&#xff0c;由美国国家标准与技术研究院&#xff08;NIST&#xff09;于2001年正式采纳&#xff0c;用于替代旧的 DES 和 3DES。AES 基于 Rijndael 算法&#xff0c;支持 128 位、192 位…...

osxcross 搭建 macOS 交叉编译环境

1. osxcross 搭建 macOS 交叉编译环境 1. osxcross 搭建 macOS 交叉编译环境 1.1. 安装依赖1.2. 安装 osxcross 及 macOS SDK 1.2.1. 可能错误 1.3. 编译 cmake 类工程1.4. 编译 configure 类工程1.5. 单文件编译及其他环境编译1.6. 打包成 docker 镜像1.7. 使用 docker 编译 …...

联通余额查询接口

接口名称 1) 请求地址 https://ucbss.10010.cn/npfweb/NpfWeb/Mustpayment/getMustpayment?number13112345586&province051&commonBean.phoneNo13112345586&channelType101https://ucbss.10010.cn/npfweb/NpfWeb/Mustpayment/getMustpayment?number13112345586&…...

Python 设计模式:桥接模式

1. 什么是桥接模式&#xff1f; 桥接模式是一种结构型设计模式&#xff0c;它通过将抽象部分与其实现部分分离&#xff0c;使得两者可以独立变化。桥接模式的核心思想是将抽象和实现解耦&#xff0c;从而提高系统的灵活性和可扩展性。 桥接模式的核心思想是将一个类的接口与其…...

7.6 GitHub Sentinel后端API实战:FastAPI高效集成与性能优化全解析

GitHub Sentinel Agent 用户界面设计与实现:后端 API 集成 关键词:前后端分离架构、RESTful API 设计、数据序列化、命令行工具开发、集成测试 后端 API 集成关键技术实现 本阶段需要完成前端界面与后端服务的无缝对接,实现以下核心功能: #mermaid-svg-FFnzT13beWV52dtx …...

Smart AI:在AI浪潮中崛起的智能NFT生态革命者

技术引领&#xff0c;智能进化&#xff1a;Smart AI强势登场 在全球AI技术浪潮席卷之际&#xff0c;由澳大利亚顶尖技术团队倾力打造的Smart AI平台横空出世&#xff0c;以其革命性的NFT智能进化系统&#xff0c;正在彻底重塑数字资产的未来图景。Smart AI不仅是一个平台&…...

Linux——基于socket编程实现简单的Tcp通信

前言1&#xff1a;想要实现一个简单的Tcp通信不难&#xff0c;对于初学者而言&#xff0c;难点在于使用了大量未曾接触过的函数调用&#xff0c;所以本篇重点在于详解每部分代码中相关函数的功能。 1. 简单认识一下TCP传输 TCP通信协议是面向字节流的、可靠的、有连接的传输&a…...

STL C++详解——priority_queue的使用和模拟实现 堆的使用

priority_queue的使用 std::priority_queue 是 C 标准模板库&#xff08;STL&#xff09;中的一个容器适配器&#xff0c;提供了优先队列的功能。 优先队列&#xff1a;是一种特殊的队列&#xff0c;队列中的每个元素都有与之关联的优先级&#xff0c;优先级高的元素会先出队…...

是否可以使用非被动 S4P 文件进行反嵌?

AEDT 电路去嵌入算法使用假定线性时不变 &#xff08;LTI&#xff09; 行为的转换。如果非被动 S 参数块不是 LTI&#xff0c;则倒数函数将无法按预期工作。...

GAEA的技术优势:分层加密与去中心化数据治理

GAEA采用分层加密架构&#xff0c;将用户数据分为三个层级&#xff1a; 基础层&#xff08;链上数据&#xff09;&#xff1a;用户身份哈希、资源贡献记录等核心数据通过零知识证明&#xff08;ZK-SNARKs&#xff09;进行链上加密&#xff0c;确保不可篡改和匿名性。 情感层…...

使用ZYNQ芯片和LVGL框架实现用户高刷新UI设计系列教程(第九讲)

这一期讲解GUI_guider中的容器控件的使用以及相关函数&#xff0c;容器本质上是具有布局和自动调整大小功能的基本对象 &#xff0c;通常用来装载其他子控件。 打开上一期的项目&#xff0c;在工具栏中选中容器控件拖拽到界面中&#xff0c;具体如图所示&#xff1a; 容器默认…...

SparkStreaming概述

SparkStreaming主要用于流式计算&#xff0c;处理实时数据。 DStream是SparkStreaming中的数据抽象模型&#xff0c;表示随着时间推移收到的数据序列。 SparkStreaming支持多种数据输入源&#xff08;如Kafka、Flume、Twitter、TCP套接字等&#xff09;和数据输出位置&#xf…...

LeetCode---整数反转

整数反转 给你一个 32 位的有符号整数 x &#xff0c;返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 − 1] &#xff0c;就返回 0。 示例 示例 1&#xff1a; 输入&#xff1a;x 123 输出&#xff1a;321 示例 2&#xf…...

Conceptrol: Concept Control of Zero-shot Personalized Image Generation(个性化图像生成)

文章目录 一、论文介绍二、项目部署三、效果展示3.1ipadapter plus sd1.5的效果3.2ipadapter plus sd1.5 plus concept的效果3.3两者结果的比较&#xff1a;原本的ipadapter、加了concept的ipadapter 一、论文介绍 个性化图像生成中的平衡问题&#xff1a;现有的zero-shot adap…...

【Harmony】常用工具类封装

文章目录 一&#xff0c;简介二&#xff0c;网络请求工具类2.1、鸿蒙原生http封装2.2、第三方axios封装(需提前下载依赖) 三、录音笔相关工具类3.1、录音封装(录入)3.2、录音封装(放音/渲染)3.3、文件写入封装(针对录音/放音功能) 四、RDB关系型数据库4.1、relationalStore简答…...

大模型部署到本地就是私有化部署吗?

大模型私有化的定义需要从部署方式和数据/模型控制权两个维度来理解&#xff0c;不能简单地仅以“部署位置”或“数据训练”单一条件判断。以下是具体分析&#xff1a; 1. 大模型私有化的核心定义 根据知识库中的描述&#xff08;[1][2][3][8]&#xff09;&#xff1a; 私有化…...

C语言高频面试题——嵌入式系统中中断服务程序

在嵌入式系统中&#xff0c;中断服务程序&#xff08;ISR&#xff09;的设计需遵循严格的规则以确保系统稳定性和实时性。以下是对这段代码的分析及改进建议&#xff1a; 代码分析 __interrupt double compute_area (double radius) { double area PI * radius * radius; pri…...

JavaFX 实战:从零打造一个功能丰富的英文“刽子手”(Hangman)游戏

大家好&#xff01;今天我们要挑战一个经典的单词猜谜游戏——“刽子手”&#xff08;Hangman&#xff09;&#xff0c;并使用 JavaFX 这个强大的 GUI 工具包来赋予它现代化的交互体验。这个项目不仅有趣&#xff0c;而且是学习和实践 JavaFX 核心概念的绝佳途径&#xff0c;涵…...

第 2.1 节: 机器人仿真环境选择与配置 (Gazebo, MuJoCo, PyBullet)

在真实机器人硬件上进行开发和测试既耗时又存在风险&#xff08;硬件损坏、安全问题&#xff09;。机器人仿真环境提供了一个虚拟的沙盒&#xff0c;让开发者能够在计算机中模拟机器人的物理行为、传感器读数和环境互动&#xff0c;极大地加速了开发、测试和调试过程。特别是对…...

网络开发基础(游戏)之 粘包分包

粘&#xff08;nin&#xff09;包、分包 在网络通信中&#xff0c;TCP协议是面向流的协议&#xff0c;没有消息边界概念&#xff0c;粘包和分包是常见的问题。在某种情况下&#xff08;例如网络环境不稳定&#xff09;就会导致"粘包"和"分包"问题&#xf…...

联邦元学习实现个性化物联网的框架

随着数据安全和隐私保护相关法律法规的出台&#xff0c;需要直接在中央服务器上收集和处理数据的集中式解决方案&#xff0c;对于个性化物联网而言&#xff0c;训练各种特定领域场景的人工智能模型已变得不切实际。基于此&#xff0c;中山大学&#xff0c;南洋理工大学&#xf…...