资源优化
图片懒加载、按需引入与 Web Vitals 监控。见 HTML 图片、CSS 字体优化。
资源优化聚焦于减少网络传输体积、延迟非关键资源加载以及监控真实性能,从而提升页面加载速度和用户体验。
一、图片懒加载:配合 IntersectionObserver 的 Vue 指令
图片懒加载是一种优化首屏加载的常用手段:只有当图片进入视口(或视口附近)时才加载真实图片,避免一次性加载大量不可见图。
1. 原生实现:IntersectionObserver
IntersectionObserver 可以高效监听元素与视口的交叉状态,相比 scroll 事件 + getBoundingClientRect,它由浏览器底层异步处理,性能更优。
基础用法:
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
const src = img.dataset.src;
if (src) {
img.src = src;
img.removeAttribute("data-src");
}
observer.unobserve(img);
}
});
},
{ rootMargin: "0px 0px 100px 0px" },
); // 提前 100px 开始加载
document.querySelectorAll("img[data-src]").forEach((img) => observer.observe(img));
2. 封装为 Vue 指令(v-lazy)
为了方便复用,可以封装一个自定义指令,支持动态绑定、响应式更新。
// directives/lazy.js
export default {
mounted(el, binding) {
// 用 data-src 存储真实图片地址
el.dataset.src = binding.value;
const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
el.src = el.dataset.src;
delete el.dataset.src;
observer.unobserve(el);
}
},
{ rootMargin: "0px 0px 100px 0px" },
);
observer.observe(el);
// 存储 observer 以便 unmounted 时断开
el._lazyObserver = observer;
},
unmounted(el) {
if (el._lazyObserver) {
el._lazyObserver.disconnect();
delete el._lazyObserver;
}
},
};
在组件中使用:
<template>
<img v-lazy="imageUrl" alt="懒加载图片" />
</template>
3. 结合 Vue 生态的现成方案
- vue-lazyload:Vue 2 经典,Vue 3 有
vue-lazyload-next。 - v-lazy-image 等轻量库。
对于背景图片懒加载,可设置 data-bg,在 IntersectionObserver 中修改 el.style.backgroundImage。
二、第三方库按需引入与 Tree Shaking
使用第三方库时,若全量引入会显著增加打包体积。按需引入 + Tree Shaking 可以只打包被使用的代码。
1. 使用按需导入的库(如 lodash-es)
- 推荐使用
lodash-es(ES 模块版本),而非lodash(CommonJS)。因为lodash-es支持 Tree Shaking。
// ❌ 全量引入 lodash
import _ from 'lodash';
_.debounce(...);
// ✅ 按需引入
import debounce from 'lodash-es/debounce';
// 或
import { debounce } from 'lodash-es';
确保构建工具(Webpack、Vite)在生产模式开启 sideEffects 优化。Vite 默认支持,Webpack 需要 mode: 'production' 或显式配置 optimization.usedExports。
2. UI 组件库按需引入( element-plus 为例)
大多数现代 UI 库都提供按需引入方式。例如 element-plus 推荐使用 unplugin-auto-import 和 unplugin-vue-components 插件自动按需引入。
手动按需引入:
import { ElButton, ElMessage } from "element-plus";
// 单独引入样式(组件库通常提供按需样式的插件)
import "element-plus/es/components/button/style/css";
使用插件自动按需(推荐):
// vite.config.js
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
export default {
plugins: [
AutoImport({ resolvers: [ElementPlusResolver()] }),
Components({ resolvers: [ElementPlusResolver()] }),
],
};
这样,模板中使用的 <el-button> 会自动引入对应组件和样式,且支持 Tree Shaking。
3. 确保库支持 Tree Shaking 的条件
- 库使用 ES 模块(ESM)格式,且
package.json中"sideEffects": false或明确声明。 - 避免只在
index.js中集中导出(二级导入通常可避免全量加载)。 - 使用构建工具的
optimization选项(Webpack)或 Vite 默认配置。
4. 工具排查未使用的依赖
webpack-bundle-analyzer、rollup-plugin-visualizer、vite-bundle-visualizer生成体积报告,找出全量引入的库。
三、首屏性能监控:Lighthouse & Web Vitals
性能监控是优化的前提,通过定量指标了解真实用户体验。
1. Lighthouse(实验室工具)
Lighthouse 是 Google 出品的高仿真性能审计工具,可在 Chrome DevTools、PageSpeed Insights 或 CLI 中使用。它给出综合评分(性能、可访问性、SEO 等)并提供具体优化建议。
核心关注指标(与 Web Vitals 重叠):
- FCP(First Contentful Paint):首次内容绘制时间。
- LCP(Largest Contentful Paint):最大内容绘制,衡量加载性能。
- TBT(Total Blocking Time):总阻塞时间,交互准备度。
- CLS(Cumulative Layout Shift):累计布局偏移,视觉稳定性。
- Speed Index:速度指数。
分析流程:
- 运行 Lighthouse(DevTools → Lighthouse 面板,选择 Mobile 或 Desktop)。
- 查看性能评分和 “Opportunities” 列表(例如 “减少未使用的 JavaScript”、 “适当调整图片大小”)。
- 按建议优化后重新测试。
2. Web Vitals(真实用户监控)
Web Vitals 是一组由 Google 定义的、衡量真实用户体验的核心指标,通常通过 现场数据(CrUX 报告)或 实验室数据(Lighthouse)获取。
| 指标 | 含义 | 良好阈值 (75分位) |
|---|---|---|
| LCP | 最大内容元素渲染完成时间 | ≤ 2.5秒 |
| INP (取代 FID) | 交互延迟(Next Paint),衡量响应性 | ≤ 200毫秒 |
| CLS | 页面布局意外偏移程度 | ≤ 0.1 |
监控方式:
- Chrome 用户体验报告 (CrUX):基于真实 Chrome 用户的匿名数据,可通过 PageSpeed Insights 查看域名总体表现。
- JavaScript API:使用
web-vitals库收集并上报。
import { onLCP, onINP, onCLS } from "web-vitals";
onLCP(console.log);
onINP(console.log);
onCLS(console.log);
3. 基于指标的优化策略
| 差指标 | 常见原因 | 优化策略 |
|---|---|---|
| LCP 慢 | 主资源(hero image)加载慢,首屏阻塞 | 优化图片(WebP、响应式)、预加载 LCP 元素、CDN、服务器端渲染(SSR) |
| INP 差 | 长任务阻塞主线程、过多输入延迟 | 拆分长任务(使用 setTimeout 或 scheduler.yield)、避免繁重的 JS 执行、使用 Web Worker |
| CLS 高 | 图片/iframe 无尺寸占位、动态注入内容 | 为所有媒体元素设置 width/height 或 CSS aspect-ratio;预留广告位;字体使用 font-display: optional 减少 FOIT |
| TBT 高 | 大量未使用/未拆分的 JS | 代码分割、懒加载非关键脚本、减少 polyfill |
4. 性能预算
设定性能预算(比如 LCP < 2s, JS 总体积 < 200KB),并在 CI 中集成 Lighthouse CI 或 lighthouse-bot,监控每次 PR 是否突破预算。
总结
| 资源优化方面 | 核心技术 | 收益 |
|---|---|---|
| 图片懒加载 | IntersectionObserver + Vue 指令封装 | 减少非可视区图片加载,提升首屏速度 |
| 按需引入与 Tree Shaking | ESM 模块、自动按需插件、sideEffects 配置 | 大幅减小打包体积,仅包含实际使用的代码 |
| 首屏性能监控 | Lighthouse(实验室)+ Web Vitals(真实) | 量化性能瓶颈,指导优化方向,跟踪迭代效果 |
通过以上资源优化手段,可以系统性地降低加载开销,并从数据层面持续提升用户体验。
参考文献
以下链接在编写时均可正常访问:
| 资料 | 说明 |
|---|---|
| MDN:Intersection Observer | 懒加载基础 |
| MDN:loading=lazy | 原生图片懒加载 |
| web.dev:LCP | 最大内容绘制(英文) |
| web.dev:CLS | 布局偏移(英文) |
相关文章
编译与打包优化
构建阶段的代码分割、Tree Shaking 与 SFC 编译优化。运行时见 runtime。
运行时优化
减少不必要的渲染与响应式开销。构建侧见 build。
GridView 宫格加载渲染优化
系统首页 GridView 宫格模块接口耗时不高,但首次进入总耗时接近 22s。复盘耗时分层定位过程、代码层面的瓶颈(约 2000 行、100 个 tab 重复节点)、优化手段与最终指标对比。
新商家系统性能优化实践
商家系统新版本上线后,团队持续针对构建效率与用户体验做了一轮系统性优化。本文记录核心方案与落地结果,供后续版本复用。
老系统升级与兼容改造实践
公司原有业务系统采用 Node + jQuery 架构,长期运行后暴露出典型遗留系统问题:
防篡改水印
在各类管理后台、SaaS 平台或内部系统中,页面水印已经是非常常见的能力:一方面在截图时携带账号、姓名、时间等信息,降低截图外传的风险;另一方面在上传图片或文档预览时,通过水印标记上传者与时间,满足审计和合规需求。
Series
performance
1 / 3