SolidJS 中的控制流组件有哪些?如何使用 Show、For、Switch 等?
SolidJS 的控制流组件是框架响应式系统的核心部分,与 React 中直接使用 JavaScript 表达式不同,SolidJS 通过专用组件让响应式追踪更加精确,从而实现细粒度的 DOM 更新。下面逐一介绍每个控制流组件的用法和设计原理。
Show — 条件渲染
Show 是最常用的控制流组件,用于根据条件决定是否渲染内容。
jsximport { Show, createSignal } from "solid-js"; function App() { const [isLoggedIn, setIsLoggedIn] = createSignal(false); return ( <Show when={isLoggedIn()} fallback={<LoginButton />}> <UserDashboard /> </Show> ); }
关键细节:
when的值会被响应式追踪,变化时只更新受影响的 DOM 节点fallback在条件为 falsy 时显示,省略则不渲染任何内容- 当条件从
false变为true时,子元素不会被重新创建,而是重新插入 DOM(记忆化机制) when为 truthy 时,子元素函数可以接收when的值:
jsx<Show when={currentUser()}> {(user) => <p>欢迎,{user().name}</p>} </Show>
为什么不用 && 和三元表达式? 在 SolidJS 中,{condition() && <Component />} 虽然语法上可行,但每次条件变化时子元素会被销毁再重建。而 Show 会保留子元素的 DOM 引用,切回时直接复用,性能更优。
Switch / Match — 多条件分支
Switch 配合 Match 处理多个互斥条件,逻辑类似 JavaScript 的 switch/case。
jsximport { Switch, Match, createSignal } from "solid-js"; function StatusPanel() { const [status, setStatus] = createSignal("loading"); return ( <Switch fallback={<p>未知状态</p>}> <Match when={status() === "loading"}> <Spinner /> </Match> <Match when={status() === "success"}> <SuccessView /> </Match> <Match when={status() === "error"}> <ErrorNotice /> </Match> </Switch> ); }
注意事项:
Match按书写顺序评估,第一个when为 truthy 的分支胜出fallback是所有分支都不匹配时的默认内容- 嵌套
Show可以替代Switch,但当条件超过两个时Switch更清晰
For — 键控列表渲染
For 是 SolidJS 中推荐的列表渲染方式,通过引用相等性追踪每个列表项。
jsximport { For, createSignal } from "solid-js"; function TodoList() { const [todos, setTodos] = createSignal([ { id: 1, text: "学习 SolidJS" }, { id: 2, text: "构建项目" }, ]); return ( <ul> <For each={todos()}> {(todo) => <li>{todo.text}</li>} </For> </ul> ); }
For 与 Array.map 的区别:
| 特性 | <For> | array.map() |
|---|---|---|
| 更新策略 | 只更新变化的项 | 重新映射整个数组 |
| 键追踪 | 按引用相等自动追踪 | 无追踪 |
| DOM 复用 | 复用已有 DOM 节点 | 条件变化时可能重建 |
| 性能 | 大列表场景优势明显 | 小列表无感,大列表卡顿 |
当数组发生变化时,For 只会对新增、删除、移动的项操作 DOM,不会触及其他项的更新。这也是为什么 SolidJS 文档强烈推荐用 For 替代 map。
Index — 索引键控列表
Index 与 For 类似,但以索引位置作为键而非引用。
jsximport { Index } from "solid-js"; function ScoreBoard() { const [scores, setScores] = createSignal([95, 87, 72]); return ( <Index each={scores()}> {(score, i) => <div>第 {i + 1} 名:{score()} 分</div>} </Index> ); }
For 还是 Index?
- 用
For:列表项是对象,顺序可能变化(如拖拽排序),需要按身份追踪 - 用
Index:列表项是原始值(string/number),位置稳定,值会更新
Index 的子元素函数中,每项是 getter 函数(item()),因为值可能在该位置上变化。而 For 的子元素函数直接接收项对象本身,因为身份不变。
Dynamic — 动态组件选择
Dynamic 根据运行时条件选择渲染哪个组件,适合标签页、多态渲染等场景。
jsximport { Dynamic, createSignal } from "solid-js"; import Home from "./Home"; import About from "./About"; import Contact from "./Contact"; const tabs = { home: Home, about: About, contact: Contact }; function App() { const [activeTab, setActiveTab] = createSignal("home"); return ( <> <nav> {Object.keys(tabs).map((key) => ( <button onClick={() => setActiveTab(key)}>{key}</button> ))} </nav> <Dynamic component={tabs[activeTab()]} /> </> ); }
component 属性接收组件函数或组件字符串(如 "div"),还可以通过其他属性传递 props。
Portal — 跨 DOM 层级渲染
Portal 将子元素渲染到 DOM 中的其他位置,常用于模态框、通知、下拉菜单等需要脱离当前组件 DOM 层级的场景。
jsximport { Portal } from "solid-js/web"; function Modal() { return ( <Portal mount={document.getElementById("modal-root")}> <div class="modal-overlay"> <div class="modal-content">模态框内容</div> </div> </Portal> ); }
Portal 解决的核心问题是 CSS 层叠上下文——模态框不会被父元素的 overflow: hidden 或 z-index 裁切。
Suspense — 异步加载协调
Suspense 配合资源(createResource)使用,在异步数据加载完成前显示 fallback。
jsximport { Suspense, createResource } from "solid-js"; function UserProfile() { const [user] = createResource(fetchUser); return ( <Suspense fallback={<Skeleton />}> <h2>{user().name}</h2> <p>{user().bio}</p> </Suspense> ); }
嵌套 Suspense:
jsx<Suspense fallback={<PageSkeleton />}> <Suspense fallback={<AvatarSkeleton />}> <UserAvatar /> </Suspense> <Suspense fallback={<FeedSkeleton />}> <PostFeed /> </Suspense> </Suspense>
外层 Suspense 等待所有子资源加载完成,内层各自独立 fallback,互不阻塞。这种模式让页面不同区域可以独立加载,而不是整个页面白屏等待。
ErrorBoundary — 错误边界
ErrorBoundary 捕获子组件树中的运行时错误,防止整个应用崩溃。
jsximport { ErrorBoundary } from "solid-js"; function App() { return ( <ErrorBoundary fallback={(err, reset) => ( <div> <p>出错了:{err.message}</p> <button onClick={reset}>重试</button> </div> )}> <UnstableComponent /> </ErrorBoundary> ); }
fallback 接收错误对象和 reset 函数,调用 reset 可以重新渲染子组件树。
组件选择速查
| 场景 | 推荐组件 |
|---|---|
| 简单条件显示/隐藏 | Show |
| 多个互斥条件 | Switch + Match |
| 渲染对象数组 | For |
| 渲染原始值数组 | Index |
| 运行时切换组件 | Dynamic |
| 模态框/弹窗/通知 | Portal |
| 异步数据加载 | Suspense |
| 运行时错误兜底 | ErrorBoundary |
核心原则: SolidJS 的控制流组件不是语法糖,它们与响应式系统深度集成,是精确追踪依赖和最小化 DOM 更新的关键。在实际开发中,优先使用这些组件而非 JavaScript 表达式,才能充分发挥 SolidJS 的细粒度响应式性能优势。