前端基础避坑:3 个实用知识点的简单用法
做前端这些年,发现不少问题不是框架难,是基础没玩明白 ------ 比如知道事件委托,却搞不定动态元素点击;用 localStorage 存对象,取出来成了[object Object]
。今天说几个自己踩过的坑,都是日常能用的,不绕虚的。
1. 事件委托:别再给每个列表项绑事件
刚入行写 Todo 列表,我会循环给每个 item 加 click 事件,结果产品加了 "动态加任务" 功能,新任务点了没反应。后来才懂,事件委托得找对父容器,还得处理子元素的情况。
小白常写错的
js
// 错1:只给现有item绑事件,动态加的没用
const todoItems = document.querySelectorAll(".todo-item");
todoItems.forEach((item) => {
item.onclick = () => item.classList.toggle("done");
});
// 错2:绑到body上,冒泡太远,可能出问题
document.body.onclick = (e) => {
if (e.target.classList.has("todo-item")) {
e.target.classList.toggle("done");
}
};
正确的做法
找 "不会动的父容器"(比如.todo-list
),再用closest
找真正的 item(防止点到 item 里的子元素,比如删除按钮):
js
const todoList = document.querySelector(".todo-list");
todoList.onclick = (e) => {
// 找到最近的.todo-item,不管点的是item还是它的子元素
const targetItem = e.target.closest(".todo-item");
if (!targetItem) return; // 不是目标就退出
// 区分点的是删除按钮还是任务本身
const delBtn = e.target.closest(".todo-del");
if (delBtn) {
targetItem.remove();
return;
}
targetItem.classList.toggle("done");
};
小提醒
别把事件绑到body
、html
上,找最近的固定父容器就行,省得浪费性能。
2. localStorage:别直接存对象!简单封装下
很多人存对象直接写localStorage.setItem('user', userInfo)
,取的时候傻了 ------localStorage 只能存字符串。还有个问题,它没过期时间,存的 token 过期了还在,容易出 bug。 分享 3 个常用的封装函数,解决这些问题:
1. 基础版:能存对象 / 数组
js
const storage = {
// 存数据:对象转成JSON字符串
set(key, value) {
try {
const str = typeof value === "object" ? JSON.stringify(value) : value;
localStorage.setItem(key, str);
} catch (e) {
console.error("存不进去:", e);
}
},
// 取数据:JSON字符串转回来
get(key) {
try {
const val = localStorage.getItem(key);
return val ? JSON.parse(val) : null;
} catch (e) {
// 不是JSON就直接返回原字符串
return localStorage.getItem(key);
}
},
// 删除数据
remove(key) {
localStorage.removeItem(key);
},
};
// 用的时候这样写
storage.set("user", { name: "张三", age: 28 });
const user = storage.get("user"); // 能拿到完整对象
2. 进阶版:带过期时间
比如存 token,想让它 2 小时后过期:
js
// 存的时候加过期时间(单位:分钟)
storage.setWithExpire = (key, value, expireMin) => {
const data = {
value: value,
expire: Date.now() + expireMin * 60 * 1000, // 过期时间戳
};
this.set(key, data);
};
// 取的时候判断是否过期
storage.getWithExpire = (key) => {
const data = this.get(key);
if (!data) return null;
if (Date.now() > data.expire) {
this.remove(key); // 过期了就删掉
return null;
}
return data.value;
};
// 示例:存token,2小时过期
storage.setWithExpire("token", "abc123", 120);
小提醒
- 别存太大的东西,localStorage 就 5MB,满了会报错;
- 密码之类的敏感数据别存在这,用
sessionStorage
或者httpOnly Cookie
。
3. CSS 优先级:别乱加!important!记个简单算法
新手调样式常遇到 "写了不生效",然后就加!important
,最后样式乱成一团。其实算清楚优先级,不用!important
也能搞定。
简单算法:算三个数
按 "ID 个数(1 个算 100)、类 / 伪类个数(1 个算 10)、元素个数(1 个算 1)" 加起来,数大的生效:
- 内联样式(
style
属性):1000(最高) #box
(ID):100.red
、:hover
(类 / 伪类):10div
、:before
(元素 / 伪元素):1
举几个例子:
div.box
→ 1(元素)+10(类)=11#app .list li
→ 100(ID)+10(类)+1(元素)=111<div style="color: red">
→ 1000
实际场景:改组件库样式
比如用 Element UI 的按钮,写.el-button { color: blue }
不生效 ------ 因为组件库的样式是.el-button--primary { color: white }
(优先级 10),和你的一样,谁后加载谁生效。 想覆盖的话,提高优先级就行:
css
/* 父容器类 + 目标类 → 10+10=20,比10高 */
.app .el-button--primary {
color: blue;
}
什么时候能加!important?
只有一种情况:要覆盖内联样式,还改不了 HTML(比如第三方组件)。比如:
html
<!-- 第三方组件的内联样式 -->
<div class="third-box" style="width: 200px;"></div>
这时才能用:
css
.third-box {
width: 300px !important;
}
记住:加了!important
,以后改这个样式也得加,能不用就不用。
最后:基础不是会了就行,是用对
框架更新快,但 DOM、存储、CSS 这些基础不变。我之前因为事件委托没处理好,查了半小时才发现动态元素没反应;也因为 localStorage 没加过期时间,导致用户看了过期数据 ------ 这些坑都不是不会,是没吃透。
要是觉得这些有用,项目里试试;你们踩过哪些基础坑,也可以聊聊~