工程化与框架系列(21)--前端性能优化(上)

前端性能优化(加载) 🚀

引言

页面加载性能直接影响用户体验和业务转化率。本文将深入探讨前端加载性能优化的各种策略和技术,包括资源加载优化、代码分割、预加载等关键主题,帮助开发者构建快速响应的Web应用。

加载性能概述

加载性能优化主要关注以下方面:

  • 资源加载:减少资源大小,优化加载顺序
  • 代码分割:按需加载,减少首屏加载时间
  • 缓存策略:合理利用浏览器缓存
  • 预加载技术:提前加载关键资源
  • 渲染优化:优化关键渲染路径

资源加载优化

资源压缩与合并

// webpack配置示例
const config = {optimization: {minimize: true,minimizer: [new TerserPlugin({terserOptions: {compress: {drop_console: true,drop_debugger: true}}}),new CssMinimizerPlugin()],splitChunks: {chunks: 'all',minSize: 20000,minRemainingSize: 0,minChunks: 1,maxAsyncRequests: 30,maxInitialRequests: 30,enforceSizeThreshold: 50000,cacheGroups: {defaultVendors: {test: /[\\/]node_modules[\\/]/,priority: -10,reuseExistingChunk: true},default: {minChunks: 2,priority: -20,reuseExistingChunk: true}}}}
};

图片优化

// 图片加载优化工具
class ImageOptimizer {// 图片懒加载static enableLazyLoading(): void {const images = document.querySelectorAll('img[data-src]');const imageObserver = new IntersectionObserver((entries, observer) => {entries.forEach(entry => {if (entry.isIntersecting) {const img = entry.target as HTMLImageElement;img.src = img.dataset.src!;img.removeAttribute('data-src');observer.unobserve(img);}});});images.forEach(img => imageObserver.observe(img));}// 响应式图片static setupResponsiveImages(): void {const images = document.querySelectorAll('img[data-srcset]');images.forEach(img => {const srcset = img.getAttribute('data-srcset');if (srcset) {img.srcset = srcset;}});}// 图片预加载static preloadImages(urls: string[]): void {urls.forEach(url => {const img = new Image();img.src = url;});}// WebP支持检测static async checkWebPSupport(): Promise<boolean> {try {const canvas = document.createElement('canvas');if (canvas.getContext && canvas.getContext('2d')) {return canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;}return false;} catch (e) {return false;}}
}// 使用示例
document.addEventListener('DOMContentLoaded', () => {ImageOptimizer.enableLazyLoading();ImageOptimizer.setupResponsiveImages();// 预加载关键图片ImageOptimizer.preloadImages(['/images/hero.jpg','/images/logo.png']);
});

代码分割与懒加载

路由级别代码分割

// React Router配置示例
import { lazy, Suspense } from 'react';const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Dashboard = lazy(() => import('./pages/Dashboard'));function App() {return (<Router><Suspense fallback={<Loading />}><Switch><Route exact path="/" component={Home} /><Route path="/about" component={About} /><Route path="/dashboard" component={Dashboard} /></Switch></Suspense></Router>);
}// 组件级别代码分割
const HeavyComponent = lazy(() => {return new Promise(resolve => {// 模拟延迟加载setTimeout(() => {resolve(import('./components/HeavyComponent'));}, 1000);});
});

动态导入

// 动态导入工具
class DynamicImporter {private loadedModules: Map<string, any> = new Map();// 动态加载模块async importModule(modulePath: string): Promise<any> {if (this.loadedModules.has(modulePath)) {return this.loadedModules.get(modulePath);}try {const module = await import(/* webpackChunkName: "[request]" */ modulePath);this.loadedModules.set(modulePath, module);return module;} catch (error) {console.error(`模块加载失败: ${modulePath}`, error);throw error;}}// 预加载模块preloadModule(modulePath: string): void {const link = document.createElement('link');link.rel = 'modulepreload';link.href = modulePath;document.head.appendChild(link);}// 清除已加载的模块clearModule(modulePath: string): void {this.loadedModules.delete(modulePath);}
}// 使用示例
const importer = new DynamicImporter();async function loadFeature() {const { default: Feature } = await importer.importModule('./features/Feature');return new Feature();
}

预加载策略

资源预加载

// 预加载管理器
class PreloadManager {private preloadedResources: Set<string> = new Set();// 预加载JavaScriptpreloadScript(url: string): void {if (this.preloadedResources.has(url)) return;const link = document.createElement('link');link.rel = 'preload';link.as = 'script';link.href = url;document.head.appendChild(link);this.preloadedResources.add(url);}// 预加载样式preloadStyle(url: string): void {if (this.preloadedResources.has(url)) return;const link = document.createElement('link');link.rel = 'preload';link.as = 'style';link.href = url;document.head.appendChild(link);this.preloadedResources.add(url);}// 预加载字体preloadFont(url: string, type: string): void {if (this.preloadedResources.has(url)) return;const link = document.createElement('link');link.rel = 'preload';link.as = 'font';link.href = url;link.type = type;link.crossOrigin = 'anonymous';document.head.appendChild(link);this.preloadedResources.add(url);}// 预连接preconnect(url: string): void {const link = document.createElement('link');link.rel = 'preconnect';link.href = url;document.head.appendChild(link);}
}// 使用示例
const preloader = new PreloadManager();// 预加载关键资源
preloader.preloadScript('/js/main.js');
preloader.preloadStyle('/css/critical.css');
preloader.preloadFont('/fonts/roboto.woff2', 'font/woff2');
preloader.preconnect('https://api.example.com');

数据预加载

// 数据预加载管理器
class DataPreloader {private cache: Map<string, any> = new Map();// 预加载API数据async preloadApiData(url: string, params?: object): Promise<void> {const cacheKey = this.generateCacheKey(url, params);if (this.cache.has(cacheKey)) return;try {const response = await fetch(url, {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(params)});const data = await response.json();this.cache.set(cacheKey, data);} catch (error) {console.error('数据预加载失败:', error);}}// 获取预加载的数据getPreloadedData(url: string, params?: object): any {const cacheKey = this.generateCacheKey(url, params);return this.cache.get(cacheKey);}// 生成缓存键private generateCacheKey(url: string, params?: object): string {return `${url}:${JSON.stringify(params || {})}`;}// 清除预加载的数据clearPreloadedData(url?: string): void {if (url) {const cacheKey = this.generateCacheKey(url);this.cache.delete(cacheKey);} else {this.cache.clear();}}
}// 使用示例
const dataPreloader = new DataPreloader();// 预加载用户数据
await dataPreloader.preloadApiData('/api/user-profile');// 使用预加载的数据
const userData = dataPreloader.getPreloadedData('/api/user-profile');

关键渲染路径优化

CSS优化

// 关键CSS提取工具
class CriticalCSSExtractor {private styles: Map<string, string> = new Map();// 提取关键CSSextractCriticalCSS(html: string): string {const criticalStyles = new Set<string>();// 分析DOM树const dom = new DOMParser().parseFromString(html, 'text/html');// 获取首屏元素const viewportElements = this.getViewportElements(dom);// 提取关键样式viewportElements.forEach(element => {const styles = this.getElementStyles(element);styles.forEach(style => criticalStyles.add(style));});return Array.from(criticalStyles).join('\n');}// 获取首屏元素private getViewportElements(dom: Document): Element[] {// 实现首屏元素检测逻辑return Array.from(dom.querySelectorAll('*'));}// 获取元素样式private getElementStyles(element: Element): string[] {// 实现样式提取逻辑return [];}// 内联关键CSSinlineCriticalCSS(html: string, criticalCSS: string): string {return html.replace('</head>',`<style id="critical-css">${criticalCSS}</style></head>`);}
}

JavaScript优化

// JavaScript加载优化
class ScriptLoader {// 异步加载脚本static loadAsync(url: string): Promise<void> {return new Promise((resolve, reject) => {const script = document.createElement('script');script.src = url;script.async = true;script.onload = () => resolve();script.onerror = () => reject(new Error(`Script load error: ${url}`));document.head.appendChild(script);});}// 延迟加载脚本static loadDeferred(url: string): void {const script = document.createElement('script');script.src = url;script.defer = true;document.head.appendChild(script);}// 按需加载脚本static loadOnDemand(url: string, condition: () => boolean): void {if (condition()) {this.loadAsync(url);}}
}// 使用示例
ScriptLoader.loadAsync('/js/analytics.js');
ScriptLoader.loadDeferred('/js/comments.js');
ScriptLoader.loadOnDemand('/js/chat.js', () => {return localStorage.getItem('userLoggedIn') === 'true';
});

性能监控

性能指标监控

// 性能监控工具
class PerformanceMonitor {private metrics: Map<string, number> = new Map();// 记录性能指标recordMetric(name: string, value: number): void {this.metrics.set(name, value);}// 获取关键指标getMetrics(): object {const entries = performance.getEntriesByType('navigation')[0] as PerformanceNavigationTiming;return {// 首次内容绘制FCP: this.getFCP(),// DOM内容加载完成DCL: entries.domContentLoadedEventEnd - entries.domContentLoadedEventStart,// 页面完全加载loadComplete: entries.loadEventEnd - entries.navigationStart,// 首字节时间TTFB: entries.responseStart - entries.requestStart,// 自定义指标custom: Object.fromEntries(this.metrics)};}// 获取首次内容绘制时间private getFCP(): number {const fcp = performance.getEntriesByName('first-contentful-paint')[0];return fcp ? fcp.startTime : 0;}// 发送性能数据async sendMetrics(): Promise<void> {const metrics = this.getMetrics();try {await fetch('/api/performance', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify(metrics)});} catch (error) {console.error('性能数据发送失败:', error);}}
}// 使用示例
const monitor = new PerformanceMonitor();// 记录自定义指标
monitor.recordMetric('componentLoad', performance.now());// 页面加载完成后发送性能数据
window.addEventListener('load', () => {setTimeout(() => {monitor.sendMetrics();}, 0);
});

最佳实践与建议

  1. 资源优化

    • 压缩和合并资源文件
    • 使用适当的图片格式
    • 实现懒加载策略
  2. 代码优化

    • 实施代码分割
    • 移除未使用的代码
    • 优化第三方依赖
  3. 加载策略

    • 优先加载关键资源
    • 实现预加载机制
    • 使用适当的缓存策略
  4. 监控与分析

    • 监控关键性能指标
    • 进行性能分析
    • 持续优化改进

总结

前端加载性能优化是一个持续的过程,需要从多个层面进行优化:

  1. 减少资源体积和请求数
  2. 优化资源加载顺序
  3. 实现智能的预加载策略
  4. 优化关键渲染路径
  5. 建立有效的性能监控

通过合理运用这些优化策略,可以显著提升Web应用的加载性能,为用户提供更好的体验。

学习资源

  1. Web Vitals指南
  2. Chrome DevTools性能分析
  3. webpack优化指南
  4. 图片优化最佳实践
  5. 性能监控工具文档

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

终身学习,共同成长。

咱们下一期见

💻

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/31050.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

本地搭建DeepSeek R1模型 + 前端

本地搭建DeepSeek R1模型 前端 配置&#xff1a; 操作系统&#xff1a;windows11 CPU&#xff1a;i5 13600KF GPU&#xff1a;英伟达4070 12GB 内存&#xff1a;16G DDR5 硬盘&#xff1a;1TB 模型安装 本文采用Ollama进行安装。Ollama安装比较简单。 官网&#xff1…

[项目]基于FreeRTOS的STM32四轴飞行器: 五.Motor驱动

基于FreeRTOS的STM32四轴飞行器: 五.Motor驱动 一.配置CubeMX二.Motor驱动3.bug修改 一.配置CubeMX 观察motor原理图引脚对应的TIM&#xff1a; 使用内部时钟&#xff0c;配置4分频&#xff0c;后为18M&#xff0c;在设置Counter Period为1000-1&#xff0c;之后频率为18K&am…

Electron-Forge + Vue3 项目初始化

本人对Electron的浅薄理解如下图所示 由上图可以&#xff0c;如果你需要开发一个electron应用&#xff0c;你得具备基本的前端开发经验。对于electron相关的知识&#xff0c;建议先了解下基本的窗口操作&#xff0c;比如新建窗口、关闭窗口等简单的操作&#xff0c;这些内容在…

神经网络为什么要用 ReLU 增加非线性?

在神经网络中使用 ReLU&#xff08;Rectified Linear Unit&#xff09; 作为激活函数的主要目的是引入非线性&#xff0c;这是神经网络能够学习复杂模式和解决非线性问题的关键。 1. 为什么需要非线性&#xff1f; 1.1 线性模型的局限性 如果神经网络只使用线性激活函数&…

手写Tomcat:实现基本功能

首先&#xff0c;Tomcat是一个软件&#xff0c;所有的项目都能在Tomcat上加载运行&#xff0c;Tomcat最核心的就是Servlet集合&#xff0c;本身就是HashMap。Tomcat需要支持Servlet&#xff0c;所以有servlet底层的资源&#xff1a;HttpServlet抽象类、HttpRequest和HttpRespon…

PyTorch系列教程:编写高效模型训练流程

当使用PyTorch开发机器学习模型时&#xff0c;建立一个有效的训练循环是至关重要的。这个过程包括组织和执行对数据、参数和计算资源的操作序列。让我们深入了解关键组件&#xff0c;并演示如何构建一个精细的训练循环流程&#xff0c;有效地处理数据处理&#xff0c;向前和向后…

Linux系统基于ARM平台的LVGL移植

软硬件介绍&#xff1a;Ubuntu 20.04 ARM 和&#xff08;Cortex-A53架构&#xff09;开发板 基本原理 LVGL图形库是支持使用Linux系统的Framebuffer帧缓冲设备实现的&#xff0c;如果想要实现在ARM开发板上运行LVGL图形库&#xff0c;那么就需要把LVGL图形库提供的关于帧缓冲设…

Consensus 大会全观察:政策、生态与技术交汇,香港能否抢占 Web3 先机?

被誉为 “区块链界超级碗” 和 “Web3 世界杯” 的全球顶级行业峰会 —— Consensus 大会&#xff0c;在诞生十年之际首次跨越太平洋登陆亚洲&#xff0c;于 2025 年 2 月 18 日至 20 日在香港会议展览中心盛大启幕。大会汇聚了亚洲主要金融政策制定者、加密领域思想领袖、投资…

hadoop集群环境配置

目录 VMware虚拟机安装 Xshell安装 网络问题 centos7下载 ---------参考以下视频步骤进行生态搭建---------- 搭建好hadoop01 克隆出hadoop02、hadoop03 启动三台虚拟机 打开终端 输入 记录下各个ip 打开Xshell&#xff0c;新建会话 修改主机名 配置静态IP 主机名称…

C++之list

list是链表的意思&#xff0c;由一个个节点组成 一、基本接口使用&#xff1a; &#xff08;1&#xff09;与vector相同&#xff0c;有个尾插&#xff0c;也可以使用迭代器遍历&#xff1a; void test_list1() {list<int> lt;lt.push_back(1);lt.push_back(2);lt.push…

MWC 2025 | 紫光展锐联合移远通信推出全面支持R16特性的5G模组RG620UA-EU

2025年世界移动通信大会&#xff08;MWC 2025&#xff09;期间&#xff0c;紫光展锐联合移远通信&#xff0c;正式发布了全面支持5G R16特性的模组RG620UA-EU&#xff0c;以强大的灵活性和便捷性赋能产业。 展锐芯加持&#xff0c;关键性能优异 RG620UA-EU模组基于紫光展锐V62…

vue2设置横向滚动指令

图片横向滑动展示效果 创建directives.js文件 // 横向列表拖拽 const draggleScrollX {inserted(el, binding) {let isDragging false;let startX 0;let scrollLeft 0;el.classList.add("draggle-horizontal");// 添加监听事件-鼠标按下const onMouseDown (eve…

城市霓虹灯夜景拍照后期Lr调色教程,手机滤镜PS+Lightroom预设下载!

调色教程 在城市霓虹灯夜景拍摄中&#xff0c;由于现场光线复杂等因素&#xff0c;照片可能无法完全呈现出当时的视觉感受。通过 Lr 调色&#xff0c;可以弥补拍摄时的不足。例如&#xff0c;运用基本调整面板中的曝光、对比度、阴影等工具&#xff0c;可以处理出画面的整体明暗…

20250307确认荣品PRO-RK3566开发板在Android13下的以太网络共享功能

20250307确认荣品PRO-RK3566开发板在Android13下的以太网络共享功能 2025/3/7 13:56 缘起&#xff1a;我司地面站需要实现“太网络共享功能”功能。电脑PC要像连接WIFI热点一样连接在Android设备/平板电脑上来实现上网功能/数据传输。 Android设备/平板电脑通过4G/WIFI来上网。…

清华北大推出的 DeepSeek 教程(附 PDF 下载链接)

清华和北大分别都有关于DeepSeek的分享文档&#xff0c;内容非常全面&#xff0c;从原理和具体的应用&#xff0c;大家可以认真看看。 北大 DeepSeek 系列 1&#xff1a;提示词工程和落地场景.pdf  北大 DeepSeek 系列 2&#xff1a;DeepSeek 与 AIGC 应用.pdf  清华 Deep…

MYSQL之创建数据库和表

创建数据库db_ck &#xff08;下面的创建是最好的创建方法&#xff0c;如果数据库存在也不会报错&#xff0c;并且指定使用utf8mb4&#xff09; show databases命令可以查看所有的数据库名&#xff0c;可以找到刚刚创建的db_ck数据库 使用该数据库时&#xff0c;发现里面没有…

用Python写一个算24点的小程序

一、运行界面 二、显示答案——递归介绍 工作流程&#xff1a; 1. 基本情况&#xff1a;函数首先检查输入的数字列表 nums 的长度。如果列表中只剩下一个数字&#xff0c;它会判断这个数字是否接近 24&#xff08;使用 abs(nums[0] - 24) < 1e-10 来处理浮点数精度问题&…

每天五分钟深度学习框架PyTorch:使用残差块快速搭建ResNet网络

本文重点 前面我们使用pytorch搭建了残差块&#xff0c;本文我们更进一步&#xff0c;我们使用残差块搭建ResNet网络&#xff0c;当学会如何搭建残差块之后&#xff0c;搭建ResNet就会非常简单了&#xff0c;因为ResNet就是由多个残差块组成的。 残差块 残差块我们前面已经介…

Jenkins学习笔记

文章目录 一、Jenkins简介二、Jenkins的安装1. 安装前准备2. 安装Jenkins3. 启动Jenkins 三、Jenkins的配置1. 初始配置2. 全局工具配置3. 插件安装 四、Jenkins的使用1. 创建新任务2. 配置任务3. 触发构建4. 查看构建结果 五、Jenkins的高级功能1. 分布式构建2. 流水线&#x…

Android MXPlayer-v1.86.0-wushidi专业版[原团队最后一个版本]

MXPlayer 链接&#xff1a;https://pan.xunlei.com/s/VOKiDeDUxTDbJNN7yRAZjW8HA1?pwd4bzc# MX Video Player视频播放器是一款安卓最优秀的媒体播放器软件&#xff0c;它能够播放几乎每一个影片档案&#xff0c;并且具备多核心的译码能力来处理你的影片档案和字幕。多核译码…