hello,大家好,我是唐叔,今天想介绍的是 SPA - 单页应用的首屏性能问题优化的一些实践,也是我理解的 SPA 应用最大的技术难点吧。
SPA 的优势很明显:一次加载,无缝交互,适合多端复用。但代价也很大——首屏需要加载所有资源,一旦代码庞大,白屏时间就会变长。这就是我们常说的“首屏性能问题”。
下面就以唐叔最近一直在开发的 TodoList 应用为例,分享几种优化手段。
优化方式 1:骨架屏
严格说,骨架屏并不是优化性能,而是优化用户体验。
在数据加载完成前,先展示一个灰色的“页面轮廓”,避免用户面对白屏。
第一步,前端 html 页面,在 HTML 中预留骨架屏结构,具体可以基于你原本页面的框架去处理:
第二步,在编写 JS 初始化操作时,先显示骨架屏,然后加载数据,加载完数据再隐藏骨架屏。
大体编码是这样的:
class App {
// 初始化应用
async init() {
try {
// 显示加载状态
Utils.setLoading(true);
// 加载数据
} catch (error) {
// 异常报错
} finally {
// 隐藏加载状态
Utils.setLoading(false);
// 隐藏骨架屏
const skeletonScreen = document.getElementById('skeleton-screen');
if (skeletonScreen) {
skeletonScreen.style.display = 'none';
}
}
}
}
document.addEventListener('DOMContentLoaded', () => {
// 显示骨架屏
const skeletonScreen = document.getElementById('skeleton-screen');
if (skeletonScreen) {
skeletonScreen.style.display = 'flex';
}
// 延迟初始化,确保所有资源加载完成
setTimeout(() => {
app.init();
}, 100);
});
额外建议:现在的开发更多是“思路驱动实现”,你可以用 AI 快速生成骨架屏代码,前提是你清楚它的原理。
优化方式 2:前端缓存
缓存虽然对首次加载无效,但对二次打开提升巨大。
常见前端存储方式主要有下述几类:
| 存储方式 | 容量 | 持久性 | 异步/同步 | 数据结构 | 同源限制 |
|---|---|---|---|---|---|
| Cookie | ~4KB | 可设置 | 同步 | 字符串 | 是 |
| localStorage | 5-10MB | 永久 | 同步 | 字符串 | 是 |
| sessionStorage | 5-10MB | 会话级 | 同步 | 字符串 | 是 |
| IndexedDB | >250MB | 永久 | 异步 | 结构化 | 是 |
| Cache API | 不定 | 永久 | 异步 | Request/Response | 是 |
| FileSystem | 不定 | 永久 | 异步 | 文件 | 是 |
在 TodoList 中,我们使用 localStorage 存储用户偏好(如主题、语言)。如果你开发的是轻量化应用,完全可以用 IndexedDB 作为整个应用的数据存储层。
💡 不过要补充说明的是,上述各类存储方式,可能存在浏览器兼容性问题。像 TodoList 用的后端是 pywebview,开启 localStorage 通过配置 private_mode。
优化方式 3:路由懒加载
SPA 首屏并不需要加载所有页面模块时,可以把部分组件延迟到用户访问时再加载。
在 TodoList 移动端中,我正在做将「左侧抽屉弹窗」改为懒加载,等用户点击时再加载对应数据,减少首屏负担。
优化方式 N:其他策略
上述几种方式,是目前 TodoList 应用主用的优化方式,当然还有其他的方式,这里以我了解到的做展开介绍,当前不是专业前端可能了解的不多,其他同学知道的其他方式也可以在评论区补充说明。
-
静态资源懒加载
和路由懒加载类似,类似图片等高占用体积的资源,可以考虑使用这种方式,不过像
TodoList应用,前端和后端都是写在一个包里面的,没有纯粹的后端服务,貌似用不了。<img src="placeholder.jpg" data-src="real-image.jpg" loading="lazy" /> -
Gzip 压缩
现代构建工具(如 Webpack、Vite)会自动生成压缩后的代码包,服务端开启 Gzip 后即可生效,显著减少传输体积。不过
TodoList是纯HTML/CSS/JS页面,估计还真的不好用这种方式。
以上是 TodoList 项目中正在实践的首屏优化策略。作为本专栏的开篇,希望能帮你少踩一些坑,也欢迎大家在评论区补充你常用的优化方式。