引言
在现代前端开发中,设计模式和性能优化是构建高质量应用的重要基石。本文将围绕单例模式与懒加载两大核心概念,详细讲解如何实现一个基于 localStorage 的单例 Storage 类,并结合实际场景优化登录弹窗的加载与使用方式。
一、单例模式简介
什么是单例模式?
单例模式(Singleton Pattern) 是一种常见的设计模式,其核心思想是:
确保一个类在整个程序中只能被实例化一次,并提供一个全局访问点来获取这个实例。
适用场景
- 数据库连接池
- 日志记录器(Logger)
- 全局配置管理
- 登录弹窗、全局状态管理等
JavaScript 中的实现方式
由于 JavaScript 并不支持类的私有构造函数,我们可以通过以下方式模拟单例:
- 闭包 + 模块模式
- ES6 类 + 静态属性
- Node.js 模块自动单例
二、实战:实现一个基于 localStorage 的单例 Storage 类
功能目标
- 封装
localStorage - 提供
setItem(key, value)和getItem(key)方法 - 确保只有一个实例存在(单例)
实现代码
class Storage {
static instance = null;
constructor() {
if (Storage.instance) {
return Storage.instance;
}
Storage.instance = this;
}
// 设置值,自动序列化为 JSON
setItem(key, value) {
localStorage.setItem(key, JSON.stringify(value));
}
// 获取值,自动反序列化为 JS 对象
getItem(key) {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) : null;
}
}
🧩 使用方式
const storage1 = new Storage();
const storage2 = new Storage();
storage1.setItem('username', '小明');
console.log(storage2.getItem('username')); // 输出: 小明
console.log(storage1 === storage2); // true
###设计解析
| 代码片段 | 作用 |
|---|---|
static instance = null; | 保存唯一实例 |
constructor() | 构造函数中判断是否已存在实例 |
setItem() | 封装 localStorage.setItem 并自动序列化 |
getItem() | 封装 localStorage.getItem 并自动反序列化 |
三、结合实践:登录弹窗的优化(懒加载 + 单例)
优化目标
| 目标 | 实现方式 |
|---|---|
| 不跳转页面,浮层显示 | 使用 position: fixed 和 z-index 控制层级 |
| 用户点击时才加载资源 | 使用懒加载,推迟 DOM 创建和样式加载 |
| 多次打开弹窗只创建一次 | 使用单例模式,避免重复初始化 |
| 可以反复使用 | 通过单例统一管理状态和行为 |
实现代码:单例 + 懒加载登录弹窗
class LoginModal {
static instance = null;
constructor() {
if (LoginModal.instance) {
return LoginModal.instance;
}
this.modal = null;
LoginModal.instance = this;
}
// 懒加载初始化
init() {
if (this.modal) return;
// 创建弹窗 DOM
this.modal = document.createElement('div');
this.modal.style.position = 'fixed';
this.modal.style.top = '50%';
this.modal.style.left = '50%';
this.modal.style.transform = 'translate(-50%, -50%)';
this.modal.style.width = '300px';
this.modal.style.padding = '20px';
this.modal.style.backgroundColor = '#fff';
this.modal.style.boxShadow = '0 0 10px rgba(0,0,0,0.2)';
this.modal.style.zIndex = '1000';
this.modal.style.display = 'none';
// 添加表单内容
this.modal.innerHTML = `
<h3>登录</h3>
<input type="text" placeholder="用户名" id="login-username" />
<input type="password" placeholder="密码" id="login-password" />
<button id="login-btn">登录</button>
`;
// 添加到 body
document.body.appendChild(this.modal);
// 绑定事件
document.getElementById('login-btn').addEventListener('click', () => {
const username = document.getElementById('login-username').value;
const password = document.getElementById('login-password').value;
alert(`正在尝试登录:${username}`);
});
}
// 显示弹窗
show() {
this.init(); // 第一次调用时初始化
this.modal.style.display = 'block';
}
// 隐藏弹窗
hide() {
if (this.modal) {
this.modal.style.display = 'none';
}
}
}
使用方式
<!-- 页面上添加一个按钮 -->
<button onclick="showLogin()">打开登录弹窗</button>
<script>
function showLogin() {
const modal = new LoginModal();
modal.show();
}
</script>
设计解析
| 代码片段 | 作用 |
|---|---|
static instance = null; | 保存唯一实例 |
init() 方法 | 只在第一次调用时创建 DOM |
if (this.modal) return; | 如果已经创建过,就不再重复创建 |
show() 方法 | 用户点击时才触发初始化和显示 |
四、懒加载的优势与性能优化
优势
| 优势 | 说明 |
|---|---|
| 减少首屏加载时间 | 不加载不必要的资源 |
| 节省内存 | 不创建多余的 DOM 和监听器 |
| 提升用户体验 | 页面更快加载,用户更早看到内容 |
| 更好地支持移动端 | 移动设备网络慢,懒加载更友好 |
延伸:React 中的懒加载
const LazyComponent = React.lazy(() => import('./MyComponent'));
function App() {
return (
<React.Suspense fallback="加载中...">
<LazyComponent />
</React.Suspense>
);
}
五、总结:单例 + 懒加载 = 高性能 + 易维护
| 技术 | 作用 |
|---|---|
| 单例模式 | 确保一个类只有一个实例,便于全局统一管理 |
| 懒加载 | 推迟资源加载,提升性能 |
| 结合使用 | 性能更好 + 体验更佳 + 结构更清晰 |
适用场景对比总结
| 功能 | 实现方式 | 设计思想 |
|---|---|---|
| 单例 使用orage | ES6 类 + 静态属性 | 封装 + 单例 |
setItem / getItem | 封装 localStorage | 数据持久化 |
| 登录弹窗 | 单例 + 懒加载 | 性能优化 + UI 复用 |
| 弹窗显示/隐藏 | display 控制 | 简洁交互 |
| 弹窗内容初始化 | 第一次调用时才创建 | 推迟加载资源 |
结语
掌握单例模式与懒加载,不仅能帮助你写出更清晰、高效的代码,还能在实际项目中显著提升用户体验和性能表现。无论是封装本地存储工具类,还是优化登录弹窗等 UI 组件,这两项技能都具有极高的实用价值。
如果你正在构建一个中大型前端应用,建议将这些设计思想融入到你的组件库或工具库中,让代码更具可维护性和可扩展性。