在现代 Web 开发中,我们常常需要在浏览器中保存用户数据,即使页面刷新或关闭后也能保留。HTML5 提供的 localStorage API 正是解决这一需求的关键工具。本文将带你从零开始,构建一个简洁、美观且具备数据持久化的待办事项应用,并深入理解相关前端技术。
一、什么是 LocalStorage?
localStorage 是浏览器提供的一种客户端存储机制,具有以下特点:
- 本地存储:数据保存在用户浏览器中,不发送到服务器。
- 永久有效:除非用户手动清除或代码删除,否则数据不会过期。
- 键值对结构:以
key => value形式存储,value 只能是字符串。 - 容量限制:通常为 5~10MB(不同浏览器略有差异)。
💡 存储对象时需使用
JSON.stringify()转为字符串;读取时用JSON.parse()还原。
二、项目结构与功能预览
我们的应用包含以下核心功能:
- ✅ 显示待办事项列表
- ✅ 添加新事项
- ✅ 勾选完成状态
- ✅ 数据自动保存到
localStorage - ✅ 页面刷新后数据不丢失
页面采用极简设计,使用 Flexbox 实现居中布局,配合自定义复选框样式,体验流畅优雅。
三、关键代码解析
1. HTML 结构
html
预览
<div class="wrapper">
<h2>LOCAL TAPAS</h2>
<ul class="plates">
<li>Loading Tapas...</li>
</ul>
<form class="add-items">
<input type="text" placeholder="Item Name" required name="item">
<input type="submit" value="+ Add Item">
</form>
</div>
.wrapper:容器,居中显示.plates:待办事项列表(无序列表)- 表单用于输入新事项
2. CSS 样式亮点
✨ Flexbox 居中布局
css
编辑
html {
box-sizing: border-box;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
- 整个页面垂直+水平居中
min-height: 100vh确保占满视口高度
🎨 自定义复选框(隐藏原生 input)
css
编辑
.plates input {
display: none; /* 隐藏原生复选框 */
}
.plates input + label::before {
content: "⬜️";
margin-right: 10px;
}
.plates input:checked + label::before {
content: "✅"; /* 勾选时显示对勾 */
}
- 利用
::before伪元素模拟图标 - 通过
:checked伪类切换状态
🔒 盒模型统一管理
css
编辑
*,
*::before,
*::after {
box-sizing: inherit;
}
- 全局继承
border-box,避免宽度计算混乱
⚠️ 注意:
outline和overflow的妙用
outline:聚焦时高亮(不占布局空间)overflow: hidden:可防止子元素溢出父容器
3. JavaScript 核心逻辑
初始化数据(支持从 localStorage 读取)
js
编辑
// 优先从 localStorage 读取,否则使用默认数据
const items = JSON.parse(localStorage.getItem('items')) || [
{ text: '给平平同学文章点赞', done: false }
];
渲染列表(函数式封装)
js
编辑
function populateList(plates = [], platesList) {
platesList.innerHTML = plates.map((plate, i) => `
<li>
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} />
<label for="item${i}">${plate.text}</label>
</li>
`).join('');
}
- 使用模板字符串动态生成 HTML
data-index用于后续事件处理
添加新事项
js
编辑
function addItem(event) {
event.preventDefault();
const text = this.querySelector('[name=item]').value.trim();
if (!text) return;
items.push({ text, done: false });
populateList(items, itemsList);
localStorage.setItem('items', JSON.stringify(items)); // 立即保存
this.reset(); // 清空表单
}
处理勾选状态(需补充)
js
编辑
itemsList.addEventListener('click', e => {
if (e.target.matches('input[type="checkbox"]')) {
const index = e.target.dataset.index;
items[index].done = e.target.checked;
localStorage.setItem('items', JSON.stringify(items));
}
});
✅ 最佳实践:超过 10 行的流程式代码应封装为函数,提升可读性与复用性。
四、完整增强版 JavaScript(含 localStorage 持久化)
js
编辑
const addItems = document.querySelector('.add-items');
const itemsList = document.querySelector('.plates');
// 从 localStorage 读取或初始化默认数据
let items = JSON.parse(localStorage.getItem('items')) || [
{ text: '给平平同学文章点赞', done: false }
];
function populateList(plates = [], platesList) {
platesList.innerHTML = plates.length
? plates.map((plate, i) => `
<li>
<input type="checkbox" data-index=${i} id="item${i}" ${plate.done ? 'checked' : ''} />
<label for="item${i}">${plate.text}</label>
</li>
`).join('')
: '<li>暂无待办事项!</li>';
}
function addItem(e) {
e.preventDefault();
const input = this.querySelector('[name=item]');
const text = input.value.trim();
if (!text) return;
items.push({ text, done: false });
populateList(items, itemsList);
localStorage.setItem('items', JSON.stringify(items));
this.reset();
}
function toggleDone(e) {
if (!e.target.matches('input[type="checkbox"]')) return;
const index = e.target.dataset.index;
items[index].done = e.target.checked;
localStorage.setItem('items', JSON.stringify(items));
}
// 绑定事件
addItems.addEventListener('submit', addItem);
itemsList.addEventListener('click', toggleDone);
// 初始渲染
populateList(items, itemsList);
五、总结与延伸
通过这个小项目,我们掌握了:
| 技术点 | 应用场景 |
|---|---|
localStorage | 客户端持久化存储 |
| Flexbox | 响应式居中布局 |
伪元素 (::before) | 自定义表单控件样式 |
| 事件委托 | 高效处理动态元素点击 |
| 函数式编程 | 提升代码可维护性 |
可扩展方向:
- 添加“删除”按钮
- 支持按完成状态筛选
- 加入本地通知提醒
- 使用
dark mode主题切换
🌟 记住:优秀的前端开发不仅是实现功能,更是对用户体验、代码质量和性能的综合把控。