Skip to content

控制流与 DOM 身份 ​

Solid 的 JSX 首次执行时建立真实 DOM;<Show>、<For> 和 <Switch> 只在条件或数组项变化时更新受影响的节点。<For> 按每一项的 value identity 复用节点,<Index> 按数组位置 identity 复用节点;同一个 id 的新对象不等于同一个对象。数组重排应使用 <For>,需要固定位置并只替换该位置的值时才使用 <Index>,两者不能用一个“key”概念混为一谈。

先运行一个最小实验 ​

在 Solid 项目中创建组件:

tsx
import { For, createSignal } from 'solid-js'

export default function Demo() {
  const [items, setItems] = createSignal([{ id: 1, name: 'A' }, { id: 2, name: 'B' }])
  return <>
    <button onClick={() => setItems([{ id: 0, name: 'new' }, ...items()])}>头部插入</button>
    <For each={items()}>{(item) => <input value={item.name} data-id={item.id} />}</For>
  </>
}

先在第二个输入框改成 B*,再点击“头部插入”。稳定 id 下 B* 仍属于 id=2;若改成手写索引复用 DOM,输入焦点和未提交值会随位置移动。开发者工具中观察 input 节点是否被复用,而不是只看文本。

机制:何时创建与何时更新 ​

<Show when={loggedIn()} fallback={<Login />}> 在条件变化时切换分支;<For each={items()}> 对数组项做细粒度更新。子节点内部的 signal 只通知读取它的表达式,因此改变一个 item 不应重建整个列表。需要重置局部状态时,给它换一个稳定但不同的 key,不要用副作用强行清空 DOM。

变体与边界 ​

  • For 适合保留项身份的动态列表;需要按位置整体重建时才考虑 Index。
  • 条件分支中的请求、订阅和定时器必须在分支销毁时清理,不能把资源创建放到每次渲染表达式里。
  • SSR 输出只包含服务器可得状态;浏览器专属 API 放在 onMount,否则首屏会发生 hydration 不一致。

练习 ​

  1. 把示例改成删除任意 id,并验证其他输入值与焦点不变。
  2. 使用 <Show> 增加加载、成功、错误三态,记录每次切换时创建和清理的资源。
  3. 解释为什么索引 key 会让“未提交输入”与数据错位。

查看本节答案

用心记录每一步成长