solidjs by suryavirkapur/skills
npx skills add https://github.com/suryavirkapur/skills --skill solidjsSolidJS 是一个声明式 JavaScript 库,用于构建具有细粒度响应式的用户界面。与虚拟 DOM 框架不同,Solid 将模板编译为真实的 DOM 节点,并通过细粒度的反应来更新它们。
count() 而不是 countimport { createSignal } from "solid-js";
const [count, setCount] = createSignal(0);
// 读取值(getter)
console.log(count()); // 0
// 更新值(setter)
setCount(1);
setCount(prev => prev + 1); // 函数式更新
选项:
const [value, setValue] = createSignal(initialValue, {
equals: false, // 即使值未更改也始终触发更新
name: "debugName" // 用于开发者工具
});
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import { createEffect } from "solid-js";
createEffect(() => {
console.log("计数已更改:", count());
// 在渲染后运行,当依赖项更改时重新运行
});
关键行为:
onCleanup 进行清理逻辑import { createMemo } from "solid-js";
const doubled = createMemo(() => count() * 2);
// 像信号一样访问
console.log(doubled()); // 已缓存,仅在计数更改时重新计算
在以下情况下使用备忘录:
import { createResource } from "solid-js";
const [user, { mutate, refetch }] = createResource(userId, fetchUser);
// 在 JSX 中
<Show when={!user.loading} fallback={<Loading />}>
<div>{user()?.name}</div>
</Show>
// 资源属性
user.loading // 布尔值
user.error // 失败时的错误
user.state // "unresolved" | "pending" | "ready" | "refreshing" | "errored"
user.latest // 最后一个成功的值
用于具有细粒度更新的嵌套对象/数组:
import { createStore } from "solid-js/store";
const [state, setState] = createStore({
user: { name: "John", age: 30 },
todos: []
});
// 路径语法更新
setState("user", "name", "Jane");
setState("todos", todos => [...todos, newTodo]);
setState("todos", 0, "completed", true);
// 用于类 Immer 更新的 produce
import { produce } from "solid-js/store";
setState(produce(s => {
s.user.age++;
s.todos.push(newTodo);
}));
存储工具:
produce — 类 Immer 的变更reconcile — 差异比较和修补数据(用于 API 响应)unwrap — 获取原始的非响应式对象import { Component } from "solid-js";
const MyComponent: Component<{ name: string }> = (props) => {
return <div>你好,{props.name}</div>;
};
import { splitProps, mergeProps } from "solid-js";
// 默认属性
const merged = mergeProps({ size: "medium" }, props);
// 拆分属性(用于展开)
const [local, others] = splitProps(props, ["class", "onClick"]);
return <button class={local.class} {...others} />;
属性规则:
props.value,而不是 const { value } = propsimport { children } from "solid-js";
const Wrapper: Component = (props) => {
const resolved = children(() => props.children);
createEffect(() => {
console.log("子元素:", resolved());
});
return <div>{resolved()}</div>;
};
import { Show } from "solid-js";
<Show when={user()} fallback={<Login />}>
{(user) => <Profile user={user()} />}
</Show>
import { For } from "solid-js";
<For each={items()} fallback={<Empty />}>
{(item, index) => (
<div>{index()}: {item.name}</div>
)}
</For>
注意: index 是一个信号,item 是值。
import { Index } from "solid-js";
<Index each={items()}>
{(item, index) => (
<input value={item().text} />
)}
</Index>
注意: item 是一个信号,index 是值。更适合原始数组或输入框。
import { Switch, Match } from "solid-js";
<Switch fallback={<Default />}>
<Match when={state() === "loading"}>
<Loading />
</Match>
<Match when={state() === "error"}>
<Error />
</Match>
<Match when={state() === "success"}>
<Success />
</Match>
</Switch>
import { Dynamic } from "solid-js/web";
<Dynamic component={selected()} someProp="value" />
import { Portal } from "solid-js/web";
<Portal mount={document.body}>
<Modal />
</Portal>
import { ErrorBoundary } from "solid-js";
<ErrorBoundary fallback={(err, reset) => (
<div>
错误:{err.message}
<button onClick={reset}>重试</button>
</div>
)}>
<RiskyComponent />
</ErrorBoundary>
import { Suspense } from "solid-js";
<Suspense fallback={<Loading />}>
<AsyncComponent />
</Suspense>
import { createContext, useContext } from "solid-js";
// 创建上下文
const CounterContext = createContext<{
count: () => number;
increment: () => void;
}>();
// 提供者组件
export function CounterProvider(props) {
const [count, setCount] = createSignal(0);
return (
<CounterContext.Provider value={{
count,
increment: () => setCount(c => c + 1)
}}>
{props.children}
</CounterContext.Provider>
);
}
// 消费者钩子
export function useCounter() {
const ctx = useContext(CounterContext);
if (!ctx) throw new Error("useCounter 必须在 CounterProvider 内使用");
return ctx;
}
import { onMount, onCleanup } from "solid-js";
function MyComponent() {
onMount(() => {
console.log("已挂载");
const handler = () => {};
window.addEventListener("resize", handler);
onCleanup(() => {
window.removeEventListener("resize", handler);
});
});
return <div>内容</div>;
}
let inputRef: HTMLInputElement;
<input ref={inputRef} />
<input ref={(el) => { /* el 是 DOM 元素 */ }} />
// 标准事件(小写)
<button onClick={handleClick}>点击</button>
<button onClick={(e) => handleClick(e)}>点击</button>
// 委托事件(on:)
<input on:input={handleInput} />
// 原生事件(on:)- 非委托
<div on:scroll={handleScroll} />
完整的路由指南请参阅 references/routing.md。
快速设置:
import { Router, Route } from "@solidjs/router";
<Router>
<Route path="/" component={Home} />
<Route path="/users/:id" component={User} />
<Route path="*404" component={NotFound} />
</Router>
全栈开发指南请参阅 references/solidstart.md。
快速设置:
npm create solid@latest my-app
cd my-app && npm install && npm run dev
import { clsx } from "clsx"; // 或 classList
<div class={clsx("base", { active: isActive() })} />
<div classList={{ active: isActive(), disabled: isDisabled() }} />
import { batch } from "solid-js";
batch(() => {
setName("John");
setAge(30);
// 副作用在批量操作完成后运行一次
});
import { untrack } from "solid-js";
createEffect(() => {
console.log(count()); // 被跟踪
console.log(untrack(() => other())); // 不被跟踪
});
import type { Component, ParentComponent, JSX } from "solid-js";
// 基础组件
const Button: Component<{ label: string }> = (props) => (
<button>{props.label}</button>
);
// 带子元素
const Layout: ParentComponent<{ title: string }> = (props) => (
<div>
<h1>{props.title}</h1>
{props.children}
</div>
);
// 事件处理程序类型
const handleClick: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (e) => {
console.log(e.currentTarget);
};
# 创建新项目
npm create solid@latest my-app
# 使用模板
npx degit solidjs/templates/ts my-app
# SolidStart
npm create solid@latest my-app -- --template solidstart
vite.config.ts:
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";
export default defineConfig({
plugins: [solid()]
});
解构属性 — 破坏响应式
// ❌ 错误 const { name } = props;
// ✅ 正确 props.name
在跟踪范围外访问信号
// ❌ 不会更新 console.log(count());
// ✅ 会更新 createEffect(() => console.log(count()));
忘记调用信号 getter
// ❌ 传递函数
<div>{count}</div>// ✅ 传递值
<div>{count()}</div>使用数组索引作为键 — 使用 <For> 进行引用键控,使用 <Index> 进行索引键控
在渲染期间产生副作用 — 使用 createEffect 或 onMount
每周安装量
209
代码仓库
GitHub 星标数
1
首次出现
2026年1月21日
安全审计
安装于
opencode179
gemini-cli175
codex167
github-copilot167
amp150
kimi-cli147
SolidJS is a declarative JavaScript library for building user interfaces with fine-grained reactivity. Unlike virtual DOM frameworks, Solid compiles templates to real DOM nodes and updates them with fine-grained reactions.
count() not countimport { createSignal } from "solid-js";
const [count, setCount] = createSignal(0);
// Read value (getter)
console.log(count()); // 0
// Update value (setter)
setCount(1);
setCount(prev => prev + 1); // Functional update
Options:
const [value, setValue] = createSignal(initialValue, {
equals: false, // Always trigger updates, even if value unchanged
name: "debugName" // For devtools
});
import { createEffect } from "solid-js";
createEffect(() => {
console.log("Count changed:", count());
// Runs after render, re-runs when dependencies change
});
Key behaviors:
onCleanup for cleanup logicimport { createMemo } from "solid-js";
const doubled = createMemo(() => count() * 2);
// Access like signal
console.log(doubled()); // Cached, only recalculates when count changes
Use memos when:
import { createResource } from "solid-js";
const [user, { mutate, refetch }] = createResource(userId, fetchUser);
// In JSX
<Show when={!user.loading} fallback={<Loading />}>
<div>{user()?.name}</div>
</Show>
// Resource properties
user.loading // boolean
user.error // error if failed
user.state // "unresolved" | "pending" | "ready" | "refreshing" | "errored"
user.latest // last successful value
For nested objects/arrays with fine-grained updates:
import { createStore } from "solid-js/store";
const [state, setState] = createStore({
user: { name: "John", age: 30 },
todos: []
});
// Path syntax updates
setState("user", "name", "Jane");
setState("todos", todos => [...todos, newTodo]);
setState("todos", 0, "completed", true);
// Produce for immer-like updates
import { produce } from "solid-js/store";
setState(produce(s => {
s.user.age++;
s.todos.push(newTodo);
}));
Store utilities:
produce — Immer-like mutationsreconcile — Diff and patch data (for API responses)unwrap — Get raw non-reactive objectimport { Component } from "solid-js";
const MyComponent: Component<{ name: string }> = (props) => {
return <div>Hello, {props.name}</div>;
};
import { splitProps, mergeProps } from "solid-js";
// Default props
const merged = mergeProps({ size: "medium" }, props);
// Split props (for spreading)
const [local, others] = splitProps(props, ["class", "onClick"]);
return <button class={local.class} {...others} />;
Props rules:
props.value in JSX, not const { value } = propsimport { children } from "solid-js";
const Wrapper: Component = (props) => {
const resolved = children(() => props.children);
createEffect(() => {
console.log("Children:", resolved());
});
return <div>{resolved()}</div>;
};
import { Show } from "solid-js";
<Show when={user()} fallback={<Login />}>
{(user) => <Profile user={user()} />}
</Show>
import { For } from "solid-js";
<For each={items()} fallback={<Empty />}>
{(item, index) => (
<div>{index()}: {item.name}</div>
)}
</For>
Note: index is a signal, item is the value.
import { Index } from "solid-js";
<Index each={items()}>
{(item, index) => (
<input value={item().text} />
)}
</Index>
Note: item is a signal, index is the value. Better for primitive arrays or inputs.
import { Switch, Match } from "solid-js";
<Switch fallback={<Default />}>
<Match when={state() === "loading"}>
<Loading />
</Match>
<Match when={state() === "error"}>
<Error />
</Match>
<Match when={state() === "success"}>
<Success />
</Match>
</Switch>
import { Dynamic } from "solid-js/web";
<Dynamic component={selected()} someProp="value" />
import { Portal } from "solid-js/web";
<Portal mount={document.body}>
<Modal />
</Portal>
import { ErrorBoundary } from "solid-js";
<ErrorBoundary fallback={(err, reset) => (
<div>
Error: {err.message}
<button onClick={reset}>Retry</button>
</div>
)}>
<RiskyComponent />
</ErrorBoundary>
import { Suspense } from "solid-js";
<Suspense fallback={<Loading />}>
<AsyncComponent />
</Suspense>
import { createContext, useContext } from "solid-js";
// Create context
const CounterContext = createContext<{
count: () => number;
increment: () => void;
}>();
// Provider component
export function CounterProvider(props) {
const [count, setCount] = createSignal(0);
return (
<CounterContext.Provider value={{
count,
increment: () => setCount(c => c + 1)
}}>
{props.children}
</CounterContext.Provider>
);
}
// Consumer hook
export function useCounter() {
const ctx = useContext(CounterContext);
if (!ctx) throw new Error("useCounter must be used within CounterProvider");
return ctx;
}
import { onMount, onCleanup } from "solid-js";
function MyComponent() {
onMount(() => {
console.log("Mounted");
const handler = () => {};
window.addEventListener("resize", handler);
onCleanup(() => {
window.removeEventListener("resize", handler);
});
});
return <div>Content</div>;
}
let inputRef: HTMLInputElement;
<input ref={inputRef} />
<input ref={(el) => { /* el is the DOM element */ }} />
// Standard events (lowercase)
<button onClick={handleClick}>Click</button>
<button onClick={(e) => handleClick(e)}>Click</button>
// Delegated events (on:)
<input on:input={handleInput} />
// Native events (on:) - not delegated
<div on:scroll={handleScroll} />
See references/routing.md for complete routing guide.
Quick setup:
import { Router, Route } from "@solidjs/router";
<Router>
<Route path="/" component={Home} />
<Route path="/users/:id" component={User} />
<Route path="*404" component={NotFound} />
</Router>
See references/solidstart.md for full-stack development guide.
Quick setup:
npm create solid@latest my-app
cd my-app && npm install && npm run dev
import { clsx } from "clsx"; // or classList
<div class={clsx("base", { active: isActive() })} />
<div classList={{ active: isActive(), disabled: isDisabled() }} />
import { batch } from "solid-js";
batch(() => {
setName("John");
setAge(30);
// Effects run once after batch completes
});
import { untrack } from "solid-js";
createEffect(() => {
console.log(count()); // tracked
console.log(untrack(() => other())); // not tracked
});
import type { Component, ParentComponent, JSX } from "solid-js";
// Basic component
const Button: Component<{ label: string }> = (props) => (
<button>{props.label}</button>
);
// With children
const Layout: ParentComponent<{ title: string }> = (props) => (
<div>
<h1>{props.title}</h1>
{props.children}
</div>
);
// Event handler types
const handleClick: JSX.EventHandler<HTMLButtonElement, MouseEvent> = (e) => {
console.log(e.currentTarget);
};
# Create new project
npm create solid@latest my-app
# With template
npx degit solidjs/templates/ts my-app
# SolidStart
npm create solid@latest my-app -- --template solidstart
vite.config.ts:
import { defineConfig } from "vite";
import solid from "vite-plugin-solid";
export default defineConfig({
plugins: [solid()]
});
Destructuring props — Breaks reactivity
// ❌ Bad
const { name } = props;
// ✅ Good
props.name
Accessing signals outside tracking scope
// ❌ Won't update
console.log(count());
// ✅ Will update
createEffect(() => console.log(count()));
Forgetting to call signal getters
// ❌ Passes the function
<div>{count}</div>
// ✅ Passes the value
<div>{count()}</div>
Using array index as key — Use <For> for reference-keyed, <Index> for index-keyed
Side effects during render — Use createEffect or onMount
Weekly Installs
209
Repository
GitHub Stars
1
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode179
gemini-cli175
codex167
github-copilot167
amp150
kimi-cli147
React 组合模式指南:Vercel 组件架构最佳实践,提升代码可维护性
111,800 周安装
代码范例蓝图生成器 - GitHub Copilot 自动扫描代码库生成高质量范例文档
7,600 周安装
AI测试规划与质量保障提示:基于ISTQB与ISO 25010的全面测试策略生成
7,700 周安装
Epic架构规范提示:AI辅助软件架构设计,生成领域驱动技术方案
7,600 周安装
ASP.NET 最小 API OpenAPI 文档生成器 - 构建强类型、文档完善的 Web API
7,600 周安装
Aspire 多语言分布式应用编排框架 - 代码优先、可观测的生产就绪工具链
7,700 周安装
Vue.js 测试最佳实践指南:Vue 3 单元测试、组件测试与常见问题解决方案
8,000 周安装