条件渲染 #

一、条件渲染概述 #

1.1 Solid 的条件渲染方式 #

Solid 提供了多种条件渲染方式,最推荐使用内置的控制流组件。

text
┌─────────────────────────────────────────────────────────────┐
│                    条件渲染方式对比                          │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   1. Show 组件(推荐)                                      │
│      └── 简单条件渲染                                       │
│      └── 支持 fallback                                      │
│                                                             │
│   2. Switch/Match 组件                                      │
│      └── 多条件分支                                         │
│      └── 类似 switch-case                                   │
│                                                             │
│   3. 三元表达式                                             │
│      └── 简单条件                                           │
│      └── JSX 内联使用                                       │
│                                                             │
│   4. 逻辑与 (&&)                                            │
│      └── 条件显示                                           │
│      └── 注意 falsy 值                                      │
│                                                             │
└─────────────────────────────────────────────────────────────┘

二、Show 组件 #

2.1 基本用法 #

jsx
import { Show } from 'solid-js';

function Example() {
  const [loggedIn, setLoggedIn] = createSignal(false);

  return (
    <div>
      <Show when={loggedIn()}>
        <p>Welcome back!</p>
      </Show>
    </div>
  );
}

2.2 带 fallback #

jsx
function Example() {
  const [user, setUser] = createSignal(null);

  return (
    <Show
      when={user()}
      fallback={<p>Please log in</p>}
    >
      {(userData) => (
        <div>
          <h1>Hello, {userData().name}!</h1>
          <p>Email: {userData().email}</p>
        </div>
      )}
    </Show>
  );
}

2.3 keyed 模式 #

jsx
function UserList() {
  const [selectedId, setSelectedId] = createSignal(1);
  const [users] = createResource(() => selectedId(), fetchUser);

  return (
    <div>
      <Show when={users()} keyed>
        {(user) => (
          <div>
            <h2>{user.name}</h2>
            <p>{user.email}</p>
          </div>
        )}
      </Show>
    </div>
  );
}

2.4 嵌套 Show #

jsx
function Permissions() {
  const [user, setUser] = createSignal(null);
  const [isAdmin, setIsAdmin] = createSignal(false);

  return (
    <Show when={user()} fallback={<LoginButton />}>
      <Show when={isAdmin()} fallback={<UserDashboard />}>
        <AdminDashboard />
      </Show>
    </Show>
  );
}

2.5 Show 的优势 #

jsx
// 使用 Show:DOM 元素被正确管理
<Show when={show()}>
  <ExpensiveComponent />
</Show>

// 使用三元表达式:每次切换都会重新创建
{show() ? <ExpensiveComponent /> : null}

三、Switch/Match 组件 #

3.1 基本用法 #

jsx
import { Switch, Match } from 'solid-js';

function StatusMessage() {
  const [status, setStatus] = createSignal('loading');

  return (
    <Switch>
      <Match when={status() === 'loading'}>
        <p>Loading...</p>
      </Match>
      <Match when={status() === 'success'}>
        <p>Operation completed successfully!</p>
      </Match>
      <Match when={status() === 'error'}>
        <p>An error occurred!</p>
      </Match>
    </Switch>
  );
}

3.2 带 fallback #

jsx
function RouteHandler() {
  const [route, setRoute] = createSignal('/home');

  return (
    <Switch fallback={<NotFound />}>
      <Match when={route() === '/home'}>
        <HomePage />
      </Match>
      <Match when={route() === '/about'}>
        <AboutPage />
      </Match>
      <Match when={route() === '/contact'}>
        <ContactPage />
      </Match>
    </Switch>
  );
}

3.3 使用状态值 #

jsx
function UserRole() {
  const [user, setUser] = createSignal({ role: 'admin' });

  return (
    <Switch fallback={<p>Unknown role</p>}>
      <Match when={user().role === 'admin'}>
        <AdminPanel />
      </Match>
      <Match when={user().role === 'editor'}>
        <EditorPanel />
      </Match>
      <Match when={user().role === 'viewer'}>
        <ViewerPanel />
      </Match>
    </Switch>
  );
}

3.4 复杂条件 #

jsx
function PermissionGate() {
  const [user, setUser] = createSignal(null);
  const [permissions, setPermissions] = createSignal([]);

  return (
    <Switch>
      <Match when={!user()}>
        <LoginPrompt />
      </Match>
      <Match when={permissions().includes('admin')}>
        <AdminControls />
      </Match>
      <Match when={permissions().includes('write')}>
        <EditorControls />
      </Match>
      <Match when={permissions().includes('read')}>
        <ReadOnlyView />
      </Match>
    </Switch>
  );
}

四、三元表达式 #

4.1 基本用法 #

jsx
function Example() {
  const [isLoggedIn, setIsLoggedIn] = createSignal(false);

  return (
    <div>
      {isLoggedIn() ? <Dashboard /> : <LoginPage />}
    </div>
  );
}

4.2 内联使用 #

jsx
function Button() {
  const [loading, setLoading] = createSignal(false);

  return (
    <button disabled={loading()}>
      {loading() ? 'Loading...' : 'Submit'}
    </button>
  );
}

4.3 嵌套三元 #

jsx
// 不推荐:难以阅读
function BadExample() {
  return (
    <div>
      {status() === 'loading' ? (
        <Loading />
      ) : status() === 'error' ? (
        <Error />
      ) : (
        <Content />
      )}
    </div>
  );
}

// 推荐:使用 Switch
function GoodExample() {
  return (
    <Switch>
      <Match when={status() === 'loading'}><Loading /></Match>
      <Match when={status() === 'error'}><Error /></Match>
      <Match when={true}><Content /></Match>
    </Switch>
  );
}

五、逻辑与 (&&) #

5.1 基本用法 #

jsx
function Notifications() {
  const [count, setCount] = createSignal(5);

  return (
    <div>
      {count() > 0 && (
        <span class="badge">{count()}</span>
      )}
    </div>
  );
}

5.2 注意 falsy 值 #

jsx
// 问题:0 会被渲染
function BadExample() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      {count() && <span>{count()}</span>}
      {/* 当 count 为 0 时,会显示 0 */}
    </div>
  );
}

// 解决方案:使用显式条件
function GoodExample() {
  const [count, setCount] = createSignal(0);

  return (
    <div>
      {count() > 0 && <span>{count()}</span>}
    </div>
  );
}

// 或者使用 Show
function BetterExample() {
  const [count, setCount] = createSignal(0);

  return (
    <Show when={count() > 0}>
      <span>{count()}</span>
    </Show>
  );
}

六、实际应用 #

6.1 加载状态 #

jsx
function DataFetcher({ url }) {
  const [data] = createResource(() => url(), fetchData);

  return (
    <Switch>
      <Match when={data.loading}>
        <LoadingSpinner />
      </Match>
      <Match when={data.error}>
        <ErrorMessage error={data.error} />
      </Match>
      <Match when={data()}>
        {(result) => (
          <DataDisplay data={result()} />
        )}
      </Match>
    </Switch>
  );
}

6.2 表单验证 #

jsx
function FormField({ label, error, ...props }) {
  return (
    <div class="form-field">
      <label>{label}</label>
      <input {...props} class={error() ? 'error' : ''} />
      <Show when={error()}>
        <span class="error-message">{error()}</span>
      </Show>
    </div>
  );
}

6.3 权限控制 #

jsx
function ProtectedRoute({ permission, children }) {
  const { hasPermission } = useAuth();

  return (
    <Show
      when={hasPermission(permission)}
      fallback={<AccessDenied />}
    >
      {children}
    </Show>
  );
}

// 使用
<ProtectedRoute permission="admin">
  <AdminPanel />
</ProtectedRoute>

6.4 标签页切换 #

jsx
function Tabs({ tabs }) {
  const [activeTab, setActiveTab] = createSignal(0);

  return (
    <div>
      <div class="tab-header">
        <For each={tabs}>
          {(tab, index) => (
            <button
              class={activeTab() === index() ? 'active' : ''}
              onClick={() => setActiveTab(index())}
            >
              {tab.label}
            </button>
          )}
        </For>
      </div>
      <div class="tab-content">
        <Switch>
          <For each={tabs}>
            {(tab, index) => (
              <Match when={activeTab() === index()}>
                {tab.content}
              </Match>
            )}
          </For>
        </Switch>
      </div>
    </div>
  );
}

6.5 空状态处理 #

jsx
function TodoList() {
  const [todos, setTodos] = createSignal([]);

  return (
    <div>
      <Show
        when={todos().length > 0}
        fallback={
          <div class="empty-state">
            <p>No todos yet. Add one to get started!</p>
          </div>
        }
      >
        <ul>
          <For each={todos()}>
            {(todo) => <TodoItem todo={todo} />}
          </For>
        </ul>
      </Show>
    </div>
  );
}

七、性能考虑 #

7.1 Show vs 三元表达式 #

jsx
// Show:保持 DOM 状态
<Show when={show()}>
  <ExpensiveComponent />
</Show>

// 三元:每次切换重新创建
{show() ? <ExpensiveComponent /> : null}

7.2 条件渲染的位置 #

jsx
// 推荐:在组件外部判断
function Parent() {
  const [show, setShow] = createSignal(false);

  return (
    <Show when={show()}>
      <ExpensiveComponent />
    </Show>
  );
}

// 不推荐:在组件内部判断
function ExpensiveComponent() {
  const [show, setShow] = createSignal(false);

  if (!show()) return null; // 组件仍然会被创建

  return <div>...</div>;
}

八、最佳实践 #

8.1 选择合适的条件渲染方式 #

场景 推荐方式
简单条件 Show
多条件分支 Switch/Match
内联小内容 三元表达式
条件显示 Show 或 &&

8.2 代码组织 #

jsx
// 推荐:条件渲染逻辑清晰
function UserStatus() {
  const { user, loading, error } = useAuth();

  return (
    <div class="user-status">
      <Switch>
        <Match when={loading()}>
          <LoadingIndicator />
        </Match>
        <Match when={error()}>
          <ErrorDisplay error={error()} />
        </Match>
        <Match when={user()}>
          {(u) => <UserProfile user={u()} />}
        </Match>
      </Switch>
    </div>
  );
}

九、总结 #

9.1 条件渲染 API #

API 说明
Show 条件渲染组件
Switch 多条件容器
Match 条件分支
when 条件表达式
fallback 默认内容

9.2 最佳实践 #

  1. 优先使用 Show:简单条件渲染
  2. 使用 Switch/Match:多条件分支
  3. 避免嵌套三元:使用 Switch 替代
  4. 注意 falsy 值:使用显式条件
  5. 考虑性能:Show 保持 DOM 状态
最后更新:2026-03-28