使用 LocalStorage 构建持久化待办事项清单(To-Do List)

135 阅读3分钟

在现代 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,避免宽度计算混乱

⚠️ 注意:outlineoverflow 的妙用

  • 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 主题切换

🌟 记住:优秀的前端开发不仅是实现功能,更是对用户体验、代码质量和性能的综合把控。