What SolidJS Does
- Fine-Grained Reactivity: Updates exact DOM nodes instead of re-rendering components
- No Virtual DOM: Compiled templates, direct DOM manipulation
- React-like JSX: Familiar syntax for React developers
- Signals: Primitive reactive state management built-in
- SSR & SSG: Server-side rendering and static generation via Solid Start
- Small Bundle: Core library is ~7KB gzipped
- TypeScript First: Excellent type inference
- Progressive: Use as a library or full meta-framework
- Stores: Built-in nested reactive stores
- Context: Dependency injection without re-renders
Architecture Difference
React:
Component re-renders → Virtual DOM diff → DOM update
(Whole component function runs on every change)
Solid:
Signal changes → Specific DOM node updates
(Component function runs ONCE, only reactive parts update)This means in Solid, your component code runs once at mount, and only the specific reactive expressions update on state changes.
Basic Example
Counter
import { createSignal } from "solid-js";
function Counter() {
const [count, setCount] = createSignal(0);
// This console.log runs ONCE (at mount), not on every re-render
console.log("Counter component setup");
return (
<div>
<h1>Count: {count()}</h1>
<button onClick={() => setCount(count() + 1)}>
Increment
</button>
</div>
);
}Note: count() is called as a function (it's a getter). This is what enables Solid to track dependencies.
Derived State
import { createSignal, createMemo } from "solid-js";
function PriceCalculator() {
const [price, setPrice] = createSignal(100);
const [quantity, setQuantity] = createSignal(1);
// Memo only recomputes when price or quantity changes
const total = createMemo(() => price() * quantity());
return (
<div>
<input
type="number"
value={price()}
onInput={(e) => setPrice(+e.currentTarget.value)}
/>
<input
type="number"
value={quantity()}
onInput={(e) => setQuantity(+e.currentTarget.value)}
/>
<p>Total: ${total()}</p>
</div>
);
}Effects
import { createSignal, createEffect } from "solid-js";
function AutoSave() {
const [text, setText] = createSignal("");
createEffect(() => {
// Runs whenever text() changes
localStorage.setItem("draft", text());
console.log("Saved:", text());
});
return (
<textarea
value={text()}
onInput={(e) => setText(e.currentTarget.value)}
/>
);
}Control Flow Components
import { For, Show, Switch, Match } from "solid-js";
function UserList(props) {
return (
<div>
{/* Conditional rendering - more efficient than && */}
<Show when={props.users.length > 0} fallback={<p>No users</p>}>
{/* Keyed list rendering */}
<For each={props.users}>
{(user) => (
<div>
<h3>{user.name}</h3>
<Switch fallback={<p>Unknown status</p>}>
<Match when={user.status === "active"}>
<span class="green">Active</span>
</Match>
<Match when={user.status === "pending"}>
<span class="yellow">Pending</span>
</Match>
</Switch>
</div>
)}
</For>
</Show>
</div>
);
}Stores (Nested Reactivity)
import { createStore } from "solid-js/store";
function TodoApp() {
const [state, setState] = createStore({
todos: [],
filter: "all",
user: { name: "Anonymous", preferences: { theme: "light" } },
});
// Add todo - reactive update
const addTodo = (text) => {
setState("todos", state.todos.length, { id: Date.now(), text, done: false });
};
// Update nested property - only affects dependent components
const toggleTheme = () => {
setState("user", "preferences", "theme", (t) => t === "light" ? "dark" : "light");
};
// Filter todos - reactive
const filteredTodos = () => {
if (state.filter === "all") return state.todos;
if (state.filter === "active") return state.todos.filter(t => !t.done);
return state.todos.filter(t => t.done);
};
return (
<div class={state.user.preferences.theme}>
<button onClick={toggleTheme}>Toggle Theme</button>
<For each={filteredTodos()}>
{(todo) => <div>{todo.text}</div>}
</For>
</div>
);
}Solid Start (Meta Framework)
npm create solid@latest my-appFeatures:
- File-based routing: Like Next.js/Remix
- Server functions: RPC-like server actions
- SSR/SSG/CSR: Flexible rendering modes
- Middleware: Request/response interceptors
- Deployment: Vercel, Netlify, Cloudflare, Node
// routes/posts/[id].tsx
import { RouteDataArgs, useRouteData } from "@solidjs/router";
import { createServerData$ } from "solid-start/server";
export function routeData({ params }: RouteDataArgs) {
return createServerData$(async ([id]) => {
const post = await db.post.findUnique({ where: { id } });
return post;
}, { key: () => [params.id] });
}
export default function Post() {
const post = useRouteData<typeof routeData>();
return (
<article>
<h1>{post()?.title}</h1>
<div innerHTML={post()?.content} />
</article>
);
}Solid vs React vs Svelte
| Feature | SolidJS | React | Svelte | Vue |
|---|---|---|---|---|
| Reactivity | Signals (fine-grained) | Virtual DOM | Compiled | Proxies |
| Bundle size | ~7KB | ~45KB | ~2KB | ~34KB |
| JS Framework Benchmark | Top 3 | Middle | Top 5 | Middle |
| JSX | Yes | Yes | No | Templates |
| Re-renders | Component once | On every change | Compiled | Reactive |
| Learning curve | Easy (if React) | Easy | Easy | Medium |
| Ecosystem | Growing | Massive | Growing | Large |
| TypeScript | Excellent | Good | Good | Excellent |
Migration from React
Solid API is similar to React hooks:
// React
const [count, setCount] = useState(0);
useEffect(() => { console.log(count); }, [count]);
return <div>{count}</div>;
// Solid
const [count, setCount] = createSignal(0);
createEffect(() => { console.log(count()); });
return <div>{count()}</div>; // Note: count() as function callKey differences:
- Signals are getter functions:
count()notcount - No dependency arrays needed (automatic tracking)
- Components don't re-render on state changes
常见问题
Q: Solid 为什么比 React 快? A: React 在每次状态变化时重新运行整个组件函数,然后通过 virtual DOM diff 找出变化。Solid 编译模板为直接的 DOM 操作,只在信号变化时更新具体的 DOM 节点,跳过所有 virtual DOM 的开销。
Q: 可以用 React 库吗? A: 不能直接使用 React 库(API 不同)。但 Solid 有自己的生态:Solid Router、Solid Query(类似 TanStack Query)、Solid UI 组件库。大部分常用库都有 Solid 版本。
Q: 适合大型项目吗? A: 适合。Solid 在生产环境已被许多公司使用。TypeScript 支持优秀,性能特别适合性能敏感场景(dashboards、数据可视化、实时应用)。生态系统比 React 小但足够完整。
来源与致谢
- GitHub: solidjs/solid — 35.4K+ ⭐ | MIT
- 官网: solidjs.com