5月27日 16:01

SolidJS 中的控制流组件有哪些?如何使用 Show、For、Switch 等?

SolidJS 的控制流组件是框架响应式系统的核心部分,与 React 中直接使用 JavaScript 表达式不同,SolidJS 通过专用组件让响应式追踪更加精确,从而实现细粒度的 DOM 更新。下面逐一介绍每个控制流组件的用法和设计原理。

Show — 条件渲染

Show 是最常用的控制流组件,用于根据条件决定是否渲染内容。

jsx
import { 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

jsx
import { 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 中推荐的列表渲染方式,通过引用相等性追踪每个列表项。

jsx
import { 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 — 索引键控列表

IndexFor 类似,但以索引位置作为键而非引用。

jsx
import { 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 根据运行时条件选择渲染哪个组件,适合标签页、多态渲染等场景。

jsx
import { 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 层级的场景。

jsx
import { 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: hiddenz-index 裁切。

Suspense — 异步加载协调

Suspense 配合资源(createResource)使用,在异步数据加载完成前显示 fallback。

jsx
import { 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 捕获子组件树中的运行时错误,防止整个应用崩溃。

jsx
import { 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 的细粒度响应式性能优势。

标签:SolidJS