2.5 事件、条件渲染与列表渲染


2.5 事件、条件渲染与列表渲染

本节摘要:界面与用户对话靠事件,界面的两种高频率结构——"是/否出现的条件渲染"和"一串数据的列表渲染"——构成绝大多数视图的核心。本节把事件委托、条件与列表的思想讲清,define 审查 key 的正确姿势,为第 3、4 章的两套具体语法打底。

本节阅读目标
阅读完本节,你应当能够:

  1. 解释前端事件委托与在框架里绑定事件的本质。
  2. 说出条件渲染的三种典型写法及其适用场景。
  3. 讲清列表 key 的意义、选 key 的规则与常见反例。

一、事件系统:界面怎么听用户说话

浏览器的事件机制本质是"捕获 → 目标 → 冒泡"的传播链。真正关键的技巧是事件委托:与其给十个按钮各绑一遍处理器,不如在它们的父容器上绑一次,靠事件冒泡统一处理。框架在这一层把绑定和清理都替你收拾好,你只要声明式地写表达式即可:

<button onClick={handleSave}>保存</button>

这不仅减少绑定次数,也在动态增删列表项时免去手动重绑的麻烦。事件做的是"让组件对外报告发生了什么",配合第 2.1 节"靠事件对外通信"的接口思考一起看,理解会更深。

二、条件渲染:界面要不要出现这块

数据驱动下,"某个块出现与否"也是一项数据。三种典型写法:

  • v-if 风格 / React 的三元:真渲真销,适合不常切换的情况。
  • v-show 风格 / CSS 切换:一直渲染但用显隐控制,适合频繁切换的场景,省去反复创建销毁。
  • 空态兜底:列表为空时渲染提示而不是空白,是健壮性的标配。

下面一段小程序把条件渲染与列表渲染一起演示,注意 key 的写法。

const users = [ { id: 1, name: '阿明' }, { id: 2, name: '小雅' }, ]; function UserList({ users }) { if (users.length === 0) { return <p>还没有用户,快去添加一个</p>; // 空态兜底 } return ( <ul> {users.map((u) => ( <li key={u.id}>{u.name}</li> // 稳定唯一的 key ))} </ul> ); }

三、列表 key:diff 记住你的钥匙

回到 2.2 节讲过的 diff:列表对比时靠 key 判断每个条目是新增、移动还是重排。用好 key 有三条规则:

  1. 用稳定唯一的字段,如数据库主键 id;用 index 当 key 是常见反例,因为重排时 index 会带错身份。
  2. key 要全局稳定:不惜频繁变化的随机数,否则每个更新都像全新列表,性能退化。
  3. key 是 diff 的标识,不是渲染的内容:别把它混进展示逻辑里。

三·一、事件委托为什么仍是基本功

声明式绑定虽然把"绑事件"藏起来了,事件委托的价值并没有消失。它真正解决的是"动态列表"场景:列表项一会儿新增一会儿删除,若是给每一项都显式绑定,增删时就要手动挂/卸,极易漏推。与其逐项管理,不如在容器上绑一次、用当前冒泡到的目标来判断。上面那段 UserList 若要给每一行加"删除"按钮,比较稳的写法是:

<ul onClick={(e) => { if (e.target.dataset.action === 'delete') removeUser(e.target.dataset.id); }}> {users.map((u) => ( <li key={u.id} data-id={u.id}> {u.name} <button data-action="delete">删除</button> </li> ))} </ul>

这样列表增删完全不需要重新绑定,父容器一处统一处理。框架的声明式语法并没能消灭委托这种技巧,只是把它的细节封装得更友好——明白这层,排查"为什么动态行点不动"时你就有了方向。

四、一张图:条件与列表在"是否渲染 / 渲染什么"上的分工

图:条件渲染与列表渲染的逻辑分立

图:条件渲染与列表渲染的逻辑分立

五、常见坑与取舍

  • 见 v-if 却用 v-show 或其他混合:追求稳定的人常纠结两难取舍,先想清楚"切换频率高不高"再选。
  • key 用 index:重排时数据错位、状态错乱,务必改稳定 id。
  • 事件负责通信而非越权处理:事件回调应报告"发生了什么",把"怎么处置"交给该管的那个层面,别在组件里偷偷改全局。

条件与列表一起出现时的编排直觉

日常视图几乎都是"条件 + 列表"混着用,编排上有一条很值钱的直觉:先判断空不空、再决定渲不渲染列表。也就是先用条件渲染把空态、加载态这类"非列表"分支挡在 v-for / map 之外,再处理里面的每一项。这样既不会出现"空数组还硬渲染一遍空白"的尴尬,也让 read 逻辑更线性。

另一条是"渲染的对象和它该有的 key 别分家":列表条目一旦要随用户操作重排、增删,你就提前想好这个条目的稳定标识,而不是中途才想起来加 key。很多"排序后输入框错乱"的线上问题,最后都收口到"当时没用稳定 key"这一个根因。把这两条并进你的默认习惯,前面 2.2 节讲的 diff 知识才算真正化成了手感。

六、事件、默认动作与冒泡的三层心智

前面讲了事件怎么绑定、怎么委托,还有个常被一笔带过却天天踩的细节:事件的默认动作冒泡传播是两条独立的轨道。点击一个链接不"跳转页"要靠 preventDefault 拦默认动作;不想让点击继续往上冒泡传给父容器才用 stopPropagation。两个动作经常被混为一谈,一混,就出现"点删除没反应""点子元素把父层触发两遍"这类现场。

一个顺手的心智模型:先判断"这一步是不是我想要的跳转/提交",需要挡住就 preventDefault;再判断"要不要让父级也知道这次点击",不需要就 stopPropagation。两者各自独立,谁也不替代谁。放到条件+列表的编排里也一样——给"删除"按钮挂委托处理时,你若只在父容器接到事件,就靠 target 的 data-action 分流,一般不会再往上冒泡造成二次处理;但若是按钮自己还套在某个也监听 click 的卡片组件里,那就要想清楚"这一层接完要不要继续往上冒"。

把"冒泡链"和"默认动作"钉进心智,你会少约一半"事件为什么怪怪的"问题。这与前面几条导演性判断一样,都在训练同一件事:先把"发生什么、传到哪里、由谁收口"想清楚,再动手写那两行绑定代码。

本节要点回顾

  • 事件委托:在父容器统一处理冒泡事件,框架已封装成声明式绑定。
  • 条件渲染三写法:真渲真销、显隐切换、空态兜底,按切换频率取舍。
  • 列表 key 三规则:稳定唯一、跨更新稳定、只是 diff 标识。
  • 分工:条件管要不要出现,列表管出现几份。

第 2 章的地基到此扎稳。从第 3 章开始,我们把这套思想写成 React 代码、再在第 4 章写成 Vue 代码——两套写法会不断印证你在这一章建立的模型。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U