DevToolBox免费
博客

SolidJS 完全指南:细粒度响应式 UI 框架 (2026)

19 分钟阅读作者 DevToolBox Team

SolidJS 是一个声明式 JavaScript 库,用于构建用户界面,它编译为真实 DOM 操作,没有虚拟 DOM 开销。其细粒度响应式系统精确追踪 UI 的哪些部分依赖哪些状态,只更新需要变化的精确 DOM 节点。SolidJS 的性能与手工优化的原生 JavaScript 相当,同时提供 React 开发者熟悉的组件模型和 JSX 语法。

TL;DR

SolidJS 是一个没有虚拟 DOM 的响应式 UI 库,使用细粒度信号进行精确 DOM 更新。它将 JSX 编译为直接的 DOM 指令,提供接近原生 JS 的性能。核心原语包括 createSignal、createEffect 和 createMemo。SolidStart 提供全栈元框架,支持文件路由、SSR 和流式渲染。Solid 在 JS 框架基准测试中始终名列前茅。

Key Takeaways
  • SolidJS 使用细粒度响应式信号,只更新受影响的精确 DOM 节点,无需虚拟 DOM 差异比较。
  • Solid 中的组件在设置期间只运行一次,不像 React 那样每次状态变化都重新渲染。
  • SolidJS 在构建时将 JSX 编译为优化的真实 DOM 操作,性能与手写原生 JavaScript 相当。
  • 响应式原语(createSignal、createMemo、createEffect)自然组合,构成所有状态管理的基础。
  • SolidStart 是官方元框架,提供文件路由、服务端渲染、流式传输和 API 路由。
  • 从 React 迁移到 Solid 相对容易,因为 Solid 使用 JSX 和类似的组件模型,但响应式语义有显著差异。

什么是 SolidJS 和细粒度响应式?

SolidJS 是由 Ryan Carniato 创建的 UI 库,采用了与 React 截然不同的方法。React 使用虚拟 DOM 来差异比较和批量更新,而 Solid 将 JSX 模板编译为高效的真实 DOM 创建和更新代码。没有协调步骤,没有差异算法。

细粒度响应式意味着框架在单个表达式级别追踪依赖,而不是在组件级别。Solid 中的组件函数只执行一次来建立响应式图。之后更新自动通过图传播,无需重新运行组件。

// How Solid compiles JSX - conceptual overview
// Your JSX code:
function Counter() {
  const [count, setCount] = createSignal(0);
  return <button onClick={() => setCount(c => c + 1)}>
    Clicks: {count()}
  </button>;
}

// Solid compiles this to (simplified):
function Counter() {
  const [count, setCount] = createSignal(0);
  const button = document.createElement("button");
  button.onclick = () => setCount(c => c + 1);
  const text = document.createTextNode("");
  // Only this expression re-runs when count changes:
  createEffect(() => text.data = "Clicks: " + count());
  button.appendChild(text);
  return button;
}

SolidJS vs React:关键差异

虽然 Solid 和 React 共享 JSX 语法和基于组件的架构,但它们的执行模型有根本性的不同。

特性SolidJSReact
渲染模型细粒度响应式 DOM 更新虚拟 DOM 差异比较和协调
组件执行设置时只运行一次每次状态变化都重新运行
状态原语信号(getter/setter 元组)useState 钩子(值/setter)
记忆化不需要(无陈旧闭包)需要 useMemo、useCallback
包体积~7 KB gzipped~42 KB gzipped(含 react-dom)
JSX 编译编译为 DOM 指令编译为 createElement 调用
控制流内置组件(Show、For)JavaScript 表达式(三元运算、map)
元框架SolidStartNext.js、Remix

信号、备忘录和副作用

Solid 提供三个核心响应式原语。信号保存响应式值,备忘录高效派生计算值,副作用在依赖变化时运行。

createSignal:响应式状态

信号是一个响应式值容器,返回 getter 函数和 setter 函数。在响应式上下文中读取 getter 会自动订阅变化。

import { createSignal } from "solid-js";

function Counter() {
  // Returns [getter, setter] - getter is a function!
  const [count, setCount] = createSignal(0);
  const [name, setName] = createSignal("World");

  return (
    <div>
      {/* count() calls the getter function */}
      <p>Count: {count()}</p>
      <p>Hello, {name()}!</p>

      {/* Setter accepts value or updater function */}
      <button onClick={() => setCount(count() + 1)}>+1</button>
      <button onClick={() => setCount(c => c + 1)}>+1 (updater)</button>
      <input
        value={name()}
        onInput={(e) => setName(e.target.value)}
      />
    </div>
  );
}

createMemo:派生计算

备忘录是一个缓存的派生值,只在依赖变化时重新计算。与 React useMemo 不同,Solid 备忘录是真正响应式的。

import { createSignal, createMemo } from "solid-js";

function FilteredList() {
  const [items, setItems] = createSignal([
    { name: "Apple", category: "fruit" },
    { name: "Carrot", category: "vegetable" },
    { name: "Banana", category: "fruit" },
  ]);
  const [filter, setFilter] = createSignal("fruit");

  // Only recalculates when items() or filter() change
  const filtered = createMemo(() =>
    items().filter(item => item.category === filter())
  );

  // Derived from memo - cached, no redundant work
  const count = createMemo(() => filtered().length);

  return (
    <div>
      <p>Showing {count()} items</p>
      <For each={filtered()}>
        {(item) => <p>{item.name}</p>}
      </For>
    </div>
  );
}

createEffect:副作用

副作用在其追踪的依赖变化时运行函数。它自动检测读取了哪些信号,只在那些信号更新时重新运行。

import { createSignal, createEffect, onCleanup } from "solid-js";

function Timer() {
  const [count, setCount] = createSignal(0);
  const [running, setRunning] = createSignal(false);

  // Effect tracks running() automatically
  createEffect(() => {
    if (running()) {
      const id = setInterval(() => setCount(c => c + 1), 1000);
      // Cleanup runs before next effect or on disposal
      onCleanup(() => clearInterval(id));
    }
  });

  // Effect for document title - tracks count()
  createEffect(() => {
    document.title = "Count: " + count();
  });

  return (
    <div>
      <p>{count()}</p>
      <button onClick={() => setRunning(r => !r)}>
        {running() ? "Stop" : "Start"}
      </button>
    </div>
  );
}

组件和 JSX(无虚拟 DOM)

Solid 组件是返回 JSX 的普通函数。关键区别是 Solid 组件函数只执行一次,建立响应式绑定并返回 DOM 结构。

因为组件不重新执行,所以没有陈旧闭包、不需要依赖数组、不需要 useCallback 或 useMemo。访问 props 时不要解构。

import { createSignal } from "solid-js";

// Props must NOT be destructured at parameter level
// BAD:  function Greeting({ name, greeting }) { ... }
// GOOD: function Greeting(props) { ... }

function Greeting(props: { name: string; greeting?: string }) {
  // Access props inside JSX to keep reactivity
  return (
    <p>{props.greeting || "Hello"}, {props.name}!</p>
  );
}

function App() {
  const [name, setName] = createSignal("Solid");

  // This component function runs ONCE
  console.log("App setup - runs only once");

  return (
    <div>
      {/* When name() changes, only the text node updates */}
      <Greeting name={name()} greeting="Welcome" />
      <input
        value={name()}
        onInput={(e) => setName(e.target.value)}
      />
    </div>
  );
}

控制流组件

Solid 提供内置控制流组件,而不是依赖 JavaScript 表达式。这些组件为细粒度响应式优化,确保 DOM 节点的高效创建和销毁。

Show:条件渲染

Show 组件在条件为真时渲染子元素,支持 fallback 属性作为 else 分支。

import { Show, createSignal } from "solid-js";

function UserProfile() {
  const [user, setUser] = createSignal(null);
  const [loading, setLoading] = createSignal(true);

  return (
    <div>
      <Show when={loading()} fallback={
        <Show when={user()} fallback={<p>No user found</p>}>
          {(u) => (
            <div>
              <h2>{u().name}</h2>
              <p>{u().email}</p>
            </div>
          )}
        </Show>
      }>
        <p>Loading...</p>
      </Show>
    </div>
  );
}

For:列表渲染

For 组件遍历数组并渲染每个项目。它使用引用身份追踪项目,只更新实际变化的项目。

import { For, createSignal } from "solid-js";

function TodoList() {
  const [todos, setTodos] = createSignal([
    { id: 1, text: "Learn Solid", done: false },
    { id: 2, text: "Build an app", done: false },
  ]);

  const addTodo = (text: string) => {
    setTodos(prev => [...prev, {
      id: Date.now(), text, done: false
    }]);
  };

  return (
    <ul>
      {/* For tracks items by reference */}
      <For each={todos()}>
        {(todo, index) => (
          <li>
            {index() + 1}. {todo.text}
            {todo.done ? " (done)" : ""}
          </li>
        )}
      </For>
    </ul>
  );
}

Switch/Match:多分支条件

Switch 和 Match 提供模式匹配风格的条件渲染,比嵌套三元运算更清晰。

import { Switch, Match, createSignal } from "solid-js";

function StatusBadge() {
  const [status, setStatus] = createSignal("loading");

  return (
    <Switch fallback={<span>Unknown status</span>}>
      <Match when={status() === "loading"}>
        <span style={{ color: "orange" }}>Loading...</span>
      </Match>
      <Match when={status() === "success"}>
        <span style={{ color: "green" }}>Success</span>
      </Match>
      <Match when={status() === "error"}>
        <span style={{ color: "red" }}>Error</span>
      </Match>
    </Switch>
  );
}

存储与嵌套响应式

对于复杂的嵌套对象和数组状态,Solid 提供存储。存储是深度响应式代理对象,每个属性都被单独追踪。

存储使用基于路径的 setter API,使不可变风格的更新更加人性化。

import { createStore, produce } from "solid-js/store";

function TodoApp() {
  const [state, setState] = createStore({
    user: { name: "Alice", theme: "dark" },
    todos: [
      { id: 1, text: "Learn Solid stores", done: false },
      { id: 2, text: "Build something", done: false },
    ],
    filter: "all" as "all" | "active" | "done",
  });

  // Path-based updates - only affected subscribers update
  const toggleTodo = (id: number) => {
    setState("todos", t => t.id === id, "done", d => !d);
  };

  // Using produce for mutable-style syntax
  const addTodo = (text: string) => {
    setState(produce(s => {
      s.todos.push({ id: Date.now(), text, done: false });
    }));
  };

  // Nested update - only rerenders theme subscribers
  const toggleTheme = () => {
    setState("user", "theme", t => t === "dark" ? "light" : "dark");
  };

  return (
    <div>
      <p>Theme: {state.user.theme}</p>
      <For each={state.todos}>
        {(todo) => (
          <div onClick={() => toggleTodo(todo.id)}>
            {todo.text} {todo.done ? "(done)" : ""}
          </div>
        )}
      </For>
    </div>
  );
}

上下文与依赖注入

Solid 提供类似 React 的上下文 API,用于在组件树中传递数据而无需逐层传递 props。如果上下文值是信号或存储,下游消费者仍会自动响应变化。

import { createContext, useContext, createSignal } from "solid-js";
import type { ParentComponent } from "solid-js";

// Define the context with a type
type ThemeContextType = {
  theme: () => string;
  setTheme: (t: string) => void;
};

const ThemeContext = createContext<ThemeContextType>();

// Provider component
const ThemeProvider: ParentComponent = (props) => {
  const [theme, setTheme] = createSignal("light");
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      {props.children}
    </ThemeContext.Provider>
  );
};

// Consumer hook
function useTheme() {
  const ctx = useContext(ThemeContext);
  if (!ctx) throw new Error("useTheme: no ThemeProvider");
  return ctx;
}

// Usage in a component
function ThemeToggle() {
  const { theme, setTheme } = useTheme();
  return (
    <button onClick={() => setTheme(
      theme() === "light" ? "dark" : "light"
    )}>
      Current: {theme()}
    </button>
  );
}

SolidStart:元框架

SolidStart 是 SolidJS 的官方元框架,类似于 React 的 Next.js 或 Vue 的 Nuxt。它提供文件路由、服务端渲染、流式传输、API 路由和构建优化。

SolidStart 支持多种渲染模式,包括流式 SSR、静态站点生成和客户端渲染。它使用 Vinxi 作为服务层。

文件路由

路由由 src/routes 目录中的文件结构定义,支持动态参数、捕获全部路由、路由分组和嵌套布局。

// SolidStart file-based routing structure
src/routes/
  index.tsx          // -> /
  about.tsx          // -> /about
  blog/
    index.tsx        // -> /blog
    [slug].tsx       // -> /blog/:slug
  users/
    [id].tsx         // -> /users/:id
    [...rest].tsx    // -> /users/* (catch-all)
  (auth)/            // route group (no URL segment)
    login.tsx        // -> /login
    register.tsx     // -> /register
// src/routes/blog/[slug].tsx
import { useParams } from "@solidjs/router";
import { createAsync } from "@solidjs/router";

const getPost = async (slug: string) => {
  "use server";
  return db.query("SELECT * FROM posts WHERE slug = ?", [slug]);
};

export default function BlogPost() {
  const params = useParams();
  const post = createAsync(() => getPost(params.slug));

  return (
    <Show when={post()}>
      {(p) => (
        <article>
          <h1>{p().title}</h1>
          <div innerHTML={p().content} />
        </article>
      )}
    </Show>
  );
}

服务器函数

SolidStart 提供仅在服务器上运行但可从客户端调用的服务器函数,使用 "use server" 指令定义。

// Server functions with "use server" directive
import { action, redirect } from "@solidjs/router";

// Server function for data fetching
async function getUsers() {
  "use server";
  return db.query("SELECT id, name, email FROM users");
}

// Server action for mutations
const createUser = action(async (formData: FormData) => {
  "use server";
  const name = formData.get("name") as string;
  const email = formData.get("email") as string;
  await db.query(
    "INSERT INTO users (name, email) VALUES (?, ?)",
    [name, email]
  );
  throw redirect("/users");
});

// Using in a component
export default function NewUser() {
  return (
    <form action={createUser} method="post">
      <input name="name" placeholder="Name" />
      <input name="email" type="email" placeholder="Email" />
      <button type="submit">Create User</button>
    </form>
  );
}

Resource API 异步数据

createResource 原语处理异步数据获取,内置加载和错误状态,与 Suspense 集成实现声明式加载 UI。

import { createResource, createSignal, Suspense, ErrorBoundary } from "solid-js";

const fetchUser = async (id: number) => {
  const res = await fetch("/api/users/" + id);
  if (!res.ok) throw new Error("Failed to fetch user");
  return res.json();
};

function UserCard() {
  const [userId, setUserId] = createSignal(1);

  // Re-fetches when userId() changes
  const [user, { refetch, mutate }] = createResource(
    userId,
    fetchUser
  );

  return (
    <ErrorBoundary fallback={(err) => <p>Error: {err.message}</p>}>
      <Suspense fallback={<p>Loading user...</p>}>
        <Show when={user()}>
          {(u) => (
            <div>
              <h2>{u().name}</h2>
              <p>{u().email}</p>
              <button onClick={refetch}>Refresh</button>
            </div>
          )}
        </Show>
      </Suspense>
      <button onClick={() => setUserId(id => id + 1)}>
        Next User
      </button>
    </ErrorBoundary>
  );
}

TypeScript 集成

SolidJS 拥有一流的 TypeScript 支持。信号、存储、组件和所有 API 都有完整类型。

组件 props 使用标准 TypeScript 接口或类型别名进行类型定义。泛型组件、联合类型 props 和严格事件类型都能自然工作。

import { Component, JSX } from "solid-js";

// Typed component props
interface ButtonProps {
  variant: "primary" | "secondary" | "danger";
  size?: "sm" | "md" | "lg";
  disabled?: boolean;
  onClick?: JSX.EventHandlerUnion<HTMLButtonElement, MouseEvent>;
  children: JSX.Element;
}

const Button: Component<ButtonProps> = (props) => {
  return (
    <button
      disabled={props.disabled}
      onClick={props.onClick}
      data-variant={props.variant}
      data-size={props.size || "md"}
    >
      {props.children}
    </button>
  );
};

性能基准:为什么 Solid 快

SolidJS 在 JavaScript 框架基准测试中始终名列前茅。在 JS Framework Benchmark 中,Solid 的性能与手工优化的原生 JavaScript 相差不到几个百分点。原因如下:

  • 无虚拟 DOM 开销:更新直接作用于真实 DOM,无需差异比较。
  • 编译时优化:JSX 编译为高效的 DOM 创建代码。
  • 细粒度追踪:只重新计算依赖于变化数据的精确表达式。
  • 无组件重新执行:组件函数只运行一次,无需重新渲染整个子树。
  • 最小内存分配:更新时不分配中间虚拟 DOM 树结构。
  • 小包体积:Solid 核心约 7 KB gzipped,而 React + ReactDOM 约 42 KB。
// Benchmark-style comparison (conceptual)
// Creating 1000 rows and updating every 10th row

// React: re-renders entire list component
// - Calls component function
// - Creates new virtual DOM tree (1000 nodes)
// - Diffs old vs new tree
// - Patches 100 real DOM nodes

// Solid: updates only affected DOM nodes
// - No component re-execution
// - No virtual DOM tree allocation
// - No diffing algorithm
// - Directly updates 100 text nodes via signal subscriptions

// JS Framework Benchmark results (lower is better):
// vanilla JS:    1.00 (baseline)
// Solid:         1.04
// Svelte:        1.19
// Vue:           1.36
// React:         1.55
// Angular:       1.42

生态系统和社区

Solid 生态系统已快速成长,包含常用开发需求的关键库:

  • solid-router - undefined
  • solid-primitives - undefined
  • Kobalte - undefined
  • solid-query - undefined
  • solid-testing-library - undefined
  • solid-transition-group - undefined
  • solid-markdown - undefined
// Getting started with SolidJS
# Create a new Solid project
npx degit solidjs/templates/ts my-solid-app
cd my-solid-app
npm install
npm run dev

# Create a SolidStart project
npm init solid@latest my-solid-start-app
cd my-solid-start-app
npm install
npm run dev

# Key packages to install
npm install @solidjs/router          # routing
npm install solid-primitives          # utility primitives
npm install @kobalte/core            # UI components

从 React 迁移到 Solid

从 React 迁移到 Solid 比转向完全不同的范式更容易,因为两者都使用 JSX 和基于组件的架构。但需要几个关键的思维模型转变:

  • 用 createSignal 替换 useState。注意 getter 是函数需要调用:count() 而不是 count。
  • 用 createEffect 替换 useEffect。不需要依赖数组,Solid 自动追踪依赖。
  • 用 createMemo 替换 useMemo。不需要依赖数组。
  • 不要解构 props。在 JSX 或响应式上下文中使用 props.name 以保持响应式。
  • 用 Show 组件替换条件渲染三元运算符。用 For 组件替换 array.map。
  • 完全移除 useCallback。Solid 中的闭包永远不会过期,因为组件不会重新执行。
  • 用简单的 let 变量替换 useRef 作为 DOM 引用。使用 ref 回调来赋值。
// React version                          // Solid equivalent
// useState("") + dep arrays              // createSignal("") - no dep arrays
// useMemo(() => ..., [items, query])      // createMemo(() => ...)
// useCallback((e) => ..., [])             // Not needed in Solid
// useEffect(() => ..., [filtered.length]) // createEffect(() => ...)
// value={query}                           // value={query()}
// onChange={handler}                      // onInput={e => setQuery(...)}
// filtered.map(i => <p key={i.id}>...)    // <For each={filtered()}>...

import { createSignal, createMemo, createEffect, For } from "solid-js";

function SearchSolid(props) {
  const [query, setQuery] = createSignal("");
  const filtered = createMemo(
    () => props.items.filter(i => i.name.includes(query()))
  );
  createEffect(() => {
    document.title = "Results: " + filtered().length;
  });

  return (
    <div>
      <input value={query()} onInput={e => setQuery(e.target.value)} />
      <For each={filtered()}>
        {(i) => <p>{i.name}</p>}
      </For>
    </div>
  );
}

最佳实践

遵循这些模式编写地道且高性能的 SolidJS 代码:

  • 不要在函数参数级别解构 props。在 JSX 中使用 props.x 或用 splitProps/mergeProps 包装。
  • 对昂贵的派生计算使用 createMemo,避免每次读取时重新计算。
  • 对复杂嵌套状态优先使用存储而非多个信号,获得自动深度响应式。
  • 对列表使用 For 组件而非 array.map,获得正确的键控更新和最小 DOM 操作。
  • 使用 Show 进行条件渲染而非三元表达式,避免创建两个分支。
  • 将 createEffect 调用放在组件级别,不要放在条件或循环内。
  • 在效果中使用 onCleanup 正确清理订阅、定时器和事件监听器。
  • 利用 Suspense 和 ErrorBoundary 进行声明式异步和错误处理。
  • 同步设置多个信号时使用 batch() 避免中间更新。
  • 在 SolidStart 路由中使用 lazy() 代码分割组件,减少初始包大小。

常见问题

SolidJS 用来做什么?

SolidJS 用于构建快速的交互式 Web 应用和用户界面。适合仪表盘、单页应用、实时数据展示、开发者工具等对性能和包体积要求高的项目。SolidStart 扩展到支持 SSR 和 API 路由的全栈应用。

SolidJS 和 React 有什么区别?

SolidJS 没有虚拟 DOM。组件在设置期间只运行一次而非每次状态变化都重新渲染。状态使用信号而非钩子。Solid 将 JSX 编译为直接 DOM 操作。因此运行时性能显著更好,包体积更小。

SolidJS 比 React 快吗?

是的。SolidJS 在 JS Framework Benchmark 等基准测试中始终优于 React,性能接近原生 JavaScript。优势来自细粒度响应式、无虚拟 DOM 开销和编译时优化。

SolidJS 中的信号是什么?

信号是 SolidJS 的核心响应式原语。通过 createSignal 创建,返回 getter 和 setter 函数。在响应式上下文中读取 getter 时自动追踪依赖,调用 setter 时只更新该信号的精确订阅者。

SolidJS 有类似 Next.js 的元框架吗?

有。SolidStart 是 SolidJS 的官方元框架,提供文件路由、流式 SSR、服务器函数、静态站点生成、API 路由和部署适配器。

SolidJS 可以使用 TypeScript 吗?

可以。SolidJS 有一流的 TypeScript 支持,所有核心 API 都有完整类型。JSX 类型系统映射到真实 DOM 元素类型。

如何从 React 迁移到 SolidJS?

将 useState 替换为 createSignal,useEffect 替换为 createEffect,useMemo 替换为 createMemo。移除所有依赖数组。不要解构 props,使用 Show 和 For 组件。移除 useCallback。

SolidJS 可以用于生产环境吗?

可以。SolidJS 在 2021 年达到 1.0 版本并保持稳定。eBay、Rakuten 和 Cloudflare 等公司在生产中使用。SolidStart 在 2024 年达到 1.0。生态系统包含成熟的路由、状态管理、UI 组件和测试库。

𝕏 Twitterin LinkedIn
这篇文章有帮助吗?

保持更新

获取每周开发技巧和新工具通知。

无垃圾邮件,随时退订。

试试这些相关工具

{ }JSON FormatterTSJSON to TypeScriptJSJavaScript Minifier

相关文章

React 设计模式指南:复合组件、自定义 Hook、HOC、Render Props 与状态机

完整的 React 设计模式指南,涵盖复合组件、render props、自定义 hooks、高阶组件、Provider 模式、状态机、受控与非受控、组合模式、观察者模式、错误边界和模块模式。

Svelte指南:响应式、Store、SvelteKit和Svelte 5 Runes

掌握Svelte框架。涵盖编译器方法、响应式语句和Store、组件Props和事件、SvelteKit路由、过渡动画、状态管理以及Svelte vs React vs Vue vs SolidJS对比。

Astro 指南 2026:岛屿架构、内容集合、SSR 与视图过渡

完整的 Astro 指南,涵盖岛屿架构、内容集合、React/Vue/Svelte 组件集成、SSG 和 SSR、视图过渡、Astro DB、中间件、API 端点和部署。