# 前端性能优化方案
# 1. 减少耗时
# 1.1 缓存策略
合理利用缓存可以显著减少资源加载时间:
- 浏览器缓存:通过设置 Expires 和 Cache-Control 实现强缓存
- 协商缓存:通过设置 Last-Modified/If-Modified-Since 和 ETag/If-None-Match 实现
- 服务端缓存:利用Redis、Memcached等技术缓存计算结果
- CDN缓存:将静态资源分发到全球节点,提升用户访问速度
- DNS缓存:预解析(dns-prefetch)、本地、运营商DNS查询缓存
# 1.2 减少请求耗时
从请求次数和请求大小两个维度优化:
减少请求次数:
- 合并资源(CSS Sprites、JS/CSS文件合并)
- 使用缓存避免重复请求
- 服务端开启Gzip压缩
- 使用HTTP/2多路复用技术
减少请求大小:
- 开启Gzip/Brotli压缩
- 使用更高效的图片格式(WebP、AVIF)
- Tree-shaking清除未使用代码
- 代码压缩和混淆
# 2. 减少资源大小
# 2.1 Gzip/Brotli压缩
- 服务端开启Gzip/Brotli压缩可以有效减少传输资源的大小 40%
- webpack插件:compression-webpack-plugin
- Nginx配置示例:
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
1
2
2
# 2.2 Tree-shaking
Tree-shaking 是一种通过清除多余代码方式来优化项目打包体积的技术
基于ES6的静态引用,Tree-shaking通过扫描所有ES6的export,找出被import的内容并添加到最终代码中
webpack4中只需要将mode设置为production就会默认启动Tree-shaking
需要注意:只有ES6模块语法才能被Tree-shaking优化,CommonJS模块无法被优化
打包 针对图片进行压缩
打包 按需加载,减少无用包的引入
减小第三方库的大小,如Moment.js/lodash等,使用轻量级别替代方案或者自己重新实现 dayjs
# 3. CSS优化
# 3.1 避免使用CSS表达式
CSS表达式会频繁执行,影响页面性能:
/* bad */
background-color: expression((new Date()).getHours()%2?"#fff":"#000");
/* good */
background-color: #fff;
1
2
3
4
5
2
3
4
5
# 3.2 使用link代替@import
- link属于HTML标签,@import属于CSS范畴
- link最大限度支持并行下载,@import会使得页面在加载时增加额外的延迟
- link可以通过rel="alternate stylesheet"指定候选样式
- 浏览器对link支持早于@import
# 3.3 CSS文件优化
- 压缩CSS文件:移除空格、注释和不必要的字符
- 合并CSS文件:减少HTTP请求数量
- 使用CSS Sprites:合并小图标图片
- 避免CSS选择器嵌套过深:减少CSS引擎查找元素的时间(读取选择器,遵循从右到左读取。避免通配符,最大化样式继承,少用标签选择器,减少过深嵌套)
- 使用CSS3硬件加速:利用GPU提升动画性能
- 使用Flex布局:使用新版的 flex 进行布局比我们用的一些“老式”方法性能更好
# 4. JavaScript优化
# 4.1 JS文件位置
- JS会阻塞页面渲染,将JS文件放在body底部可以优先渲染页面内容
- 使用async/defer属性异步加载脚本:
async:立即下载脚本,下载完立即执行(不保证执行顺序)defer:立即下载脚本,等待DOM解析完成后按顺序执行
# 4.2 外部JS文件
- 外部JS文件可以被浏览器缓存,减少重复下载
- 外部JS文件可以被多个页面共享
- 合理拆分JS文件,避免单个文件过大
# 4.3 减少DOM访问
- 缓存DOM元素:重复访问的DOM元素应该缓存到局部变量中
- 减少DOM操作:合并多次DOM操作,减少重排和重绘
- 使用DocumentFragment批量操作DOM
- 避免在循环中操作DOM
# 4.4 代码优化
- 变量优化:使用局部变量而非全局变量,减少作用域链查找
- 循环优化:减少循环内重复计算,使用缓存变量
- 事件优化:使用事件委托减少事件监听器数量
- 防抖节流:对频繁触发的事件使用防抖节流优化
# 5. 图片优化
# 5.1 压缩图片
- 使用工具压缩图片,减少图片大小
- 工具:tinypng、imagemin-webpack-plugin、squoosh
- 在线工具:TinyPNG、智图
# 5.2 使用CSS Sprites
- 将多个小图片合并成一张大图片,减少HTTP请求次数
- 注意维护成本,适用于不经常变动的小图标
# 5.3 使用更高效的图片格式
- WebP格式:相比JPEG和PNG,WebP可以提供更好的压缩率和质量
- AVIF格式:新一代图像格式,压缩率比WebP更好
- SVG格式:对于简单的图标和图形,SVG可以提供无损缩放和较小的文件大小
# 5.4 图片懒加载
- 对于页面中不需要立即显示的图片,可以使用懒加载技术,当用户滚动到图片位置时再加载图片
- 实现原理:监听滚动事件,判断图片是否进入可视区域,如果进入则加载图片
- 现代浏览器原生支持:使用
loading="lazy"属性
# 5.5 响应式图片
- 使用
<picture>元素和srcset属性实现响应式图片 - 根据设备屏幕密度和尺寸加载不同分辨率的图片
- 代码示例:
<picture> <source media="(min-width: 768px)" srcset="large.jpg"> <source media="(min-width: 480px)" srcset="medium.jpg"> <img src="small.jpg" alt="响应式图片"> </picture>1
2
3
4
5
# 6. DOM优化
# 6.1 合并修改
- 合并多次DOM修改,减少重排和重绘
- 使用DocumentFragment批量操作DOM
- 使用CSS类名批量修改样式,而非逐个修改style属性
- 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来
# 6.2 虚拟列表
- 对于大量数据的列表,可以使用虚拟列表技术,只渲染可视区域的列表项,减少DOM节点数量
- 常用库:react-window、react-virtualized、vue-virtual-scroll-list
# 6.3 事件委托
- 利用事件冒泡机制,将事件监听器绑定到父元素上,减少事件监听器数量
- 适用于动态添加的元素,无需为每个元素单独绑定事件
- 使用函数节流(throttle)或函数防抖(debounce),限制某一个方法的频繁触发
# 6.4 其他DOM优化技巧
- 减少DOM节点数量:避免不必要的嵌套和冗余元素
- 优化CSS选择器:避免使用低效的选择器(如属性选择器、通配符选择器)
- 使用requestAnimationFrame:将DOM操作推迟到下一帧执行,避免阻塞渲染
- 避免强制同步布局:不要在修改DOM后立即读取布局属性
- CSS是阻塞的资源: 尽早(CSS放在head里)和尽快(启用CDN)。浏览器在构建CSSOM的过程中,不会渲染任何已处理的内容,即便DOM已经解析完毕了,防止浏览器有可能还未下载和解析到CSS就已经开始渲染页面
- JS阻塞: 加载或者执行JS时会阻塞对标签的解析,也就是阻塞了DOM 树的形成,只有等到JS执行完毕,浏览器才会继续解析标签(因为加载的 JS 中可能会创建,删除节点)
- defer并行下载: 不会阻塞 HTML 解析,等到 DOM 生成完毕且 script 加载完毕再执行 JS
- **async并行下载:表示异步执行引入的 JS,加载时不会阻塞 HTML解析,但是加载完成后立马执行,此时仍然会阻塞 load 事件
| 标签 | js执行顺序 | 阻塞解析HTML |
|---|---|---|
| scipt | 在HTML中的顺序 | 阻塞 |
| scipt defer | 在HTML中的顺序 | 不阻塞解析 |
| scipt async | 网络请求返回顺序 | 可能阻塞,也可能不阻塞,取决于什么时候下载完成 |
# 7. 浏览器渲染优化
# 7.1 关键渲染路径优化
- 减少关键资源数量:内联关键CSS,延迟加载非关键资源
- 优化关键资源大小:压缩CSS、JavaScript和HTML
- 减少关键路径长度:减少资源请求的依赖链
# 7.2 CSS优化
- 内联关键CSS:将首屏渲染必需的CSS内联到HTML中
- 避免CSS阻塞渲染:使用media属性标记非关键CSS
<link rel="stylesheet" href="non-critical.css" media="print" onload="this.media='all'">1 - 避免CSS选择器嵌套过深:减少CSS引擎查找元素的时间
# 7.3 图片优化
- 图片懒加载:使用loading="lazy"属性延迟加载非关键图片
# 8. 其他优化
# 8.1 使用Web Worker
- 将耗时的计算任务放到web worker中执行,避免阻塞主线程
- 适用于图像处理、大数据计算等场景
- 注意:web worker中无法访问DOM,只能通过postMessage通信
# 8.2 使用骨架屏
- 在页面内容加载完成前,显示骨架屏,提升用户体验
- 骨架屏应该与实际内容布局保持一致
- 可以使用CSS动画实现加载效果
# 8.3 服务端渲染(SSR)
- 使用服务端渲染可以减少首屏渲染时间,提升SEO效果
- 常用框架:Next.js、Nuxt.js
- 注意:会增加服务端压力,需要权衡利弊
# 8.4 预加载和预渲染
- DNS预解析:提前解析域名,减少DNS查询时间
- 预加载关键资源:使用
<link rel="preload">提前加载关键资源 - 预渲染:提前渲染页面内容,减少白屏时间
# 8.5 PWA优化
- 使用Service Worker实现离线缓存
- 添加Web App Manifest实现添加到主屏幕功能
- 使用Push API实现消息推送
浏览器内核 →