本文仅论述优化的手段,不涉及执行这些操作的根因,后续空闲时在整理"为什么"。
一、性能指标
指标有很多,比如 lighthouse 中使用的指标有 FCP、LCP、TBT、CLS、Speed Index,这些是最终的性能评估指标。
性能优化方案落地时,主要参看的指标:
指标 | 名词解释 | 描述 |
---|---|---|
DCL [DOMContentLoaded] | DOM 解析结束时间 | HTML 文档被解析成 DOM 树并且所有的标签都已经可用,但是外部资源(如图像、样式表等)可能还没有加载完成。 |
FP [First Pain] | 首次绘制 | 浏览器首次将像素渲染到屏幕上的时间点,即页面第一次有可见的内容呈现给用户。 |
FCP [First Content Pain] | 首次内容绘制 | 浏览器首次将第一个有意义的内容(如文本、图像、SVG 等)呈现到屏幕上的时间点。 |
LOAD | 加载结束时间 | 在整个页面及其所有资源(包括图像、样式表、脚本等)加载完成后触发。 |
(1)指标对应的值的获取
通过 Chrome 的 Performance 面板,可以看到大部分的值,这里说下如何通过 API 获取值。
1.1 DCL
js
console.time("DCL");
document.addEventListener("DOMContentLoaded", function (event) {
console.timeEnd("DCL");
});
1.2 FP 与 FCP
js
const [fp, fcp] = performance.getEntriesByType("paint");
const fpDuration = fp.startTime;
const fcpDuration = fcp.startTime;
1.3 Load
js
const entry = performance.getEntries()[0];
const loadDuration = entry.duration;
二、资源模块
针对静态资源保持一个大体的优化思路,即在当前优先处理与当前页面相关的资源。
(1)HTML 资源
下载过程中没有相关的处理
(2)CSS 资源
2.1 减少字节
- CSS 简写。属性能够简写的,不要分开写
- CSS 浅选择器。去除非必要的、详细的选择器,这是一个累活。
2.2 移除未使用的 CSS
一个复杂项目中可能会加载很多的 css 样式表,有些样式表不一定会被使用到,可以将这部分的样式去除。相关工具:
- uncss
2.3 启用 GPU 渲染
针对变换、动画等场景,可以启用 GPU 渲染。方式:
- will-change
2.4 @import 问题
避免@import,其弊端:
- 阻塞页面加载。当使用@import 导入外部样式表时,浏览器会在解析到@import 规则时暂停页面渲染,并立即请求导入的样式表
- 顺序依赖性。@import 导入的样式表必须在主样式表中等待导入样式表加载完毕后才能应用。
- 额外的 HTTP 请求。每次使用@import 导入样式表时,浏览器都需要发起额外的 HTTP 请求来获取该样式表
- 无法并行加载。由于@import 导入的样式表必须在主样式表中等待加载完毕后才能继续渲染,因此无法与主样式表并行加载。
(3)JS 资源
3.1 资源处理
压缩
压缩资源
,将多余的换行符、多余的打印等去除;
丑化
丑化资源
,将代码中变量名、函数名等替换为简短的、难以阅读的代码。
分包
当个 JS 文件交大时,尝试执行分包处理。主流的构建工具都包含对应的处理方式:
- webpack
- dll。将第三方库按照一个的规则抽成一个或多个文件。
- optimization
- externals。第三方库,以 umd 的方式引入工程。打包时不参与打包
- splitChunks。按照一定的规则将包模块进行拆分。
- rollup
- manualChunks。按照一定的规则将包模块进行拆分。
3.2 资源下载
- async。异步加载资源,下载完成后立即执行脚本
- defer。异步加载资源,在 load 事件时执行脚本
- preload。提前加载资源
- prefetch。空闲时加载下一个页面需要使用的资源
(4)图片资源
4.1 图标
有段时间,雪碧图是一个很流行的图标整合方式。到了现在这个阶段,字体图标是一个更好的方案。比如:阿里字体图
4.2 格式选择
常用的图片格式有 png、jpg、svg、webp。图片可以按照多个维度进行划分,如下:
- 无损图和有损图
- 光栅图和矢量图
WebP
有 google 开发,旨在提供更高的压缩率和更高的图像质量,以替代传统的 JPEG、PNG 和 GIF 格式。WebP
格式的特性:
- 支持有损压缩和无损压缩
- 支持透明色
- 支持动态图
- 压缩率更高
推荐使用WebP
格式,主流浏览器厂商都已支持。
4.3 回退写法
为了保险起见,可以采用<picture>
标签的回退语法确保正常显示。
html
<picture>
<source srcset="image.webp" type="image/webp" />
<source srcset="image.jpg" type="image/jpeg" />
<img src="image.jpg" alt="图像" />
</picture>
三、预加载
预加载分为几种类型,如:
(1)网络处理
dns-prefetch
浏览器在用户浏览页面时,在后台运行 DNS 的解析
html
<link rel="dns-prefetch" href="//fonts.googleapis.com" />
preconnect
允许浏览器在一个 HTTP 请求正式发给服务器前预先执行一些操作,这包括 DNS 解析,TLS 协商,TCP 握手,这消除了往返延迟并为用户节省了时间。
html
<link href="https://cdn.domain.com" rel="preconnect" crossorigin />
(2)资源预加载
在浏览器空闲时,加载站点将来可能用到的资源。
html
<!-- 图片 -->
<link rel="prefetch" href="/uploads/images/pic.png" />
<!-- 样式表 -->
<link rel="preload" href="styles.css" as="style" />
<!-- 脚本 -->
<link rel="preload" href="jQuery.js" as="javascript" />
四、懒加载
(1)图片懒加载
当资源出现在可视区时,再加载资源。实现方式,基于IntersectionObserve
+ src 替换
的思路实现。
(2)路由懒加载
现代工程,大多是 SPA 工程,路由是有前端控制,当路由变化时再加载对应的资源很有必要。
2.1 Vue 工程
js
const Home = () => import("./components/Home.vue");
const About = () => import("./components/About.vue");
const routes = [
{ path: "/", component: Home },
{ path: "/about", component: About },
];
const router = new VueRouter({
routes,
});
2.2 React 工程
js
const Home = React.lazy(() => import("./components/Home"));
const About = React.lazy(() => import("./components/About"));
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</Switch>
</Suspense>
</Router>
);
}
export default App;
五、网络模块
(1)CDN
启用 CDN 网络,减少资源的加载时长。比如:腾讯桶
(2)压缩
请求传输的压缩算法,常用的有:
- GZIP。一种常用的压缩算法,它基于 DEFLATE 算法
- Brotli。由谷歌开发,与 GZIP 和 Deflate 相比,Brotli 具有更高的压缩率。
备注:
存在无损的 PNG 资源时,不要启用 GZIP。否则会起到反效果,导致压缩后的文件更大
(3)HTTP
升级为 HTTP/2,好处:
- 提供成本更低的请求
- 一套新的传输体系。连接->流->消息->帧
- HTTP/2,一个连接,处理多个请求
- 头部压缩。
- 采用索引的思路,对相同的 header 值只用引用
- 服务器推送
六、总结
性能优化,名词高大上,真正落地时会发现都是些基本功。
方法很简单,重要的是要知道为什么要这么做,空闲时再聊这块。