SolidJS 不建议在 JSX 中使用 JS 的 if/else、三元表达式或 && 来做控制流,而是提供了一套专用的控制流组件。原因是这些组件经过编译器优化,比普通的 JS 运算符更高效——它们只更新需要更新的部分,不会销毁整个节点树。
1. <Show> — 条件渲染
<Show> 是最常用的控制流组件,类似 v-if。当 when 条件为真时渲染 children,否则渲染 fallback(可选)。
import { Show } from "solid-js";
function UserProfile() {
const [user, setUser] = createSignal<User | null>(null);
const [loading, setLoading] = createSignal(true);
return (
<div>
{/* 基本用法 */}
<Show when={!loading()} fallback={<p>加载中...</p>}>
<p>加载完成!</p>
</Show>
{/* keyed 模式:when 变化时重建子节点(而不是复用) */}
<Show when={user()} keyed fallback={<p>未登录</p>}>
{(u) => <p>欢迎,{u.name}!</p>}
{/* keyed 时,children 作为函数接收 when 的非空值 */}
</Show>
</div>
);
}
Show vs 三元运算符:{condition() ? <A /> : <B />} 也能工作,但 <Show> 更推荐。Show 会在条件切换时正确销毁和重建子组件(触发 onCleanup),三元运算符可能不会。
2. <For> — 高性能列表渲染
<For> 是渲染列表的推荐方式。它通过引用相等性识别列表项,当数组变化时只更新真正发生变化的项,不会重新渲染整个列表。
import { For } from "solid-js";
interface Task { id: number; text: string; done: boolean; }
function TaskList() {
const [tasks, setTasks] = createSignal<Task[]>([
{ id: 1, text: "学习 SolidJS", done: false },
{ id: 2, text: "构建应用", done: false },
]);
return (
<ul>
<For each={tasks()} fallback={<li>暂无任务</li>}>
{(task, index) => (
{/* task 是响应式 getter,index 也是响应式 getter */}
<li>
<span>{index() + 1}. {task.text}</span>
<span>{task.done ? "✅" : "⬜"}</span>
</li>
)}
</For>
</ul>
);
}
For 的 key 策略:<For> 使用对象引用相等来追踪列表项,而不是 React 那样的 key 属性。这意味着:更新某项的属性时,必须替换整个对象(返回新引用),否则 For 检测不到变化。如果需要按索引追踪,使用 <Index>。
3. <Index> — 按位置追踪的列表
<Index> 与 <For> 的区别:<For> 按项的引用追踪(项的位置可变),<Index> 按索引追踪(位置固定,项内容可响应式变化)。
import { Index } from "solid-js";
function NumberGrid() {
const [nums, setNums] = createSignal([1, 2, 3, 4, 5]);
return (
<div>
<Index each={nums()}>
{/* item 是 Signal getter(响应式),index 是普通数字 */}
{(item, index) => (
<span>[{index}]={item()}</span>
)}
</Index>
</div>
);
}
// 何时用 For,何时用 Index?
// For:列表项是对象,有唯一 id,支持重排
// Index:列表项是原始值(数字/字符串),或位置固定不重排
4. <Switch> / <Match> — 多分支条件
当有多个条件分支时,<Switch> + <Match> 比嵌套 <Show> 更清晰:
import { Switch, Match } from "solid-js";
type Status = "idle" | "loading" | "success" | "error";
function StatusDisplay() {
const [status, setStatus] = createSignal<Status>("idle");
return (
<Switch fallback={<p>未知状态</p>}>
<Match when={status() === "idle"}>
<p>点击按钮开始</p>
</Match>
<Match when={status() === "loading"}>
<p>正在加载... <span class="spinner">⏳</span></p>
</Match>
<Match when={status() === "success"}>
<p style="color:green">操作成功!</p>
</Match>
<Match when={status() === "error"}>
<p style="color:red">发生错误,请重试</p>
</Match>
</Switch>
);
}
5. <Dynamic> — 动态组件
<Dynamic> 允许根据响应式值动态决定渲染哪个组件或 HTML 标签,类似 Vue 的 <component :is="...">:
import { Dynamic } from "solid-js/web";
const components = {
text: TextWidget,
image: ImageWidget,
video: VideoWidget,
} as const;
function WidgetRenderer() {
const [type, setType] = createSignal<keyof typeof components>("text");
return (
<div>
{/* 动态切换组件 */}
<Dynamic component={components[type()]} data="示例数据" />
{/* 也可以动态切换 HTML 标签 */}
<Dynamic
component={type() === "text" ? "p" : "div"}
class="widget"
>
内容
</Dynamic>
</div>
);
}
6. <Portal> — 传送门
<Portal> 将子节点渲染到 DOM 树中的另一个位置(通常是 body),常用于模态框、Tooltip、下拉菜单等需要脱离容器层叠上下文的 UI。
import { Portal } from "solid-js/web";
function Modal() {
const [open, setOpen] = createSignal(false);
return (
<div>
<button onClick={() => setOpen(true)}>打开模态框</button>
<Show when={open()}>
{/* Portal 将内容挂载到 document.body,而不是当前 DOM 位置 */}
<Portal>
<div class="modal-overlay" onClick={() => setOpen(false)}>
<div class="modal" onClick={(e) => e.stopPropagation()}>
<h2>模态框标题</h2>
<p>模态框内容</p>
<button onClick={() => setOpen(false)}>关闭</button>
</div>
</div>
</Portal>
</Show>
</div>
);
}
7. <ErrorBoundary> — 错误边界
<ErrorBoundary> 捕获子组件树中的渲染错误,显示备用 UI 而不是让整个应用崩溃:
import { ErrorBoundary } from "solid-js";
function App() {
return (
<ErrorBoundary
fallback={(err, reset) => (
<div class="error-fallback">
<h2>出错了 😢</h2>
<p>{err.message}</p>
<button onClick={reset}>重试</button>
</div>
)}
>
<DataFetchingComponent />
<AnotherComponent />
</ErrorBoundary>
);
}
| 组件 | 用途 | React 等价 |
|---|---|---|
<Show> | 条件渲染 | condition && ... / 三元 |
<For> | 列表渲染(按引用追踪) | array.map() |
<Index> | 列表渲染(按位置追踪) | array.map() |
<Switch>/<Match> | 多分支条件 | switch/case 逻辑 |
<Dynamic> | 动态组件/标签 | React.createElement(type) |
<Portal> | DOM 传送门 | ReactDOM.createPortal() |
<ErrorBoundary> | 错误边界 | ComponentDidCatch |
本章小结:SolidJS 的控制流组件是经过编译器优化的高效原语。For 按引用追踪、Index 按位置追踪,Switch/Match 处理多分支,Dynamic 动态选择组件,Portal 传送到任意 DOM 位置,ErrorBoundary 防止崩溃。优先使用这些组件而非 JS 运算符。