普通视图

发现新文章,点击刷新页面。
昨天 — 2025年6月8日首页

React组件化开发实战:从"待办事项"看前端乐高搭建术

2025年6月8日 00:43

《React组件化开发:把前端变成乐高积木大师之旅》

警告:当你学会组件化思维后,看任何网站都会自动拆解成乐高积木,此症状不可逆!

一、开篇:从“一锅炖”到“分餐制”的进化史

还记得你第一次写网页的样子吗?一个HTML文件塞满1000行代码,CSS和JS在文件里打架——这就像把披萨、冰淇淋、螺蛳粉全倒进一个碗里搅拌(别试,会后悔的)。

而现代React开发如同米其林大厨备餐:

  • Vite 是智能厨房(自动处理火候/刀工)
  • 组件 是预制菜包(每个独立封装)
  • 数据流 是传菜机器人

就像我的TodoList项目,被拆解成三个精致“料理包”:

// 料理包1:TodoForm.jsx(食材输入机)
const handleSubmit = (e) => {
  e.preventDefault(); // 拦截百度外卖订单!
  onAdd(text)         // 呼叫主厨加菜
}

// 料理包2:TodoList.jsx(中央厨房)
const [todos, setTodos] = useState([ { id: 1, text: '吃饭' } ])
const handleAdd = (text) => {
  setTodos([...todos, { id: todos.length+1, text }]) 
}

// 料理包3:Todos.jsx(菜品展示台)
todos.map(todo => <li key={todo.id}>{todo.text}</li>)

二、组件化:前端的乐高革命

1. 为什么说DOM操作像用镊子搭积木?

传统开发如同用镊子组装微观乐高:

// 远古时代的痛苦记忆
document.querySelector('ul').innerHTML = 
  todos.map(todo => `<li>${todo.text}</li>`).join('')

每当数据变化就要:

  1. 找积木盒(querySelector)
  2. 拆旧积木(innerHTML = '')
  3. 拼新积木(拼接字符串)
  4. 手抖拼错全塌(页面崩溃)

2. React组件是智能积木块

想象有会自我更新的魔法积木:

// 积木块1:TodoForm
<Form魔法盒 onAdd={加菜方法} />

// 积木块2:TodoList
<中央厨房 
  菜单={todos} 
  加菜方法={handleAdd} 
/>

// 积木块3:Todos
<展示柜 菜单={todos} />

魔法原理:当你往中央厨房(TodoList)的菜单数组里塞新菜时:

setTodos([...todos, 新菜])

所有关联积木自动重组!就像乐高城市突然长出新建筑。

三、深度解密组件通信:组件间的摩斯密码

1. Props:父子组件的“悄悄话管道”

TodoList.jsx中:

<TodoForm onAdd={handleAdd} /> {/* 塞给儿子一部对讲机 */}
<Todos todos={todos} />        {/* 递给女儿一张菜单 */}

子组件接收时如同拆快递:

// TodoForm.jsx
function TodoForm(props) {
  props.onAdd(text) // 用对讲机呼叫爸爸
}

// Todos.jsx
function Todos(props) {
  props.todos.map(...) // 查看爸爸给的菜单
}

2. 数据流:单向快递系统

React的数据流像严格的快递网络:

中央仓库(setTodos) → 卡车(props) → 子组件签收

禁止反向运输!  子组件不能直接修改props(就像不能篡改快递单)

四、useState:给组件装上“记忆芯片”

1. 变量失忆症治疗指南

普通变量刷新就失忆:

let todos = [] // 刷新页面?失忆了

useState给变量装上记忆芯片:

const [todos, setTodos] = useState(() => {
  // 首次加载从缓存读取记忆
  return JSON.parse(localStorage.getItem('todos')) || []
})

2. 数据驱动UI:魔法镜子原理

想象todos数组是现实世界,UI是它的魔法镜像:

// 现实世界改变...
setTodos([...todos, { text: "写React博客", completed: false }])

// 魔法镜像自动同步更新!
<ul>
  <li>吃饭</li>
  <li>睡觉</li>
  <li>写React博客</li> {/* 自动出现! */}
</ul>

这就是为什么叫“数据驱动” ——数据是提线木偶师,UI是听话的木偶。

五、实战黑科技:动态TodoList诞生记

1. 表单拦截术:从百度嘴里抢回数据

TodoForm.jsx中上演谍战大戏:

<form action="http://www.baidu.com" onSubmit={handleSubmit}>
  {/* 表面伪装成百度搜索... */}
  <input type="text" placeholder="伪装成搜索框" />
  
  {/* 实际暗度陈仓 */}
  {handleSubmit(e) => {
    e.preventDefault() // 截获百度快递车!
    onAdd(text)        // 把数据偷运给自家仓库
  }}
</form>

2. 数组更新绝技:三明治堆叠法

添加新任务像做三明治:

setTodos([
  ...todos, // 1. 铺下面包(原数组)
  {         // 2. 放新食材(新对象)
    id: todos.length + 1, 
    text: "学习React魔法",
    completed: false 
  }          // 3. 自动封装!(新数组)
])

3. Key的重要性:给积木贴防撞条

渲染列表时:

todos.map(todo => (
  <li key={todo.id}>{todo.text}</li>
))

没有key的后果:React会哭喊着:“这些积木长得都一样!我分不清谁是谁!” 然后胡乱重新排列,导致性能崩溃。

六、组件化哲学:从码农到乐高大师

1. 组件设计黄金法则

  • 单一职责原则:像瑞士军刀,但每个工具独立

    我的TodoForm只管输入,Todos只管展示,绝不越界

  • 可配置性:预留插槽如乐高凸点

    // 高度可配置的标题
    <TodoList title="今日待办" theme="dark" />
    
  • 无状态优先:尽量做“傻白甜”组件

    // 最佳实践:只负责展示的傻组件
    const Display = ({ value }) => <div>{value}</div>
    

2. 数据流架构图

deepseek_mermaid_20250607_57184a.png

七、血泪教训:新手村避坑指南

  1. Props命名惨案
// 父组件传参
<Todos todoList={todos} />

// 子组件拆包
function Todos(props) {
  props.todos.map(...) // 报错!实际叫todoList
}

急救方案:解构时直接重命名
const { todoList: todos } = props

  1. 直接修改状态灾难
// 错误示范(引发静默失效)
todos.push(newTodo) 
setTodos(todos) // React:这俩不是同一个数组?不理你!

// 正确姿势(创建新数组)
setTodos([...todos, newTodo])
  1. Key的重复危机
// 用索引当key?删除第二项时...
[<li key=0>A</li>, <li key=1>B</li>, <li key=2>C</li>]
// 删除B后:
[<li key=0>A</li>, <li key=1>C</li>] // React以为B→C变了!

黄金准则:用唯一ID(如数据库id/crypto.randomUUID())

八、终极思考:为什么说React是界面编程的范式革命?

回顾传统方式添加一个待办事项:

sequenceDiagram
   程序员->>DOM: 找到ul元素
   程序员->>DOM: 创建li元素
   程序员->>DOM: 设置li内容
   程序员->>DOM: 插入ul末尾

React范式下:

sequenceDiagram
   程序员->>数据: setTodos(更新数组)
   数据->>React: 通知状态变更
   React->>虚拟DOM: 计算差异
   虚拟DOM->>真实DOM: 精准更新

本质区别:从“指挥DOM做每个动作”变成“声明数据状态”,如同从驾驶马车变成设置GPS导航。

结语:你的乐高帝国正在崛起

当你掌握组件化思维后:

  • 看到按钮 → “这是个Button组件”
  • 看到导航栏 → “NavBar+MenuItem组合”
  • 看到整个页面 → “Header, Content, Footer三大模块”

现在尝试给你的TodoList添加新功能:

  1. TodoItem组件中添加删除按钮
  2. 通过父组件传递onDelete回调
  3. 使用filter更新状态:
// TodoList.jsx
const handleDelete = (id) => {
  setTodos(todos.filter(todo => todo.id !== id))
}

挑战:如何让待办事项支持完成状态切换?提示:map+条件样式

记住React哲学的核心口诀: “UI是数据的函数” 。当你下次对着页面发呆时,不妨想想——眼前的一切,不过是数据的精致舞衣罢了。

昨天以前首页

CSS 基础知识小课堂:从“选择器”到“声明块”,带你玩转网页的时尚穿搭!

2025年6月5日 23:35

🎯 一、引子:CSS 是网页的时尚设计师

想象一下,HTML 是一个人的骨架和器官,那么 CSS 就是这个人穿的衣服、发型、配饰。没有 CSS 的 HTML,就像一个穿着睡衣上街的人——虽然功能齐全,但看起来总有点不太得体。

今天,我们就来聊聊这个让网页变得“有颜值”的神奇语言:CSS(层叠样式表) 。它不仅是前端开发的“化妆师”,更是我们控制网页外观的超级武器!

🧠 二、基础概念扫盲班:什么是 CSS?

1. 声明(Declaration):属性与值的 CP 组合

在 CSS 中,一个 声明(declearation)就是一组键值对,比如:

color: red;
font-size: 20px;

这就好比你给衣服选颜色和尺码,color 是属性,red 是它的值;font-size 是属性,20px 是它的值。它们组合在一起,就是一个完整的“穿搭指令”。

2. 声明块(Declaration Block):多个声明组成的集合

这些声明不是单打独斗的,而是被包裹在一个大括号 {} 里的,形成一个 声明块

{
  color: red;
  font-size: 20px;
}

这就像是一个完整的穿搭方案,包括了颜色、大小、字体等等所有风格细节。

3. 选择器(Selector)+ 声明块 = 规则集(Ruleset)

选择器决定了这个穿搭方案要应用在谁身上,声明块则是具体的穿搭内容。

h1 {
  color: red;
  font-size: 20px;
}

这段代码的意思是:“嘿,所有的 <h1> 标题元素,你们都给我穿上红色衣服,字号调成 20 像素。”

这就是一个完整的 规则集(ruleset) ,也就是我们常说的 CSS 样式规则。

就会修饰我们的html:

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h1 {
            color: red;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <h1>hello 大佬</h1>
</body>

</html>

image.png

🔍 三、CSS 选择器:精准定位你的“穿搭对象”

选择器就像是一个侦探,负责找到 HTML 页面中你想打扮的元素。不同的选择器可以让你更精确地找到目标。

1. 相邻兄弟选择器:h1 + p

h1 + p {
  color: blue;
}

意思是:“紧接在 <h1> 后面的那个 <p> 元素,你要变成蓝色。”
就像你在排队时只跟紧挨着你的人打招呼一样。

📌 比如:

 <h1>我是标题</h1>
 <p>我是紧跟其后的段落</p>

这个段落会被选中。

是只与其紧挨,且只有一个

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h1+p {
            color: red;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <h1>hello 大佬</h1>
    <p>段落1</p>
    <p>段落2</p>
</body>

</html>

image.png

2. 后续兄弟选择器:h1 ~ p

h1 ~ p {
  color: green;
}

这次就不是只找第一个兄弟了,而是找所有在 <h1> 后面的 <p> 兄弟。

📌 示例:


<h1>我是标题</h1>
<p>我也是段落</p>
<p>我也想变绿!</p>

这两个 <p> 都会被选中。 后续的兄弟元素(选中类型)都会被修饰

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        h1~p {
            color: red;
            font-size: 20px;
        }
    </style>
</head>

<body>
    <h1>hello 大佬</h1>
    <p>段落1</p>
    <p>段落2</p>
    <div>盒子1</div>
    <p>段落3</p>
</body>

</html>

image.png

3. 子元素选择器:div > p

div > p {
  background-color: yellow;
}

意思是:“只要你是 <div> 的亲生孩子(直接子元素),而且你是一个 <p>,那就给你黄色背景。”

✅ 正确匹配:

<div>
  <p>我是 div 的儿子</p>
</div>

❌ 不匹配:

<div>
  <span><p>我不是亲生的</p></span>
</div>

我们来看看具体代码:一定要是指定的后代元素

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        .outer>p {
            color: red;
            font-size: 20px;
        }

        .inner>p {
            color: blue;
            font-size: 30px;
        }
    </style>
</head>

<body>
    <div class="outer">
        <p>段落1</p>
        <div class="inner">
            <p>段落2</p>
        </div>
        <span>文字1</span>
    </div>
</body>

</html>

🦋 四、伪类选择器:为元素的状态“穿衣打扮”

有时候,我们不仅想根据元素的位置来打扮它,还想根据它的状态来调整样式。这时候就要用到 伪类选择器(Pseudo-classes)。

1. 行为伪类:用户正在做什么?

  • :hover:鼠标悬停
  • :active:点击瞬间
  • :focus:获得焦点(比如输入框被点击)
button:hover {
  background-color: lightblue;
}

input:focus {
  border: 2px solid orange;
}

💡 小贴士:你可以把 :hover 理解成“靠近我我就换装”,:focus 是“被选中我就发光”。

我们来看具体实现

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        button:active {
            background-color: red;
        }

        input:focus {
            background-color: yellow;
        }

        p:hover {
            background-color: blue;
        }
    </style>
</head>

<body>
    <button>点击我</button><br>
    <input type="text" placeholder="请输入">
    <p>一段文字</p>
</body>

</html>

点击按钮变成红色,聚焦输入框,背景颜色变成黄色,移动到p元素上面,背景颜色变成蓝色

B1E595BDE5B620231219_converted.gif

2. 状态伪类:元素当前的状态

  • :checked:复选框或单选按钮被选中时
  • :enabled / :disabled:元素是否启用
input:checked + label {
  color: purple;
}

📌 应用场景:当你选中某个选项时,对应的标签文字变成紫色,非常直观。

我们来看看具体实现

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        input:checked+label {
            color: red;
            font-weight: bold;
        }

        button:disabled {
            background-color: yellow;
        }
    </style>
</head>

<body>
    <input type="checkbox">
    <label for="">选项</label><br>
    <button>点击我</button>
    <script>
        let btn = document.querySelector('button');
        btn.addEventListener('click', function () {
            btn.disabled = true
        })
    </script>
</body>

</html>

我们添加了一个点击事件,点击后按钮就不可用了,我们css的修饰,按钮不可用就变成黄色,单选框被勾选label变成红色

B1E595BDE5B620232355_converted.gif

3. 结构伪类:根据结构关系来选择元素

这部分是我们今天重点学习的内容之一,特别是 nth-child()nth-of-type() 的区别。

✅ .container p:nth-child(3):第 3 个孩子必须是 p 才行

.container p:nth-child(3) {
  color: red;
}

📌 解释:

  • 容器 .container 中第 3 个子元素必须是 <p>,才会被选中。
  • 如果第 3 个是 <div> 或其他标签,就不会命中。

🧪 示例:

<div class="container">
  <p>1</p>
  <div>2</div>
  <p>3</p> <!-- 会被选中 -->
</div>

image.png

✅ .container p:nth-of-type(3):第 3 个 p 元素,不管它排第几

.container p:nth-of-type(3) {
  color: blue;
}

📌 解释:

  • 只要是 .container 中的第 3 个 <p> 元素,无论它在整个容器中的位置如何,都会被选中。
  • 类似于“按类型排队”,不看整体顺序。

🧪 示例:

<div class="container">
  <p>1</p>
  <div>2</div>
  <p>3</p> <!-- 第 2 个 p,不会被选中 -->
  <p>4</p> <!-- 第 3 个 p,会被选中 -->
</div>

image.png

🧠 对比记忆口诀:

  • .container p:nth-child:我只认“座位号”,必须坐那个位置。并且类型也要对
  • .container p:nth-of-type:我只认“类型+数量”,哪怕你坐在后排也无所谓。

🧩 五、进阶技巧:让选择器更强大

1. 属性选择器:根据属性来筛选

input[type="text"] {
  width: 200px;
}

📌 说明:所有 type="text" 的输入框,宽度设为 200px。

2. 通配符选择器:*

* {
  margin: 0;
  padding: 0;
}

⚠️ 注意:慎用,影响全局。

3. 分组选择器:多个选择器共享一套样式

h1, h2, h3 {
  color: #333;
}

📌 效果:所有一级、二级、三级标题都使用深灰色字体。


🧵 六、总结:CSS 是一门优雅的语言

通过今天的学习,我们掌握了:

  • 声明、声明块、规则集的基本结构;
  • 多种选择器的使用方法;
  • 伪类选择器的作用及分类;
  • nth-child() 与 nth-of-type() 的区别;
  • 如何用 CSS 来为网页“穿衣打扮”。

结语:CSS 很有趣,也很值得深入研究!

CSS 并不只是写几个颜色和边距那么简单,它是构建现代网页视觉体验的重要基石。掌握好选择器,就像拥有一双能精准识别“穿搭对象”的眼睛,让你的网页美得恰到好处。

希望这篇轻松幽默的技术博客,能帮助你更好地回顾今天所学的内容。如果你觉得有用,不妨点赞收藏,下次继续带你看懂更多 CSS 的奥秘!

❌
❌