一、LocalStorage:浏览器端的持久化存储
localStorage 是 Web Storage API 的一部分,用于在浏览器端进行本地持久化存储。其主要特点如下:
- 本地性:数据保存在用户浏览器中,不会发送到服务器。
- 持久性:除非用户手动清除或通过代码删除,否则数据永不过期。
- 键值对结构:以
key => value形式存储,但value只能是字符串类型。 - 容量限制:通常每个域名下有约 5~10MB 的存储空间(不同浏览器略有差异)。
使用要点
由于 localStorage 只能存储字符串,当我们需要存储对象、数组等复杂数据结构时,必须借助 JSON.stringify() 进行序列化;读取时则需用 JSON.parse() 反序列化。
// 存储
localStorage.setItem('todos', JSON.stringify(items));
// 读取(注意容错)
const items = JSON.parse(localStorage.getItem('todos')) || [];
⚠️ 注意:
JSON.parse(null)会报错,因此务必使用|| []等默认值处理。
在本例中,我们实现了一个简单的“待办事项”(Todos)应用,所有任务列表都通过 localStorage 持久化保存,即使刷新页面也不会丢失。
二、CSS 继承机制:哪些属性会继承?
CSS 具有**继承(inheritance)**特性,但并非所有属性都会自动从父元素传递给子元素。
会继承的属性(典型):
colorfont-familyfont-sizeline-heighttext-align
这些属性若没有继承机制,开发者将不得不为每个子元素重复书写样式,极大增加维护成本。
不会继承的属性(典型):
backgroundwidth/heightmargin/paddingborderdisplay
这些属性通常与布局和盒模型相关,若自动继承反而会导致布局混乱。
理解继承机制有助于我们写出更简洁、高效的 CSS 代码,并合理利用 inherit、initial、unset 等关键字控制样式行为。
三、outline 轮廓线:不影响布局的视觉反馈
outline 是一个常被忽视但非常实用的 CSS 属性。
- 类似于
border,用于绘制元素外边的线条。 - 关键区别:
outline不占据盒模型空间,不会影响元素的尺寸、位置或周围布局。 - 常用于表单控件的聚焦状态(如输入框获得焦点时的高亮效果)。
在本项目中:
.add-items input {
outline: 5px solid rgba(34, 34, 192, 0.8);
}
这里自定义了输入框的轮廓样式,既提供了良好的可访问性(用户知道当前聚焦在哪),又不会破坏原有布局。
✅ 最佳实践:不要完全移除
outline(如outline: none),除非提供其他聚焦指示方式,否则会影响键盘用户的操作体验。
四、overflow:控制内容溢出行为
overflow 属性用于处理容器内内容超出其边界的情况。
常用值:
visible(默认):内容溢出可见。hidden:隐藏溢出部分。scroll:始终显示滚动条。auto:仅在需要时显示滚动条。
虽然本例中未显式使用 overflow,但在实际开发中,它常用于:
- 防止文本溢出破坏布局;
- 创建滚动区域(如聊天窗口、长列表);
- 清除浮动(配合
overflow: hidden触发 BFC)。
五、Flexbox 布局:现代弹性盒子模型
本项目大量使用了 Flexbox(弹性盒子) 实现居中与排列。
核心思想
Flexbox 创建了一个新的格式化上下文(Formatting Context),让子元素(flex items)能够根据容器(flex container)的设置灵活伸缩、对齐。
关键代码解析
html {
box-sizing: border-box;
min-height: 100vh;
display: flex;
justify-content: center; /* 主轴(水平)居中 */
align-items: center; /* 交叉轴(垂直)居中 */
}
display: flex将<html>变为弹性容器。justify-content: center使.wrapper在水平方向居中。align-items: center使其在垂直方向居中。- 结合
min-height: 100vh,确保即使内容很少,也能全屏居中。
此外,.plates li 也使用了 display: flex,让复选框图标与文字在同一行并合理分配空间:
.plates li {
display: flex;
}
.plates label {
flex: 1; /* 占据剩余空间 */
}
这种写法避免了使用 float 或 position,代码更简洁、语义更清晰。
六、JavaScript 事件与函数式思维
1. 表单提交处理
addItems.addEventListener('submit', addItem);
- 监听
submit事件而非按钮点击,更符合语义。 - 在
addItem中调用event.preventDefault()阻止页面刷新。
2. 事件委托优化性能
itemsList.addEventListener('click', toggleItem);
- 不为每个
<li>单独绑定事件,而是在父级<ul>上监听。 - 利用事件冒泡机制,通过
event.target判断实际点击元素。 - 动态添加的项无需重新绑定事件,天然支持。
3. 函数式封装思想
“流程式代码超过10行,一定要封装函数。”
项目中将 UI 渲染逻辑封装为 populateList 函数:
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('');
}
优点:
- 复用性:添加、切换状态后均可调用。
- 可维护性:渲染逻辑集中,修改只需改一处。
- 默认参数:ES6 的
plates = []避免传参为undefined时报错。
七、CSS 技巧:用伪元素实现自定义复选框
原生 <input type="checkbox"> 样式难以定制,本项目采用“隐藏原生 + 伪元素替代”方案:
.plates input {
display: none; /* 隐藏原生复选框 */
}
.plates input + label::before {
content: "⬜️";
margin-right: 10px;
}
.plates input:checked + label::before {
content: "✅";
}
原理:
- 利用
+相邻兄弟选择器,当<input>被选中时,其后的<label>伪元素内容改变。 - 用户点击
<label>仍能触发<input>的checked状态(因for与id关联)。
这种方式兼顾了语义化(仍使用标准表单元素)与视觉定制。
总结
本项目虽小,却涵盖了现代前端开发的多个核心知识点:
| 技术点 | 应用场景 |
|---|---|
localStorage | 数据持久化 |
| CSS 继承 | 样式传递机制 |
outline | 可访问性与视觉反馈 |
| Flexbox | 响应式布局与居中 |
| 事件委托 | 性能优化 |
| 函数封装 | 代码可维护性 |
| 伪元素技巧 | UI 定制 |
通过这样一个“本地待办事项”应用,我们不仅掌握了具体 API 的使用,更理解了如何组织代码、如何设计交互、如何平衡功能与用户体验——这正是前端工程化的精髓所在。