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

JavaScript 性能优化:调优策略与工具使用

引言

在当今的 Web 开发领域,性能优化已不再是锦上添花,而是产品成功的关键因素。据 Google 研究表明,页面加载时间每增加 3 秒,跳出率将提高 32%。而移动端用户如果页面加载超过 3 秒,有 53% 的用户会放弃访问。性能直接影响用户体验、转化率,这使得性能优化成为我们必备的核心技能。

性能评估指标

在开始优化之前,我们需要建立清晰的性能衡量标准。Google 提出的 Web Vitals 是目前业界公认的性能评估指标体系:

  • TTFB (Time To First Byte): 从用户请求到收到服务器响应第一个字节的时间,反映服务器响应速度和网络状况。理想值应小于 100ms。

  • FCP (First Contentful Paint): 首次内容绘制时间,指浏览器渲染出第一块内容(文本、图片等)的时间点。这是用户首次看到页面有内容的时刻,是感知速度的重要指标。良好的 FCP 值应小于 1.8 秒。

  • LCP (Largest Contentful Paint): 最大内容绘制时间,衡量视窗内最大内容元素(通常是主图或标题文本)完成渲染的时间。这是 Core Web Vitals 的重要指标,良好表现值应在 2.5 秒以内。

  • TTI (Time To Interactive): 页面可交互时间,指页面首次完全可交互的时刻。此时,页面已经显示有用内容,事件处理程序已注册,且界面能在 50ms 内响应用户输入。

  • TBT (Total Blocking Time): 总阻塞时间,衡量 FCP 到 TTI 之间主线程被阻塞的总时长。阻塞时间是指任何超过 50ms 的长任务所阻塞的时间。这个指标直接反映了页面交互流畅度。

  • CLS (Cumulative Layout Shift): 累积布局偏移,量化页面加载过程中视觉元素意外移动的程度。良好的 CLS 值应低于 0.1,表明页面加载过程中元素位置较为稳定。

这些指标构成了 Google Core Web Vitals,直接影响搜索排名和用户体验。在优化工作中,我们应该以这些指标为目标,有针对性地改进应用性能。

Chrome DevTools 性能分析

Chrome DevTools 是前端性能分析的核心工具,掌握它的使用方法对于发现和解决性能问题至关重要。

性能面板(Performance Panel)详解

Performance 面板允许我们录制和分析页面在特定操作期间的性能表现。通过它,我们可以看到 JavaScript 执行、样式计算、布局、绘制和合成等活动的详细时间线。

使用方法

  1. 打开 DevTools(Windows/Linux: F12 或 Ctrl+Shift+I, Mac: Command+Option+I)
  2. 切换到 Performance 选项卡
  3. 点击左上角的"录制"按钮(圆形记录图标)
  4. 在页面上执行需要分析的操作(如滚动、点击、输入等)
  5. 点击"停止"按钮结束录制
  6. 分析生成的性能报告

你也可以使用 Performance API 在代码中标记和测量特定操作:

// 开始标记一个操作
performance.mark('操作开始');// 执行需要测量的代码
doSomething();// 结束标记
performance.mark('操作结束');// 创建测量(从开始到结束)
performance.measure('操作耗时', '操作开始', '操作结束');// 获取测量结果
const measurements = performance.getEntriesByType('measure');
console.log(measurements);

性能记录解读

Performance 面板的报告包含多个关键区域,每个区域提供不同的性能信息:

  • 控制栏:包含录制设置(如设备模拟、网络节流、CPU 节流等),这些设置可以模拟不同的设备条件。

  • 概述窗格:显示 FPS(帧率)、CPU 利用率和网络活动的总览图表。您可以在此处拖动选择要查看详细信息的时间段。

    • FPS 图表中的绿色条越高,表示帧率越高,用户体验越流畅
    • 红色块表示帧率下降严重,可能导致卡顿
    • CPU 图表展示了不同类型活动(如脚本执行、渲染、垃圾回收)占用的 CPU 时间
  • 火焰图(Flame Chart):主要展示主线程活动的详细时间线。这是分析性能瓶颈的核心区域:

    • 每个条形代表一个事件,宽度表示执行时间
    • 条形堆叠表示调用栈,顶部事件由其下方的事件调用
    • 黄色部分表示 JavaScript 执行
    • 紫色部分表示布局计算(可能导致重排)
    • 绿色部分表示绘制操作(重绘)
    • 灰色部分通常表示系统活动或空闲时间
  • 关键性能事件:在时间线上标记的重要事件,如:

    • FCP(首次内容绘制)
    • LCP(最大内容绘制)
    • Layout Shifts(布局偏移)
    • Long Tasks(长任务,执行时间超过 50ms 的任务)

优化决策方法

查看性能记录时,应重点关注以下几点:

  1. 长任务:查找持续时间超过 50ms 的任务(在火焰图中显示为红色标记),这些任务会阻塞主线程,导致界面无响应。

  2. 布局抖动:查找反复触发布局(紫色事件)的模式,这通常表示代码中存在强制重排的问题。

  3. 过多垃圾回收:频繁的垃圾回收(标记为 GC 的灰色事件)表明可能存在内存管理问题。

  4. 阻塞渲染的资源:检查资源加载是否阻塞了关键渲染路径。

内存面板(Memory Panel)实用指南

内存泄漏是导致页面长时间运行后性能不断下降的主要原因之一。Chrome DevTools 的 Memory 面板提供了强大的工具来分析内存使用情况:

使用方法

  1. 打开 DevTools 并切换到 Memory 选项卡
  2. 选择分析类型:
    • Heap Snapshot(堆快照):捕获 JavaScript 对象和相关 DOM 节点的完整内存快照
    • Allocation Timeline(分配时间线):记录随时间推移的内存分配情况
    • Allocation Sampling(分配采样):低开销的内存分配采样

内存泄漏检测步骤

  1. 基线快照:在页面加载完成后立即拍摄一个堆快照作为基准
  2. 操作执行:执行可能导致内存泄漏的操作(如打开/关闭模态框、切换页面等)
  3. 强制垃圾回收:点击内存面板中的垃圾桶图标强制执行垃圾回收
  4. 比较快照:拍摄第二个快照,并使用比较功能(选择 “Comparison” 视图)分析两次快照的差异

分析关键点

  • 关注 “Objects added” (新增对象)部分
  • 检查 “Detached DOM trees”(分离的 DOM 树)和 “Detached elements”(分离的元素)
  • 查看对象的引用链(右键选择 “Show object’s references”)以了解是什么阻止了对象被垃圾回收

内存泄漏:检测与防范

内存泄漏不仅会导致页面随时间推移变得缓慢,还可能最终导致页面崩溃。了解常见的内存泄漏模式及其解决方法至关重要。

常见内存泄漏模式

1. 闭包引用导致的泄漏

闭包是 JavaScript 中强大的特性,但如果使用不当,可能导致大对象无法被垃圾回收:

// 泄漏示例:闭包长期引用大型数据结构
function createLeak() {// 创建一个大型数组(约占用 8MB 内存)const largeArray = new Array(1000000).fill('x');// 返回的函数形成闭包,持有对 largeArray 的引用return function() {// 即使只使用数组的一小部分,整个数组都不会被回收console.log(largeArray[0]);};
}// 现在 leak 函数持有对 largeArray 的引用
// 即使 largeArray 再也不会被完全使用,它也不会被垃圾回收
const leak = createLeak();// 即使调用无数次,largeArray 始终存在于内存中
leak(); 

这种情况下,返回的函数通过闭包持有对 largeArray 的引用,即使只用到了数组的第一个元素,整个 1,000,000 元素的数组也会一直保留在内存中。

解决方法

function avoidLeak() {// 创建大型数组const largeArray = new Array(1000000).fill('x');// 只保留需要的数据const firstItem = largeArray[0]; // 返回仅引用所需数据的函数return function() {console.log(firstItem);};// largeArray 在函数结束后可以被垃圾回收
}

2. 未清除的事件监听器

事件监听器是最常见的内存泄漏来源之一,特别是在 SPA(单页应用)中:

// 泄漏示例:事件监听器未被清除
function setupListener() {const button = document.getElementById('my-button');// 创建引用大量数据的处理函数const largeData = new Array(1000000).fill('x');// 添加引用了 largeData 的事件监听器button.addEventListener('click', function() {console.log(largeData.length);});
}// 调用函数设置监听器
setupListener();// 即使后来移除了按钮元素,事件监听器仍然存在,
// 由于监听器引用了 largeData,所以 largeData 也不会被回收
document.body.removeChild(document.getElementById('my-button'));

在这个例子中,即使按钮从 DOM 中移除,事件监听器仍持有对 largeData 的引用,导致内存泄漏。

解决方法

function setupListenerProperly() {const button = document.getElementById('my-button');const largeData = new Array(1000000).fill('x');// 存储处理函数的引用,以便稍后可以移除const handleClick = function() {console.log(largeData.length);};button.addEventListener('click', handleClick);// 返回清理函数,在组件卸载或元素移除前调用return function cleanup() {button.removeEventListener('click', handleClick);// 现在 handleClick 和 largeData 可以被垃圾回收};
}const cleanup = setupListenerProperly();// 在移除元素前调用清理函数
cleanup();
document.body.removeChild(document.getElementById('my-button'));

3. 循环引用

对象之间相互引用可能导致整个对象图都无法被垃圾回收:

// 泄漏示例:循环引用
function createCyclicReference() {let objectA = { name: 'Object A', data: new Array(1000000) };let objectB = { name: 'Object B' };// 创建循环引用objectA.reference = objectB;objectB.reference = objectA;// 只返回 objectB,看似objectA可以被回收return objectB;
}const result = createCyclicReference();
// 虽然我们只保留了对 objectB 的引用,
// 但由于循环引用,objectA 及其大型数组也不会被回收

现代 JavaScript 引擎通常能处理简单的循环引用,但复杂的对象关系仍可能导致问题。

使用 WeakMap 和 WeakSet

WeakMapWeakSet 是解决特定类型内存泄漏的利器,它们持有对对象的弱引用,不会阻止被引用对象的垃圾回收:

// 使用 WeakMap 存储与 DOM 元素关联的数据
const nodeData = new WeakMap();function processNode(node) {// 为节点关联大量数据const data = { processed: true, timestamp: Date.now(),details: new Array(10000).fill('x')};// 使用 WeakMap 存储关联nodeData.set(node, data);// 当 node 被从 DOM 中移除并且没有其他引用时,// data 对象会被自动垃圾回收,不会造成内存泄漏
}// 处理一个DOM节点
const div = document.createElement('div');
processNode(div);// 当 div 不再被引用时,WeakMap 中关联的数据也会被回收
div = null; // 假设没有其他地方引用这个 div

WeakMap 的实际应用场景包括:

  1. 存储 DOM 节点的额外数据,而不影响节点的生命周期
  2. 实现私有属性和方法
  3. 缓存计算结果,但不阻止对象被回收

JavaScript 内存管理最佳实践

除了解决具体的内存泄漏问题,还应遵循以下最佳实践:

  1. 定期检查内存使用情况:将内存分析纳入开发和测试流程
  2. 避免全局变量:全局变量不会被垃圾回收,除非页面刷新
  3. 使用事件委托:减少事件监听器数量
  4. 合理使用闭包:确保闭包不会无意中引用大型对象
  5. 注意 DOM 引用:不要在长期存在的对象中保存对临时 DOM 元素的引用
  6. 定期进行代码审查:特别关注内存管理相关问题

重排重绘:渲染性能优化

理解浏览器的渲染流程是优化视觉性能的基础。这一流程通常包括以下步骤:

  1. JavaScript: 执行 JavaScript 代码,可能改变 DOM 或 CSSOM
  2. Style: 根据 CSS 规则计算元素的样式
  3. Layout (重排): 计算元素的几何位置和大小
  4. Paint (重绘): 填充元素的像素
  5. Composite: 将各层合成并显示在屏幕上

重排(Layout/Reflow)和重绘(Paint/Repaint)是渲染过程中最消耗性能的步骤:

  • 重排:当元素的几何属性(如宽度、高度、位置)发生变化时触发,需要重新计算布局
  • 重绘:当元素的视觉属性(如颜色、透明度)发生变化时触发,不改变布局

检测重排重绘问题

Chrome DevTools 提供了多种方法来识别重排重绘问题:

  1. Performance 面板

    • 重排在火焰图中显示为紫色的"Layout"事件
    • 重绘显示为绿色的"Paint"事件
    • 这些事件时间过长或频率过高都表明存在性能问题
  2. 渲染面板

    • 打开 DevTools > 按 Esc 键 > 在出现的抽屉面板中选择"Rendering"
    • 启用"Paint flashing"可以高亮显示重绘区域
    • 启用"Layout Shifts"可以显示布局偏移区域
  3. 性能监控

    • 开启 FPS 计数器:DevTools > 更多工具 > 渲染 > FPS meter
    • 帧率下降通常表明存在渲染性能问题

减少重排重绘的策略详解

1. 批量修改 DOM

每次 DOM 修改都可能触发重排和重绘。通过批量修改可以将多次更改合并为一次:

// 优化前:每次操作都会触发布局计算
function poorPerformance() {const element = document.getElementById('container');// 每行都可能导致单独的重排element.style.width = '100px';element.style.height = '200px';element.style.margin = '10px';element.style.padding = '15px';element.style.border = '1px solid black';
}// 优化后:批量修改样式
function goodPerformance() {const element = document.getElementById('container');// 方法一:使用 cssText 一次性设置多个样式element.style.cssText = 'width: 100px; height: 200px; margin: 10px; padding: 15px; border: 1px solid black;';// 方法二:使用 class 切换而不是直接修改样式// element.classList.add('styled-container');// 方法三:使用 DocumentFragment 批量添加多个DOM元素// const fragment = document.createDocumentFragment();// for (let i = 0; i < 10; i++) {//   const child = document.createElement('div');//   child.textContent = `Item ${i}`;//   fragment.appendChild(child);// }// element.appendChild(fragment); // 只触发一次重排
}

2. 使用 will-change 属性提示浏览器

will-change 属性告诉浏览器元素的某个属性可能会发生变化,使浏览器提前做好准备:

/* 告诉浏览器这些元素的 transform 和 opacity 属性会发生变化 */
.animated-element {will-change: transform, opacity;
}/* 注意:animated-element-gpu 为需要进行动画的元素创建新的层 */
.animated-element-gpu {/* 将元素提升到 GPU 层 */transform: translateZ(0);/* 或使用 will-change */will-change: transform;
}

需要注意的是,will-change 不应过度使用,因为:

  • 创建新的图层需要额外的内存
  • 对于过多元素同时使用会适得其反
  • 应该在动画开始前添加,在动画结束后移除

3. 使用 Transform 和 Opacity 属性代替直接改变位置和显示

CSS 的 transformopacity 属性是特殊的,它们的变化通常只触发合成阶段,跳过布局和绘制步骤:

/* 不佳实践:更改位置属性导致重排 */
.box-bad {transition: left 0.5s, top 0.5s;position: absolute;left: 0;top: 0;
}
.box-bad:hover {left: 100px;top: 100px;
}/* 良好实践:使用 transform 只触发合成 */
.box-good {transition: transform 0.5s;position: absolute;transform: translate(0, 0);
}
.box-good:hover {transform: translate(100px, 100px);
}

4. 离线操作 DOM

当需要进行大量 DOM 操作时,先将元素从文档流中移除,操作完成后再放回:

// 优化复杂 DOM 操作
function updateComplexUI(data) {const list = document.getElementById('large-list');// 1. 记录当前滚动位置const scrollTop = list.scrollTop;// 2. 从文档流中移除元素const parent = list.parentNode;const nextSibling = list.nextSibling;parent.removeChild(list);// 3. 进行大量DOM操作for (let i = 0; i < data.length; i++) {const item = document.createElement('li');item.textContent = data[i].name;list.appendChild(item);}// 4. 将元素放回文档if (nextSibling) {parent.insertBefore(list, nextSibling);} else {parent.appendChild(list);}// 5. 恢复滚动位置list.scrollTop = scrollTop;
}

5. 使用 CSS 动画而非 JavaScript 操作

CSS 动画通常比 JavaScript 动画更高效,因为浏览器可以对其进行优化:

/* CSS 动画示例 */
@keyframes slide-in {from { transform: translateX(-100%); }to { transform: translateX(0); }
}.animated {animation: slide-in 0.5s ease-out;
}

6. 避免强制同步布局

当 JavaScript 在读取某些 DOM 属性后立即修改 DOM 时,可能导致浏览器提前执行布局计算:

// 不良实践:强制同步布局
function forceSyncLayout() {const boxes = document.querySelectorAll('.box');boxes.forEach(box => {// 读取布局信息const width = box.offsetWidth;// 立即写入修改,导致浏览器必须重新计算布局box.style.width = (width * 2) + 'px';// 再次读取,导致另一次强制布局const height = box.offsetHeight;box.style.height = (height * 2) + 'px';});
}// 良好实践:分离读写操作
function avoidForcedLayout() {const boxes = document.querySelectorAll('.box');const dimensions = [];// 先读取所有需要的布局信息boxes.forEach(box => {dimensions.push({width: box.offsetWidth,height: box.offsetHeight});});// 再一次性写入所有修改boxes.forEach((box, i) => {const dim = dimensions[i];box.style.width = (dim.width * 2) + 'px';box.style.height = (dim.height * 2) + 'px';});
}

异步加载优化

在现代 Web 应用中,资源加载策略直接影响页面启动性能。异步加载技术允许页面只加载当前需要的资源,推迟非关键资源的加载。

代码分割与懒加载详解

代码分割是将应用程序代码分解成多个小块(chunks),按需加载的过程。这种方法能显著减少初始加载时间:

在 React 中实现代码分割

// 传统方式:一次性加载所有组件
import Dashboard from './Dashboard';
import Profile from './Profile';
import Settings from './Settings';// 使用 React.lazy 和 Suspense 实现代码分割
import React, { Suspense, lazy } from 'react';// 组件将在需要渲染时才加载
const Dashboard = lazy(() => import('./Dashboard'));
const Profile = lazy(() => import('./Profile'));
const Settings = lazy(() => import('./Settings'));function App() {return (<Suspense fallback={<div>Loading...</div>}><Router><Route path="/dashboard" component={Dashboard} /><Route path="/profile" component={Profile} /><Route path="/settings" component={Settings} /></Router></Suspense>);
}

在 Vue 中实现代码分割

// Vue Router 配置中的代码分割
const routes = [{path: '/dashboard',name: 'Dashboard',// 使用动态导入实现懒加载component: () => import('./views/Dashboard.vue')},{path: '/profile',name: 'Profile',component: () => import('./views/Profile.vue')}
];

使用 Webpack 手动控制代码分割

// Webpack 动态导入示例
button.addEventListener('click', () => {// 动态导入模块,仅在点击按钮时加载import('./modules/heavy-module.js').then(module => {module.default();}).catch(err => console.error('Module loading failed:', err));
});

图片懒加载深度剖析

图片通常是 Web 应用中最大的资源,实现图片懒加载可以显著提升页面加载性能:

使用原生懒加载

<!-- 使用 HTML5 原生懒加载属性 -->
<img src="placeholder.jpg" data-src="actual-image.jpg" loading="lazy" alt="Lazy loaded image" class="lazy-image" />

使用 Intersection Observer API 实现自定义懒加载

// 高性能的图片懒加载实现
document.addEventListener("DOMContentLoaded", function() {// 获取所有带有 lazy-image 类的图片const lazyImages = document.querySelectorAll(".lazy-image");// 如果浏览器不支持 IntersectionObserver,则加载所有图片if (!('IntersectionObserver' in window)) {lazyImages.forEach(image => {if (image.dataset.src) {image.src = image.dataset.src;}});return;}// 创建观察器实例const imageObserver = new IntersectionObserver((entries, observer) => {entries.forEach(entry => {// 当图片进入视口时if (entry.isIntersecting) {const img = entry.target;// 替换图片源if (img.dataset.src) {img.src = img.dataset.src;}// 图片加载完成后移除占位样式img.onload = () => {img.classList.remove("lazy-placeholder");img.classList.add("lazy-loaded");};// 停止观察已处理的图片observer.unobserve(img);}});}, {// 根元素,默认为浏览器视口root: null,// 根元素的边距,用于扩展或缩小视口rootMargin: '0px 0px 200px 0px', // 图片距离视口底部200px时开始加载// 元素可见度达到多少比例时触发回调threshold: 0.01 // 图片有1%进入视口时触发});// 开始观察所有懒加载图片lazyImages.forEach(image => {imageObserver.observe(image);});
});

相比简单的滚动事件监听,Intersection Observer API 更高效,不会阻塞主线程,并且提供更精确的可见性检测。

预加载和预获取技术

现代浏览器提供了资源提示(Resource Hints)API,允许开发者指示浏览器预加载关键资源:

<!-- 预加载当前页面立即需要的资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="hero-image.jpg" as="image">
<link rel="preload" href="main-font.woff2" as="font" crossorigin><!-- 预获取用户可能导航到的下一个页面资源 -->
<link rel="prefetch" href="next-page.html">
<link rel="prefetch" href="article-data.json"><!-- 预连接到将要从中请求资源的域 -->
<link rel="preconnect" href="https://api.example.com"><!-- DNS 预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">

这些资源提示的使用场景:

  • preload:用于当前页面肯定会用到的关键资源
  • prefetch:用于下一页面可能需要的资源
  • preconnect:用于提前建立到第三方域的连接
  • dns-prefetch:用于提前解析第三方域的 DNS

按需加载与按需执行

除了按需加载资源外,还可以实现按需执行代码:

// 按需执行示例:用户交互触发的代码
function setupDeferredExecution() {// 只设置事件监听,不立即加载或执行复杂逻辑document.getElementById('advanced-feature').addEventListener('click', () => {// 用户点击时再加载并执行复杂功能import('./features/advanced-chart.js').then(module => {module.initializeChart('chart-container');});});// 使用 Intersection Observer 监测元素是否接近视口const observer = new IntersectionObserver((entries) => {entries.forEach(entry => {if (entry.isIntersecting) {// 元素接近视口时加载评论系统import('./features/comments.js').then(module => {module.initComments();observer.unobserve(entry.target);});}});}, { rootMargin: '200px' });// 观察评论容器const commentsSection = document.getElementById('comments-section');if (commentsSection) {observer.observe(commentsSection);}
}

性能优化工作流程

一套有效的性能优化工作流程或许是这样的:

1. 建立基准

在开始优化前,必须建立性能基准,以便衡量改进效果:

// 使用 Performance API 建立基准
const performanceMeasures = {};// 记录关键用户操作的性能
function measurePerformance(action, callback) {const startMark = `${action}_start`;const endMark = `${action}_end`;performance.mark(startMark);// 执行操作const result = callback();performance.mark(endMark);performance.measure(action, startMark, endMark);// 收集测量结果const measures = performance.getEntriesByName(action);performanceMeasures[action] = measures[0].duration;console.log(`${action} took ${measures[0].duration.toFixed(2)}ms`);return result;
}// 使用示例
measurePerformance('product_filter', () => {return filterProducts(products, { category: 'electronics' });
});

除了代码测量外,使用以下工具建立全面基准:

  • Lighthouse: 提供全面的性能审计报告
  • WebPageTest: 在不同网络条件和设备上测试性能
  • Core Web Vitals 报告: 使用真实用户数据评估性能

2. 诊断问题

使用系统化方法定位性能瓶颈:

  • 性能瀑布图分析: 查看关键渲染路径和阻塞资源
  • JavaScript CPU 分析: 识别耗时的函数调用
  • 内存分析: 查找内存泄漏和过度内存使用
  • 渲染性能: 检测重排重绘和帧率下降

3. 制定方案

根据诊断结果,制定针对性的优化策略:

问题类型优化策略
资源加载过多代码分割、懒加载、资源压缩
主线程阻塞Web Workers、长任务分解、节流/防抖
渲染性能不佳虚拟滚动、减少重排重绘、使用 CSS 硬件加速
内存管理问题修复内存泄漏、减少闭包、使用 WeakMap/WeakSet

4. 实施优化

遵循"最大收益原则",先处理影响最显著的问题:

  1. 优先级划分:

    • P0: 影响核心功能的严重性能问题
    • P1: 影响用户体验但不阻碍核心功能的问题
    • P2: 小型优化和改进
  2. 增量实施:

    • 每次修改后测量性能改进
    • 确保不引入新的性能问题
    • 建立性能回归测试

5. 验证成效

使用多种方法验证优化效果:

// 性能对比测试
function runPerformanceComparison(testName, oldFn, newFn, iterations = 1000) {console.log(`Running comparison for: ${testName}`);// 预热for (let i = 0; i < 10; i++) {oldFn();newFn();}// 测试旧实现const startOld = performance.now();for (let i = 0; i < iterations; i++) {oldFn();}const endOld = performance.now();const oldTime = endOld - startOld;// 测试新实现const startNew = performance.now();for (let i = 0; i < iterations; i++) {newFn();}const endNew = performance.now();const newTime = endNew - startNew;// 计算改进百分比const improvement = ((oldTime - newTime) / oldTime) * 100;console.log(`Old implementation: ${oldTime.toFixed(2)}ms`);console.log(`New implementation: ${newTime.toFixed(2)}ms`);console.log(`Improvement: ${improvement.toFixed(2)}%`);return {oldTime,newTime,improvement};
}

除了代码测试,还应进行:

  • A/B 测试: 对比新旧实现在真实用户中的表现
  • 用户体验测试: 收集用户对优化后体验的反馈
  • 回归测试: 确保优化不影响功能正确性

6. 持续监控

建立长期性能监控系统:

  • 实时性能监控: 使用 Performance API 和 Beacon API 收集真实用户数据
  • 性能预算: 设定资源大小、加载时间和交互延迟的上限
  • 性能警报: 当性能指标超过阈值时触发警报
  • 定期审查: 每个版本发布前进行性能审查

通过这种系统化的方法,性能优化不再是一次性工作,而是开发流程中的持续活动。

未来趋势与进阶技术

作为前端工程师,了解性能优化的未来趋势对保持技术竞争力至关重要:

Web Assembly (WASM)

WASM 允许以接近原生的速度在浏览器中运行代码,适用于计算密集型任务:

// 示例:使用 WASM 加速图像处理
async function loadWasmImageProcessor() {try {// 加载 WASM 模块const wasmModule = await WebAssembly.instantiateStreaming(fetch('/image-processor.wasm'),{env: {abort: () => console.error('WASM模块出错')}});// 获取导出的函数const { applyFilter } = wasmModule.instance.exports;// 使用 WASM 函数处理图像function processImage(imageData) {const { data, width, height } = imageData;// 分配内存const wasmMemory = wasmModule.instance.exports.memory;const inputPtr = wasmModule.instance.exports.allocate(data.length);// 拷贝数据到 WASM 内存const inputArray = new Uint8Array(wasmMemory.buffer, inputPtr, data.length);inputArray.set(data);// 调用 WASM 函数处理图像const outputPtr = applyFilter(inputPtr, width, height);// 获取结果const outputArray = new Uint8Array(wasmMemory.buffer, outputPtr, data.length);const resultData = new Uint8ClampedArray(outputArray);// 清理内存wasmModule.instance.exports.deallocate(inputPtr);wasmModule.instance.exports.deallocate(outputPtr);return new ImageData(resultData, width, height);}return processImage;} catch (error) {console.error('Failed to load WASM module:', error);// 降级处理return fallbackImageProcessor;}
}

HTTP/3 和 QUIC

新的网络协议提供更快的连接建立和更可靠的传输:

// 检测并优先使用 HTTP/3 
async function detectAndUseHTTP3() {// 检测浏览器是否支持 HTTP/3const supportsHTTP3 = 'http3' in window || 'quic' in window;if (supportsHTTP3) {// 使用支持 HTTP/3 的 CDN 域名return 'https://http3.example.com';} else {// 降级到 HTTP/2return 'https://cdn.example.com';}
}// 在资源加载中使用
async function loadResources() {const baseUrl = await detectAndUseHTTP3();const resources = [`${baseUrl}/styles.css`,`${baseUrl}/main.js`,`${baseUrl}/images/hero.jpg`];// 预连接const link = document.createElement('link');link.rel = 'preconnect';link.href = baseUrl;document.head.appendChild(link);// 加载资源// ...
}

Web Workers 和计算并行化

Web Workers 使复杂计算可以在后台线程运行,不阻塞 UI 线程:

// 主线程代码
function setupDataProcessing() {// 创建 Workerconst worker = new Worker('data-processor.js');// 监听 Worker 消息worker.addEventListener('message', (event) => {const { type, result } = event.data;switch (type) {case 'PROCESSED_DATA':updateUI(result);break;case 'PROGRESS':updateProgressBar(result.percent);break;case 'ERROR':showError(result.message);break;}});// 发送数据到 Workerfunction processLargeDataSet(data) {worker.postMessage({type: 'PROCESS_DATA',data});}return {processLargeDataSet,terminateWorker: () => worker.terminate()};
}// Worker 文件 (data-processor.js)
/* 
self.addEventListener('message', (event) => {const { type, data } = event.data;if (type === 'PROCESS_DATA') {try {// 报告进度self.postMessage({ type: 'PROGRESS', result: { percent: 0 } });// 进行耗时计算const chunks = splitIntoChunks(data, 10);let processedData = [];chunks.forEach((chunk, index) => {const processed = processChunk(chunk);processedData = processedData.concat(processed);// 更新进度const progress = Math.round(((index + 1) / chunks.length) * 100);self.postMessage({ type: 'PROGRESS', result: { percent: progress } });});// 发送处理结果self.postMessage({type: 'PROCESSED_DATA',result: processedData});} catch (error) {self.postMessage({type: 'ERROR',result: { message: error.message }});}}
});function splitIntoChunks(array, numChunks) {// 将数组分成多个块const chunkSize = Math.ceil(array.length / numChunks);return Array.from({ length: numChunks }, (_, i) => array.slice(i * chunkSize, (i + 1) * chunkSize));
}function processChunk(chunk) {// 处理数据的复杂计算return chunk.map(item => {// 假设这是一个复杂计算return complexTransformation(item);});
}
*/

结语

JavaScript 性能优化是一个不断发展的领域,需要持续学习和实践。通过本文介绍的诊断工具、优化策略和最佳实践,我希望能为你提供一个全面的性能优化框架。

和许多事情一样,性能优化也不是一蹴而就的,而是需要贯穿整个开发生命周期的持续实践。

参考资源

  • Web Vitals - Google 的 Web 性能指标体系
  • Chrome DevTools 官方文档
  • JavaScript 性能优化 - Web.dev 上的性能优化指南
  • 高性能 JavaScript - Nicholas Zakas 的经典著作
  • You Don’t Know JS - 深入理解 JavaScript 的系列书籍

如果你觉得这篇文章有帮助,欢迎点赞收藏,也期待在评论区看到你的想法和建议!👇

终身学习,共同成长。

咱们下一期见

💻

相关文章:

JavaScript 性能优化:调优策略与工具使用

引言 在当今的 Web 开发领域&#xff0c;性能优化已不再是锦上添花&#xff0c;而是产品成功的关键因素。据 Google 研究表明&#xff0c;页面加载时间每增加 3 秒&#xff0c;跳出率将提高 32%。而移动端用户如果页面加载超过 3 秒&#xff0c;有 53% 的用户会放弃访问。性能…...

48、c# 中 IList 接⼝与List的区别是什么?

在 C# 中&#xff0c;IList 接口和 List 类在集合操作中扮演不同角色&#xff0c;主要区别体现在定义、功能、灵活性、性能及适用场景等方面。以下是详细对比&#xff1a; 1. 定义与本质 IList 接口 抽象契约&#xff1a;仅定义集合的基本操作&#xff08;如索引访问、添加、…...

在 Azure OpenAI 上使用 Elastic 优化支出和内容审核

作者&#xff1a;来自 Elastic Muthukumar Paramasivam&#xff0c;Bahubali Shetti 及 Daniela Tzvetkova 我们为 Azure OpenAI 正式发布包添加了更多功能&#xff0c;现在提供内容过滤监控和计费见解的增强&#xff01; 在之前的博客中&#xff0c;我们展示了如何使用 Elasti…...

Redis学习专题(三)主从复制

目录 引言&#xff1a; 1、搭建一主多从 1) 创建/hspredis目录, 并拷贝redis.conf 到 /hspredis 2) vi /hspredis/redis.conf , 进行如下设置 3) 创建3个文件/hspredis/redis6379.conf 、/hspredis/redis6380.conf 、/hspredis/redis6381.conf 并编辑 4) 启动三台redis服…...

设计模式之备忘录模式

在日常开发中&#xff0c;我们经常会遇到这样的场景&#xff1a;需要保存对象的某个历史状态&#xff0c;以便将来恢复。这种需求最常见的例子就是“撤销操作”。在这种情况下&#xff0c;备忘录模式(Memento Pattern)就派上了用场。 目录 1. 概念 2. 代码实现 3. 总结 1. …...

深度学习-runner.run(data_loaders, cfg.workflow)内部执行过程

文件&#xff1a;~/catkin_ws/SparseDrive/projects/mmdet3d_plugin/apis/mmdet_train.py 完成数据加载器、优化器、运行器实例化后&#xff0c; RUNNERS.register_module() class IterBasedRunner(BaseRunner):"""Iteration-based Runner.This runner train m…...

嵌入式开发学习日志(linux系统编程--文件读写函数)Day24

一、系统编程 标准oi 【输入输出】 stdio.h 头文件 &#xff1a;stdio.h >标准输入输出头文件&#xff1b;/usr/include/stdio.h 二、文件操作 1、关于文件操作的步骤 &#xff08;1&#xff09;打开文件&#xff1b; &#xff08;2&#xff09;io操作&#xff0c;读写…...

DEBUG:Lombok 失效

DEBUG&#xff1a;Lombok 失效 问题描述 基于 Spring Boot 的项目中&#xff0c;编译时显示找不到 log 属性。查看对应的 class 类&#xff0c;Lombok 正常在编译时生成 log 属性。 同时存在另一个问题&#xff0c;使用Getter注解&#xff0c;但实际使用中该注解并没有生效&…...

Qt 控件发展历程 + 目标(1)

文章目录 声明简述控件的发展历程学习目标QWidget属性 简介&#xff1a;这篇文章只是一个引子&#xff0c;介绍一点与控件相关的但不重要的内容&#xff08;浏览浏览即可&#xff09;&#xff0c;这一章节最为重要的还是要把之后常用且重要的控件属性和作用给学透&#xff0c;学…...

按键精灵ios/安卓辅助工具高级函数OcrEx文字识别(增强版)脚本开发介绍

函数名称 OcrEx文字识别&#xff08;增强版&#xff09; 函数功能 返回指定区域内所有识别到的字符串、左上角坐标、区域宽高、可信度&#xff0c;无需自制字库&#xff0c;识别范围越小&#xff0c;效率越高&#xff0c;结果越准确 注意&#xff1a;安卓版按键APP需在设置…...

零基础入门Selenium自动化测试:自动登录edu邮箱

&#x1f31f; Selenium简单概述一下 Selenium 是一个开源的自动化测试工具&#xff0c;主要用于 Web 应用程序的功能测试。它能够模拟用户操作浏览器的行为&#xff08;如点击按钮、填写表单、导航页面等&#xff09;&#xff0c;应用于前端开发、测试和运维领域。 特点 跨…...

MySQL高频面试八连问(附场景化解析)

文章目录 "为什么订单查询突然变慢了&#xff1f;"——从这个问题开始说起一、索引的生死时速&#xff08;必考题&#xff01;&#xff09;二、事务的"套娃"艺术三、锁机制的相爱相杀四、存储引擎的抉择五、慢查询的破案技巧六、分页的深度优化七、高可用架…...

JVM 性能问题排查实战10连击

&#x1f5c2;️ 目录 前言&#xff1a;理论掌握只是起点&#xff0c;定位能力才是核心全局排查模型&#xff1a;三步法1️⃣Full GC 频繁触发&#xff1a;老年代压力过大2️⃣ OOM 爆炸&#xff1a;元空间泄漏 or 缓存未清理3️⃣ CPU 飙升却不是 GC&#xff1a;线程阻塞或热方…...

零基础深入解析 ngx_http_session_log_module

一、引言 在传统的 HTTP 日志中&#xff0c;每个请求都会被单独记录&#xff0c;这对于短连接、异步加载等场景非常直观&#xff1b;但在一些需要以“会话”为单位分析用户行为的场景下&#xff0c;如视频点播、多资源并行加载、长轮询等&#xff0c;单个请求日志难以准确反映…...

10.17 LangChain v0.3核心机制解析:从工具调用到生产级优化的实战全指南

LangChain v0.3 技术生态与未来发展 关键词:LangChain 工具调用, 聊天模型集成, @tool 装饰器, ToolMessage 管理, 多模态交互 使用聊天模型实现工具调用 LangChain v0.3 通过 工具调用(Tool Calling) 机制,将大模型与外部工具深度结合,形成闭环能力链。本节以 GPT-4、L…...

Android Framework学习七:Handler、Looper、Message

文章目录 简介LooperMessageMessageQueueHandlerFramework学习系列文章 简介 Looper当做一台传送装置&#xff0c;MessageQueue是传送带&#xff0c;传送带上放的是Message&#xff0c;Handler用于发送Message分发与接收处理。 Looper frameworks/base/core/java/android/app…...

分钟级降水预报API:精准预测每一滴雨的智慧科技

引言&#xff1a;天气预报进入"分钟时代" 在数字化生活高度发达的今天&#xff0c;人们对天气预报的精确度要求越来越高。传统的24小时预报或小时级预报已无法满足出行、物流、户外活动等场景的精细化需求。分钟级降水预报API的出现&#xff0c;标志着气象服务正式进…...

民政部等部门针对老人权益保障工作发布指导意见

​ 1 品牌资讯 佛慈制药&#xff1a;将探索开发特医食品等产品 李子园将丰富大健康产品矩阵适应银发族需求 京东健康2025年第一季度收入166.45亿元 宁美浩维获融资&#xff0c;致力提供健康管理方案 2 行业动态 固生堂合作华为&#xff0c;联合推动中医药智慧化转型 怡…...

LinkedList源码分析

1. LinkedList初始化 public class LinkedListTest {public static void main(String[] args) {LinkedList<String> list new LinkedList<String>();// 新增list.add("a");list.add("b");list.add("c");list.add("d");l…...

OpenAI Codex 加入Agent编程工具新阵营

上周五&#xff0c;OpenAI推出了一款名为Codex的新型编程系统&#xff0c;该系统能够通过自然语言命令执行复杂的编程任务。Codex标志着OpenAI正式进军正在形成的代理编程工具新阵营。 从GitHub早期的Copilot到当代的Cursor和Windsurf等工具&#xff0c;大多数AI编程助手都是作…...

AMBA三种总线详解并比较

AMBA三种总线详解并比较 AMBA&#xff08;Advanced Microcontroller Bus Architecture&#xff09;是 ARM 公司推出的片上总线标准&#xff0c;旨在为 SoC&#xff08;片上系统&#xff09;提供高效、灵活的通信架构。 一、总线详解 1. AHB&#xff08;Advanced High-perform…...

国产视频转换LT6211UX:HDMI2.0转LVDS/MIPI芯片简介,支持4K60Hz

1. LT6211UX HDMI2.0信号输入 支持HDMI2.0b, HDMI1.4和DVI1.0 支持HDCP2.2和HDCP1.4 数据速率高达6Gbps 自适应接收机均衡 支持4k60Hz 支持的3D格式&#xff1a; 对于HDMI -> LVDS&#xff1a; 直接3D输出 2路2D L/R输出 对于HDMI -> MIPI&#xff1a; 框架包装&#x…...

在nextjs项目当中使用wagmi连接MetaMask SDK

Wagmi 是一个为以太坊和 EVM 兼容链构建的 React Hooks 库,专为简化 Web3 应用开发而设计。它提供了一组强大且类型安全的工具,使开发者能够更方便地与钱包(如 MetaMask、WalletConnect 等)和智能合约进行交互。 Wagmi 的全称其实并不是一个传统意义上的缩写,它源自加密社…...

SAP-ABAP:SAP的`TRY...CATCH` 异常处理机制详解

一、异常处理架构与核心机制 1. 异常分类与层次结构 异常类型触发机制处理要求典型子类CX_STATIC_CHECK编译器强制检查(必须声明或捕获)必须显式处理CX_SY_ZERODIVIDE(除零错误)CX_DYNAMIC_CHECK运行时检查(若未处理则触发运行时错误RESUMABLE_FAILURE)推荐显式处理CX_S…...

HarmonyOS NEXT~鸿蒙系统与Uniapp跨平台开发实践指南

HarmonyOS NEXT&#xff5e;鸿蒙系统与Uniapp跨平台开发实践指南 引言&#xff1a;鸿蒙与Uniapp的融合价值 华为鸿蒙系统(HarmonyOS)作为新一代智能终端操作系统&#xff0c;其分布式能力与跨设备协同特性为开发者带来了全新机遇。而Uniapp作为流行的跨平台应用开发框架&…...

python 提交命令 到远程windows中

在Python中&#xff0c;你可以使用多种方式来提交命令到远程Windows机器上。最常见的方法是通过SSH协议&#xff08;使用paramiko库&#xff09;或者通过Windows远程管理工具如WinRM&#xff08;使用python-winrm库&#xff09;。 使用Paramiko进行SSH连接 Paramiko是一个Pyth…...

【520 特辑】用 HTML/CSS/JavaScript 打造浪漫炫酷的表白网页

一、前言 在 520 这个充满爱意的日子里&#xff0c;程序员该如何用代码表达浪漫&#xff1f;本文将分享一个结合动画特效与交互设计的 520 表白网页案例&#xff0c;通过 HTML/CSS/JavaScript 实现动态爱心、渐变背景、浮动文字等炫酷效果&#xff0c;手把手教你用技术传递心意…...

【QT】QTableWidget获取width为100,与真实值不符问题解决

背景 用stackedWidget内嵌2个QTableWidget页面&#xff0c;实现切换。在进行stackedWidget.width()的获取时候&#xff0c;可以正常获得ui界面设置的宽度值&#xff0c;但是在QTableWidget页面用同样的方式无法成功获取真实值&#xff0c;即使采用获取内容区域宽度&#xff08…...

Hive drop column 的解决方法

示例&#xff1a; 创建 text 格式的表 create table t1(c1 int, c2 int) stored as textfile;增加一个字段 alter table t1 add columns (c3 int);使用 replace columns 删除新加的字段 alter table t1 replace columns (c1 int, c2 int);对于 ORC 类型的表&#xff0c;使用…...

Python虚拟环境再PyCharm中自由切换使用方法

Python开发中的环境隔离是必不可少的步骤,通过使用虚拟环境可以有效地管理不同项目间的依赖,避免包冲突和环境污染。虚拟环境是Python官方提供的一种独立运行环境,每个项目可以拥有自己单独的环境,不同项目之间的环境互不影响。在日常开发中,结合PyCharm这样强大的IDE进行…...

Spark大数据分析案例(pycharm)

所需文件&#xff08;将文件放在路径下&#xff0c;自己记住后面要用&#xff09;&#xff1a; 通过百度网盘分享的文件&#xff1a;beauty_p....csv等4个文件 链接&#xff1a;https://pan.baidu.com/s/1pBAus1yRgefveOc7NXRD-g?pwd22dj 提取码&#xff1a;22dj 复制这段内…...

【QT】ModbusTCP读写寄存器类封装

背景 在编写ModbusTCP时候&#xff0c;连接、寄存器读写属于通用的功能&#xff0c;为了便于后续直接复用&#xff0c;选择封装到一个类。本博文在封装展示该类过程中&#xff0c;会提及到编写该类过程中&#xff0c;出现的连接未成功的问题&#xff0c;以及该问题的解决方式。…...

SQLMesh 内置宏详解:@PIVOT等常用宏的核心用法与示例

本文系统解析 SQLMesh 的四个核心内置宏&#xff0c;涵盖行列转换的 PIVOT、精准去重的 DEDUPLICATE、灵活生成日期范围的 DATE_SPINE&#xff0c;以及动态表路径解析的 RESOLVE_TEMPLATE。通过真实案例演示参数配置与 SQL 渲染逻辑&#xff0c;并对比宏调用与传统 SQL 的差异&…...

ajax post请求 解决自动再get请求一次

ajax post请求 解决自动再get请求一次 HTMLjavascriptFlask第一种方法&#xff1a;第二种方法&#xff1a; HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title></head> &l…...

当前主流的传输技术(如OTN、IP-RAN、FlexE等)

好的&#xff01;当前主流的传输技术&#xff08;如OTN、IP-RAN、FlexE等&#xff09;各有其独特的应用场景&#xff0c;下面我会逐一展开讲解&#xff0c;并结合实际案例说明它们如何在不同领域发挥作用。 一、OTN&#xff08;光传送网&#xff09; 1. 核心特点 大容量&…...

利用 SQL Server 作业实现异步任务处理,简化系统架构

在现代企业系统中&#xff0c;异步任务是不可或缺的组成部分&#xff0c;例如&#xff1a; 电商系统中的订单超时取消&#xff1b; 报表系统中的异步数据导出&#xff1b; CRM 系统中的客户积分计算。 传统的实现方式通常涉及引入消息队列&#xff08;如 RabbitMQ、Kafka&a…...

【Java高阶面经】3.熔断机制深度优化:从抖动治理到微服务高可用架构实战

一、熔断抖动的本质剖析与核心成因 1.1 熔断机制的核心价值与抖动危害 熔断机制作为微服务弹性架构的核心组件,通过模拟电路断路器逻辑,在服务出现异常时自动阻断请求链,防止故障扩散引发雪崩。但频繁的“熔断-恢复-熔断”抖动会导致: 用户体验恶化:请求成功率波动大,响…...

如何删除 HP 笔记本电脑中的所有数据:3 种解决方案说明

当您准备删除 HP 笔记本电脑中的所有数据时&#xff0c;无论是为了保护您的隐私还是为设备重新启动做好准备&#xff0c;使用正确的方法非常重要。在本文中&#xff0c;您可以获得 3 个有效的解决方案&#xff0c;帮助您轻松删除计算机中的所有内容。之后&#xff0c;您可以安全…...

以太联 - Intellinet 闪耀台北 SecuTech 国际安全科技应用博览会

2025 年 5 月 7 日至 9 日&#xff0c;台北 SecuTech 国际安全科技应用博览会现场热闹非凡&#xff0c;以太联 - Intellinet 携旗下前沿产品与解决方案精彩亮相&#xff0c;成为展会上一道亮丽的风景线&#xff0c;吸引了众多业内人士的目光&#xff0c;收获了广泛关注与高度认…...

JavaScript性能优化实战(13):性能测试与持续优化

在前面的系列文章中,我们探讨了各种JavaScript性能优化的方法和实战案例。然而,优化工作不应仅是一次性的努力,而应当成为开发流程中的常态。本篇将聚焦于如何建立系统化的性能测试体系,并实现持续的性能优化机制,确保应用长期保持出色的性能表现。 前端性能测试体系构建…...

nbufxz动态规划1

草药题 dp[i][j]&#xff0c;考虑i个草药&#xff0c;j个时间&#xff0c;能获得的最大价值。这i个草药中&#xff0c;你不一定全部都采集了。你可能有的采了&#xff0c;有的没采。然后你最终得到了一个最优的结果。 状态转移方程无非就是&#xff1a; dp[i][j] max(dp[i …...

PostgreSQL 初体验

目录 一、PostgreSQL 1. 简介 2. 特点 &#xff08;1&#xff09; 开源免费&#xff08;Open Source&#xff09; &#xff08;2&#xff09;标准兼容&#xff08;SQL Compliance&#xff09; &#xff08;3&#xff09; 丰富的数据类型&#xff08;Data Types&#xff09…...

北斗导航 | 基于matlab的多波束技术的卫星通信系统性能仿真

基于多波束技术的低轨(LEO)卫星通信系统 **1. 仿真场景建模**1.1 LEO卫星轨道参数设置1.2 地面终端分布**2. 多波束天线模型**2.1 波束方向图生成2.2 频率复用方案**3. 链路预算与干扰分析**3.1 自由空间路径损耗3.2 信噪比(SNR)计算**4. 动态资源调度算法**4.1 基于流量需…...

数据结构与算法学习笔记(Acwing 提高课)----动态规划·状态机模型

数据结构与算法学习笔记----动态规划状态机模型 author: 明月清了个风 first publish time: 2025.5.20 ps⭐️背包终于结束了&#xff0c;状态机模型题目不多。状态机其实是一种另类的状态表示方法&#xff0c;将某一个点扩展为一个状态进行保存并在多个状态之间转移&#xf…...

Vue 3.0 中 Teleport 详解

Teleport 是 Vue 3.0 引入的一个非常有用的特性&#xff0c;它允许你将组件的一部分模板"传送"到 DOM 中的其他位置&#xff0c;而不改变组件的逻辑层次结构。 1. 基本概念 Teleport 的主要用途是将某些 DOM 元素渲染到 Vue 应用之外的 DOM 节点中&#xff0c;这在…...

Linux在防火墙中添加开放端口

例如&#xff1a;安装docker时启动报错&#xff1a; Job for docker.service failed because the control process exited with error code. See "systemctl status docker.service" and "journalctl -xe" for details. 此时开放对应端口号就可以咯 在防…...

day24:零基础学嵌入式之系统编程

一、系统编程包含 文件的读写、和常用操作&#xff0c;操作系统已经进入多任务时代&#xff0c;在同一时刻同时运行多个程序。 二、标准io&#xff1b;stdio.h&#xff08;以计算机为中心&#xff09; 1.头文件路径&#xff1a;/usr/include/stdio.h so动态库&#xff1a;st…...

2.10 财务分析

10.1 财务报告构成及列报基本要求 10.1.1 财务报告 1.财务报告的构成 资产负债表、利润表、现金流量表、所有者权益变动表和附注小型企业可不编现金流量表。 2.财务报表及其作用 1.资产负债表的内容及其作用 内容 资产类、流动性大小顺序排序。流动资产、非流动资产负债和…...

docker容器知识

一、docker与docker compose区别&#xff1a; 1、docker是创建和管理单个容器的工具&#xff0c;适合简单的应用或服务&#xff1b; 2、docker compose是管理多容器应用的工具&#xff0c;适合复杂的、多服务的应用程序&#xff1b; 3、docker与docker compose对比&#xff…...

国标GB28181视频EasyGBS视频监控平台搭建城市交通道路可视化管理/道路视频巡检/应急监控指挥

一、方案背景​ 随着城市人口与车辆激增&#xff0c;交通管理面临严峻挑战&#xff1a;高峰期道路拥堵、事故处理滞后、违法取证低效&#xff0c;传统管理模式难以为继。智慧交通依托信息技术&#xff0c;成为破局关键&#xff0c;其中视频监控是实现精细化管理的核心。国标GB…...