性能优化的方向
说起性能优化,就得从 《从浏览器输入URL到页面渲染完成经历了哪些过程?》 这个问题。
基本总结下来就是分为两块 两部分: 加载层面 和 渲染层面
加载层面
就是项目文件的网络加载过程
加载层面就是网络在加载文件,核心要点就是快速加载
主要优化方向:
网络快
- DNS 策略选择:
- 使用可靠的DNS服务提供商,避免 用户所在区域因为区域问题 导致 DNS 查询失败
- 使用
dns-prefetch
/preconnect
,将域名解析的 IP 地址缓存到本地。
1 | <!-- 三方资源 DNS 预解析: --> |
- 使用CDN 加速,将文件放在离用户更近的地方。优先 使用
CDN 预热
- 使用HTTP2.0,并行加载多个资源,有效减少加载时间 「通过多路复用、头部压缩等技术,可以显著提高网络性能」
- 避免重定向
- 减少请求数量,避免进入等待队列「 此处需要权衡取舍,避免单个文件过大 导致加载缓慢」:
图片转化成base64
、精灵图 - 提高服务器性能
- 配置更高的服务器、使用SSD硬盘、增加服务器带宽
- 负载均衡:将用户请求分配到多台服务器,减少单台服务器的负载,Nginx。
文件小 [可能会导致HTTP请求过多, 注意平衡]
树摇-Tree Shaking
Webpack 5
- 默认支持 Tree Shaking,并在生产模式下自动启用。
- 使用
ES Module
语法(import
和export
),而不是CommonJS
的require
和module.exports
,以充分利用 Webpack 的静态分析功能。 - 在
package.json
中设置sideEffects
属性来标记模块是否存在副作用,这样可以更精确地优化。
Vite
Vite 基于 Rollup,默认支持 Tree Shaking,无需额外配置。
按需加载
使用的时候才加载 如: 路由懒加载、组件懒加载、 暂时不需要的文件动态导入
- 动态导入组件、路由懒加载: 使用 ES6 的
() => import()
语法动态导入路由组件。
1 | const LazyComponent = () => import( /* webpackChunkName: "lazy-component" */ './components/LazyComponent.vue'); |
import().then
动态加载 模块: 任何你觉得比较占体积的包或者文件 都可以进行 动态导入。
1 | import('./store/modules/largeModule').then(module => { |
- vue3 中的 动态组件 使用
defineAsyncComponent
进行异步组件加载
支持指定加载中组件和错误处理组件,以及相关的延时和超时设置。
1 | import { |
- Vue3 中的异步组件:
<Suspense>
组件 包裹异步组件,提供加载中和错误处理的功能。
1 | <template> |
- 借助 Aiosx 进行 对非必要 文件 进行动态加载, 如 多语言模块等
代码分割
Webpack 的 SplitChunks
Webpack 提供了代码分割的功能,可以自动将代码拆分为多个块,按需加载这些块
1 | // webpack.config.js |
vite中进行代码分割
- 基于 Rollup 的
manualChunks
1 | // vite.config.js |
- 基于
viteplugin-chunk-split
1 | // vite.config.ts |
动态垫片
通过垫片服务根据UserAgent返回当前浏览器代码垫片,好处是无需将繁重的代码垫片打包进去。
每次构建都配置@babel/preset-env和core-js根据某些需求将Polyfill打包进来,这无疑又为代码体积增加了贡献
压缩资源
服务开启 Gzip 压缩
对常用文本资源 开启Gzip:
- HTML 文件:text/html(nginx 服务器默认就会压缩)、application/xhtml+xml
- CSS 文件:text/css
- JS 文件:application/x-javascript、application/javascript、text/javascript
- JSON 文件(或者API请求结果):application/json、application/geo+json、application/ld+json application/manifest+json、application/x-web-app-manifest+json
- XML 文件:application/xml、application/atom+xml、application/rdf+xml、application/rss+xml
- SVG 文件:image/svg+xml;
- 前端借助构建工具,预先生成gz文件,
缺点是构打包后构建的产物体积会变大
,优点是不耗费服务器的性能。
- webpack安装
compression-webpack-plugin
- Vite 使用
vite-plugin-compression
来实现
1 | const CompressionPlugin = require('compression-webpack-plugin') |
- Nginx 服务器开启 Gzip 压缩
1 | //Nginx 服务器配置文件中 |
具体可参考启用 Gzip 压缩
最小化代码
去除冗余字符(例如不必要的注释、空格符和换行符等),减少文件大小。
- JavaScript 代码的最小化和压缩: Rollup.js 和 Webpack 中都得到了应用,以降低代码体积、减少下载时间。
- CSS,在 Webpack 中,通常会利用 mini-css-extract-plugin 插件进行优化。
压缩字体
- TTF 字体更换为 WOFF2,体积减小 近60%
- 字体文件 取子集 按需压缩:借助三方工具
MP4 视频 优化
编码视频选择 “为 web 优化” 或 “为串流优化”的选项,将名称为 moov
的特殊 atom
(包含 元数据信息)置于文件开头,让视频不必下载完成就可以播放,带来更好的用户体验
图像处理
- 使用图片压缩工具,将图片压缩到合适大小,减少请求次数。
- 选择适合的图片格式。图像格式及应用场景:
- SVG:用于 icon 和 logo,包含几何图形,无论缩放如何都保持清晰。
- JPEG:适用于摄影图片,通过有损和无损优化减小文件大小。(适用于非透明的 banner图)
- PNG:适用于高分辨率图片,无损压缩,而 WebP 整体上更小。
- WebP:适用于高分辨率图片,支持无损和有损压缩,文件大小更小。(注意兼容性问题)
- Video:对于动画,建议使用 video 而不是 GIF,因为 GIF 有颜色限制且文件大小较大。
- 远端图片控制:借助图床压缩服务,进行图片尺寸裁剪、压缩 等操作。
缓存类
利用http缓存
- 静态资源 优先 强缓存
- 同域公共资源提取,最大化利用 缓存共享
利用indexDB 缓存
利用indexDB 缓存 进行 图片、视频、音频等资源的缓存。如:地图项目常用于缓存瓦片数据。
特点:
- indexDB大小取决于你的硬盘,存储的数据量非常大。
- 可以直接存储任何类型的数据,如 js任何类型的数据 、blob流。
- 可以创建索引,提供高性能搜索功能。
- 采用事务,保证数据的准确性和一致性。
ServiceWorker
Service Worker 是一种在浏览器后台运行的脚本,它可以拦截网络请求,缓存资源,提供离线访问等功能。 Service Worker
借助了 cacheStorage
进行缓存
通常借助 workbox-webpack-plugin
进行配置。
1 | //webpack.config.js: |
或者直接使用 sw-precache-webpack-plugin
`workbox-build` 进行构建。
ServiceWorker 让你的网页拥抱服务端的能力
WorkBox 之底层逻辑Service Worker
使用 Service Worker 让首页秒开
前端更新部署后通知用户刷新
离线包
离线包个家实现方式各有不同,本质是要依赖 客户端完成。
相较于 ServiceWorker
方案,离线包 可以解决用户第一次访问 慢的问题。
货拉拉H5离线包原理与实践
离线包实现详解
Hybrid App 离线包方案实践
渲染层面
就是我们拿到文件后页面开始渲染并且交互的过程
渲染层面的配置就 落实到我们前端的具体技术细节上
阻塞策略:
位置放
底部,让JS不阻塞HTML和CSS的解析脚本与DOM/其它脚本的依赖关系很强:对
<script>设置defer
脚本与DOM/其它脚本的依赖关系不强:对 <script>设置async
CSS策略:
避免-多层的嵌套规则
避免-为ID选择器添加多余选择器
避免-使用通配选择器,只对目标节点声明规则
DOM策略:(减少回流重绘)
缓存DOM计算属性
避免频繁操作 DOM
使用display控制DOM显隐,将DOM离线化
修改DOM时把其包装成微任务
使用 transform 代替 top/left 修改位置
代码实践策略:
懒加载策略
图片-懒加载:非必要图片 延后加载
图片-渐进式处理:
- 使用响应式图像 (srcset 和 sizes 属性) 根据设备尺寸加载合适的图像
- 小图先出,大图后出 前端体验优化之渐进式图片
代码业务懒加载(路由、模块、 多语言)
视频 利用 Video标签的Preload 延后加载
- none: 表示视频不应预加载。
- metadata: 表示仅获取视频元数据(例如长度)。
- auto: 表示整个视频文件可以下载,即使用户预计不会使用它。
- 空字符串: auto 值的同义词。
1 | <video controls preload="none" poster="placeholder.jpg"> |
预加载策略
- 借助进行 预加载、预渲染、预获取
1 | <!-- 顾名思义,提前加载资源(未用到),首先要确定这个资源一定会在未来用到,然后提前加载,放入浏览器缓存中 --> |
- 使用 js 预加载资源,为后面页面、模块使用做准备
对于 UI 改动,推荐使用requestAnimationFrame
浏览器会在下次重绘时调用该方法,相较于 setInterval 或 setTimeout,它能够更智能地在浏览器的帧渲染中进行优化。
使用 setInterval 或 setTimeout 有可能导致回调在帧的某个点运行,可能在帧的末尾,这通常导致错过一帧,从而导致界面卡顿。
而 requestAnimationFrame 可以确保回调在浏览器准备好进行下一次重绘时执行,使得动画效果更加流畅。
避免长任务,代码优化 (执行时间超过 50 毫秒的任务)
- 使用 requestIdleCallback 空闲时间处理任务, 是一种优化手段 可在 主线程 空闲时调度执行低优先级或后台任务,以提高页面的响应性
- 利用 Promise.then 、queueMicrotask 微任务 进行包装,延后任务处理
- scheduler.postTask 允许以更细粒度的方式调度任务,并且是一种帮助浏览器确定任务优先级的方法,确保低优先级任务可以释放main thread的机制。尽管目前大多数浏览器并不全面支持,但可在这里获取详细信息。
- 使用 Web Workers 处理耗时任务【web worker 可以让主线程另起新的线程来运行脚本】
相关推荐:
如何减少卡顿的代码级别详细文章
7种在 JavaScript 中分解长任务的技术
一文彻底了解Web Worker,十万条数据都是弟弟
首页处理
- loding
- 骨架屏
- SSR ( 需要注意 html体积,尽量只预先渲染 首屏内容)
(一文彻底说清楚SSR渲染)[https://mp.weixin.qq.com/s/6pnp8F6j8MSxP7iPp_YepA]