引言
在现代Web开发中,如何优雅地保存用户输入并确保其在页面刷新后仍然存在,是一个常见的需求。localStorage 提供了一个简单而强大的解决方案,它允许我们在浏览器中持久化存储数据。本文将通过一个生动的例子——“LOCAL TAPAS”应用,向您展示 localStorage 的魅力。
话不多说,结果展示
如上图所示,在重新进入界面时,之前输入的数据依然存在。这一功能的实现,正是得益于 localStorage 的使用。当用户离开页面后再回来时,他们不会丢失任何已经输入的信息,这大大提升了用户体验。而在这里我们可以通过以下步骤查看我们的localStorage在哪里,并以什么形式存储数据。
接下来我们将深入探讨这个页面的实现细节以及 localStorage 的具体用法。
代码演示
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta data-n-head="ssr" name="viewport"
content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">
<title>localStorage</title>
<style>
* {
margin: 0;
padding: 0;
}
html {
box-sizing: border-box;
background: url('http://wes.io/hx9M/oh-la-la.jpg') center no-repeat;
background-size: cover;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-family: Futura, "Trebuchet MS", Arial, sans-serif;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
svg {
fill: white;
background: rgba(0, 0, 0, 0.1);
padding: 20px;
border-radius: 50%;
width: 200px;
margin-bottom: 50px;
}
.wrapper {
padding: 20px;
max-width: 350px;
background: rgba(255, 255, 255, 0.95);
box-shadow: 0 0 0 10px rgba(0, 0, 0, 0.1);
}
h2 {
text-align: center;
margin: 0;
font-weight: 200;
}
.plates {
margin: 0;
padding: 0;
text-align: left;
list-style: none;
}
.plates li {
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
padding: 10px 0;
font-weight: 100;
display: flex;
}
.plates label {
flex: 1;
cursor: pointer;
}
.plates input {
display: none;
}
.plates input+label:before {
content: '⬜';
margin-right: 10px;
}
.plates input:checked+label:before {
content: '🌮';
}
.add-items {
margin-top: 20px;
}
.add-items input {
padding: 10px;
outline: 0;
border: 1px solid rgba(0, 0, 0, 0.1);
}
</style>
</head>
<body>
<div class="wrapper">
<h2>LOCAL TAPAS</h2>
<p></p>
<ul class="plates">
<li>Loading Taps...</li>
</ul>
<form class="add-items">
<input type="text" name="item" placeholder="Item Name" required>
<input type="submit" value="+ Add Item">
</form>
<script>
const addItems = document.querySelector('.add-items'); //form
const itemsList = document.querySelector('.plates'); //ul
const items = JSON.parse(localStorage.getItem('items')) || [];
//event 是事件对象 1.内部this指向提交的表单 2.运行时分配event对象
function addItem(event) {
//阻止默认行为
event.preventDefault();
//.querySelector()体现了性能 优化
const text = (this.querySelector('[name=item]')).value.trim();
//创建新项对象
const item = {
text,//es6
done: false
}
items.push(item);
//更新DOM树
populateList(items, itemsList);
//存储数据 更新本地存储
localStorage.setItem('items', JSON.stringify(items));
清空输入框准备下一次输入
this.reset();
}
//渲染ul
function populateList(plates, platesList) {
platesList.innerHTML = plates.map((plate, i) => {
return `
<li>
<input
type="checkbox"
data-index=${i}
id="item${i}"
${plate.done ? 'checked' : ''}
>
<label for="item${i}">${plate.text}</label>
</li>
`
}).join(''); //去除页面中的“,”
}
addItems.addEventListener('submit', addItem)
populateList(items, itemsList);
//// 处理复选框状态变更
itemsList.addEventListener('change', function (event) {
if (event.target.type === 'checkbox') {
const index = event.target.dataset.index;
items[index].done = event.target.checked;
localStorage.setItem('items', JSON.stringify(items));
}
});
</script>
</div>
</body>
</html>
代码流程解析
HTML 和 CSS 编写
HTML与CSS部分构建了页面的基础结构和视觉样式。这部分较为基础,主要定义了页面元素及其样式,如表单、无序列表等,并设置了全局样式规则以确保一致性和美观性,对于想要深入了解这部分内容的朋友,可以通过查阅相关代码获取更多信息。
JavaScript 实现逻辑详解
初始化与加载现有数据
在JavaScript中,我们首先使用querySelector方法选择DOM中的.add-items表单和.plates无序列表元素,并创建了一个名为items的常量。该常量从浏览器的localStorage中读取名为items的数据项。如果localStorage中没有找到对应的键值对,则初始化为空数组。这一步骤确保了用户返回页面时能看到之前添加的所有项。
添加新项到列表
function addItem(event) {
......
}
当用户提交表单时,addItem函数被触发。它首先阻止了表单的默认提交行为,然后获取输入框中的文本,并将其与一个done属性(初始为false)一起包装成一个新的对象item。接着,这个新对象被添加到items数组中,调用populateList函数更新DOM,将最新的数据保存回localStorage,并重置表单以便用户继续添加更多项。
渲染列表
function populateList(plates, platesList) {
platesList.innerHTML = plates.map((plate, i) => {
......
}).join(''); // 去除页面中的逗号
}
populateList函数负责根据当前items数组的状态构建HTML列表。它遍历plates数组,为每个元素创建一个<li>标签,包括一个复选框和标签文字。这些标签随后被合并成一个完整的字符串并赋值给platesList.innerHTML,从而实现DOM的批量更新。
用户交互:更新项状态
itemsList.addEventListener('change', function(event) {
......
});
为了保持复选框状态的一致性,我们在itemsList上监听change事件。每当复选框状态改变时,会找到对应的项并更新其done属性,最后再次保存更新后的items数组到localStorage中,以确保状态的持久化。
关键代码解释
const items = JSON.parse(localStorage.getItem('items')) || [];:这段代码尝试从localStorage中检索名为items的数据项,并使用JSON.parse()方法解析成JavaScript对象或数组。如果不存在这样的数据项或解析失败,则初始化为空数组。localStorage.setItem('items', JSON.stringify(items));:这里使用了JSON.stringify()方法将items数组转换为字符串格式,再通过localStorage.setItem()方法保存到浏览器的本地存储中。这是为了让复杂的数据结构(例如对象或数组)可以被正确地序列化和保存。platesList.innerHTML = plates.map((plate, i) => {}).join('');:此代码片段用于动态生成HTML内容。它遍历plates数组,为每个元素创建相应的HTML标记,最后通过join('')方法将所有标记合并成一个完整的字符串,赋值给platesList.innerHTML,以此方式更新DOM树。
对于代码考点的思考
HTML5 localStorage 简介
它允许开发者以键值对的形式保存数据,并且这些数据不会因为浏览器关闭或页面刷新而丢失。每个源都有大约5MB的存储空间可用。
- 存储与获取:通过
setItem方法可以将字符串形式的数据存储到localStorage中,而getItem则用于检索对应的值。 - 序列化与反序列化:由于
localStorage只能存储字符串,因此使用JSON.stringify()将对象或数组转换成字符串进行存储,再用JSON.parse()将其解析回原来的数据结构。 - 表单重置:
this.reset()用于重置表单元素,使得用户可以连续添加多个项目而不必手动清空输入框。
HTML5 特性
文档类型声明
<!DOCTYPE html>这是HTML5的标准文档类型声明,确保浏览器按照最新的标准渲染页面,提高兼容性和性能。
视口设置
<meta data-n-head="ssr" name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, viewport-fit=cover">这段元标签定义了移动设备上的视口行为:
user-scalable=no禁止用户缩放页面,提供更一致的用户体验,尤其是在移动端应用中,这种做法很常见,因为它避免了不必要的复杂操作,随着移动互联网的发展,专门为移动端优化的网站逐渐成为主流,这使得PC端的一些设计习惯(如频繁缩放)变得不那么必要。
表单增强功能
HTML5增加了诸如placeholder和required等属性,改善了表单的用户体验,placeholder提供了提示信息,而required确保用户必须填写该字段才能提交表单。
JavaScript 性能考量
JavaScript中的DOM编程确实是最耗性能的部分之一,尤其是涉及到查找和修改DOM节点时。为了优化性能,我们可以采取以下措施:
- 减少DOM操作:尽可能批量更新DOM,比如一次性构建完整的HTML字符串然后插入DOM树,而不是频繁地修改单个元素。
- 缩小查找范围:利用
querySelector方法可以在特定上下文中查找元素,从而减少了全局搜索带来的开销。 - 采用虚拟DOM技术:虽然当前代码没有使用框架如Vue.js,但在大型应用中,虚拟DOM可以显著提升性能,因为它只更新实际发生变化的部分。
良好的代码风格不仅有助于团队协作,还能让代码更容易维护和扩展。以下几点建议旨在帮助您写出既专业又易于理解的代码:
代码风格建议
命名清晰的事件处理函数
为事件处理函数选择有意义的名字至关重要。一个恰当的函数名能够在不查看具体实现的情况下传达其功能,即使在项目规模扩大时也能快速理解代码意图,清晰的命名如同路标,指引开发者快速找到所需逻辑。
充分利用ES6特性
现代JavaScript(ES6及以上版本)引入了许多简洁而强大的语法糖,如对象字面量简化写法、箭头函数等。合理使用这些特性可以使代码更加紧凑且易读。比如,当对象属性名与值同名时,可以省略重复的键值对书写;利用解构赋值简化参数获取等。这种做法不仅减少了冗余,还提高了代码的可读性和开发效率。
遵循单一职责原则
保持函数简短精炼,专注于单一任务。这不仅能提升代码的可读性和可维护性,还便于单元测试和功能复用。大型应用中,每个函数都应像一个乐高积木块,小而独立,组合起来构建复杂逻辑。避免过长的函数,因为它们会让阅读者感到如同在水下憋气般难受,难以一口气理解整个逻辑。
- 多拆分函数:将复杂的功能分解成多个小型函数,使得每个函数只做一件事。
- 方便复用:小型函数更可能被其他地方复用,减少冗余代码。
- 提高可读性:简短的函数更容易理解和审查,降低了错误发生的几率。
- 增强可测试性:单一职责的函数更容易编写针对性的单元测试。
结语
本项目不仅实现了数据持久化存储,更展示了如何在实际开发中运用现代Web技术。这一过程不仅是技术实现的展示,更是对开发者解决问题能力和代码质量追求的体现,希望这篇文章不仅能为您提供有价值的知识摄入,还能为你的大厂之路提供素材。