当前位置: 首页 > 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', // 根据内容生成哈希值
}

相关文章:

  • 从代码学习深度学习 - 异步计算 PyTorch 版
  • 深入理解无监督学习:探索数据的潜在结构
  • Android Studio调试中的坑二
  • Idea创建项目的搭建方式
  • this._uid:Vue 内部为每个组件实例分配的唯一 ID
  • 数字化转型“变形记”:中钧科技经营帮如何让企业长出“智慧骨骼”
  • Python简介
  • 搜索策略的基本概念
  • 【音视频】AAC-ADTS分析
  • Ubuntu与Linux的关系
  • Redis之Java操作redis
  • 性能比拼: Go vs Java
  • 反素数c++
  • 云原生--基础篇-4--CNCF-1-云原生计算基金会(云原生生态发展和目标)
  • 【文件上传/下载Java+vue3——MQ】
  • vue3新增特性
  • KUKA机器人KR 3 D1200 HM介绍
  • [Android]豆包爱学v4.5.0小学到研究生 题目Ai解析
  • Postman下载安装与使用汉化版教程
  • 桌面我的电脑图标不见了怎么恢复 恢复方法指南
  • 供应商已“陷入困境”,美汽车行业致信特朗普政府
  • 外交部:中方近日派出停火监督组赴缅,监督缅军和果敢同盟军停火
  • 罗马教皇方济各去世,享年88岁
  • 一年一CT,十年进ICU?关于CT检查致癌的真相
  • 田野调查|“心青年”的日常秩序与归属之地
  • 日本乒乓名将丹羽孝希因赌博被禁赛6个月,曾获奥运男团银牌