关于 solid-js-best-practices
solid-js-best-practices 为 AI 辅助编写、审查和重构 Solid.js 应用提供规则集合,按主题与任务组织建议,并要求先确认项目使用的 Solid.js 版本再采用示例。
主要功能
- 01按版本选择规则
- 02响应式与组件指导
- 03状态、DOM 与性能规则
- 04无障碍与测试指导
- 05Web Component 集成规则
适用场景
- 01编写 Solid.js 组件
- 02审查 Solid.js 代码
- 03重构应用状态与响应式逻辑
- 04在 Solid.js 中集成自定义元素
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ richardcarls/ solid-js-best-practices --skill solid-js-best-practices安装后在 Claude Code 中输入/solid-js-best-practices确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
LICENSE.gitignoreAGENTS.mdREADME.mdCHANGELOG.mddocs/version-support-plan.mdrules/1-1-use-signals-correctly.md1-4-avoid-signal-in-effect.md1-2-use-memo-for-derived.md1-3-effects-for-side-effects.md1-6-batch-signal-updates.md2-2-use-merge-props.md1-5-use-untrack-when-needed.md2-1-never-destructure-props.md1-7-no-primitives-in-reactive-contexts.md2-10-custom-element-typescript-declarations.md2-3-use-split-props.md2-5-component-composition.md2-4-use-children-helper.md2-6-components-return-once.md3-1-use-show-for-conditionals.md2-8-style-prop-conventions.md2-9-never-call-components-as-functions.md2-7-no-react-specific-props.md3-3-use-index-for-primitives.md3-6-stable-component-mount.md3-5-provide-fallbacks.md3-7-use-keyed-for-stateful-children.md3-2-use-for-for-lists.md3-4-use-switch-match.md详细介绍
Solid.js 最佳实践
全面介绍构建 Solid.js 应用和组件的最佳实践,针对 AI 辅助代码生成、审查和重构进行了优化。
版本路由
应用代码示例前,请先从项目的清单文件和锁文件中读取 solid-js 版本。
如果无法确定主版本,请询问项目使用的是哪个主版本。现有规则文件基于 Solid 1.x;version_basis 记录规则所依据的版本,但不限制其原则仅适用于该主版本。未经核实当前对应写法,请勿将 1.x API 示例直接用于 2.x 项目。
Solid 2.x 的覆盖计划推迟至其正式发布后。对于预发布版 2.x 项目,请参阅官方 1.x 迁移指南以及已安装候选版本的文档;不要将这些 1.x 规则视为已验证的 2.x 指南。
按类别划分的规则
1. 响应式(7 条规则)
| # | 规则 | 优先级 | 说明 |
|---|---|---|---|
| 1-1 | 正确使用 Signals | 严重 | 始终将 signal 作为函数调用,例如 count(),而不是 count |
| 1-2 | 使用 Memo 获取派生值 | 高 | 对计算值使用 createMemo,而不是 createEffect |
| 1-3 | Effect 仅用于副作用 | 高 | 仅将 createEffect 用于副作用,而非派生值 |
| 1-7 | 响应式上下文中不创建原语 | 高 | 不要在 effect 或 memo 中调用 hooks 或创建响应式原语 |
| 1-4 | 避免在 Effect 中设置 Signal | 中 | 在 effect 中设置 signal 可能导致无限循环 |
| 1-5 | 按需使用 Untrack | 中 | 使用 untrack() 防止不必要的响应式订阅 |
| 1-6 | 批量更新 Signal | 低 | 多次同步更新 signal 时使用 batch() |
2. 组件(10 条规则)
| # | 规则 | 优先级 | 说明 |
|---|---|---|---|
| 2-1 | 不要解构 Props | 严重 | 解构 props 会破坏响应性 |
| 2-6 | 组件只返回一次 | 严重 | 不要提前返回;在 JSX 中使用 <Show>、<Switch> 等 |
| 2-9 | 不要将组件作为函数调用 | 严重 | 始终使用 JSX 或 createComponent();直接调用会导致响应式作用域泄漏 |
| 2-2 | 使用 mergeProps | 高 | 使用 mergeProps 设置默认 prop 值 |
| 2-3 | 使用 splitProps | 高 | 使用 splitProps 安全地拆分 prop 组 |
| 2-7 | 不使用 React 专属 Props | 高 | 使用 class 而非 className,使用 for 而非 htmlFor |
| 2-10 | 自定义元素 TypeScript 声明 | 高 | 在 JSX 命名空间中声明自定义元素标签;为较新的属性扩展 DOM 类型 |
| 2-4 | 使用 children 辅助函数 | 中 | 使用 children() 辅助函数安全地访问子项 |
| 2-5 | 优先采用组合 | 中 | 优先使用组合和上下文,而非逐层传递 props |
| 2-8 | Style Prop 约定 | 中 | style 使用对象语法,并采用 kebab-case 属性名 |
3. 控制流(7 条规则)
| # | 规则 | 优先级 | 描述 |
|---|---|---|---|
| 3-1 | 条件渲染使用 Show | HIGH | 使用 <Show> 进行条件渲染;当调用方需要经过类型收窄的值时,使用其函数子项 |
| 3-2 | 列表使用 For | HIGH | 使用 <For> 按引用键渲染列表 |
| 3-7 | 有状态子项使用 keyed | HIGH | 当子项拥有内部状态,且值的身份(而不只是其真假)很重要时,添加 keyed |
| 3-3 | 基本类型使用 Index | MEDIUM | 当数组索引比身份更重要时,使用 <Index> |
| 3-4 | 使用 Switch/Match | MEDIUM | 多个条件时使用 <Switch>/<Match>;分支失效后,经过类型收窄的访问器会抛出异常 |
| 3-6 | 稳定的组件挂载 | MEDIUM | 避免在多个 Switch/Show 分支中渲染同一个组件 |
| 3-5 | 提供回退值 | LOW | 加载状态始终提供 fallback 属性 |
4. 状态管理(7 条规则)
| # | 规则 | 优先级 | 描述 |
|---|---|---|---|
| 4-1 | Signals 与 Stores | HIGH | 基本类型使用 signals,嵌套对象使用 stores |
| 4-2 | 使用 Store 路径语法 | HIGH | 使用路径语法进行细粒度、高效的 store 更新 |
| 4-3 | 使用 produce 执行变更 | MEDIUM | 使用 produce 进行复杂的、类似可变操作的 store 更新 |
| 4-4 | 使用 reconcile 处理服务端数据 | MEDIUM | 集成服务端或外部数据时使用 reconcile |
| 4-5 | 使用 Context 管理全局状态 | MEDIUM | 使用 Context API 管理跨组件共享状态 |
| 4-6 | 使用包装器存储函数 | HIGH | 包装函数值,避免 setStore 将其作为更新函数调用 |
| 4-7 | 在页面所有权边界执行清理 | HIGH | 多个路由面板保持挂载时,使用按页面进行的清理;拆分共享上下文时,检查每个上下文字段 |
5. Refs 与 DOM(10 条规则)
| # | 规则 | 优先级 | 说明 |
|---|---|---|---|
| 5-1 | 正确使用 Refs | HIGH | 对条件渲染的元素使用回调 refs |
| 5-2 | 在 onMount 中访问 DOM | HIGH | 在 onMount 中访问 DOM 元素,而不是在渲染期间访问 |
| 5-3 | 使用 onCleanup 清理 | HIGH | 始终清理订阅和计时器 |
| 5-5 | 避免使用 innerHTML | HIGH | 避免使用 innerHTML 以防止 XSS;改用 JSX 或 textContent |
| 5-7 | Web Component 受控状态 | HIGH | 对现代自定义元素使用 prop:* 属性和 on:wc-* 事件;仅对原生或旧版 API 使用 refs/effects |
| 5-8 | 在视图过渡中包装 Signal 变更 | HIGH | 在 startViewTransition() 中调用 Solid setter,让浏览器在应用新的响应式状态前捕获旧 DOM |
| 5-9 | 双向滚动同步 | HIGH | 仅报告滚动位置稳定后的值,并跳过对自身回传事件的再次滚动 |
| 5-10 | progress/meter 的 value 可能缺失 | HIGH | value 始终是属性,设为 undefined 时会抛出异常;请使用 attr:value |
| 5-4 | 使用指令 | MEDIUM | 使用 use: 指令复用元素行为 |
| 5-6 | 事件处理程序模式 | MEDIUM | 正确使用 on:/oncapture: 命名空间和数组处理程序语法 |
6. 性能(6 条规则)
| # | 规则 | 优先级 | 说明 |
|---|---|---|---|
| 6-1 | 避免不必要的追踪 | HIGH | 不要在响应式上下文之外访问 signals |
| 6-2 | 使用延迟加载组件 | MEDIUM | 使用 lazy() 对大型组件进行代码分割 |
| 6-3 | 使用 Suspense | MEDIUM | 使用 <Suspense> 设置异步加载边界 |
| 6-6 | Web Component CSS 与打包策略 | MEDIUM | 单独导入各个组件;将 ::part() 覆盖规则放在全局样式表中 |
| 6-4 | 优化 Store 访问 | LOW | 只访问需要的 Store 属性 |
| 6-5 | 优先使用 classList | LOW | 使用 classList prop 有条件地切换类名 |
7. 无障碍访问(4 条规则)
| # | 规则 | 优先级 | 说明 |
|---|---|---|---|
| 7-1 | 使用语义化 HTML | HIGH | 使用适当的语义化 HTML 元素 |
| 7-2 | 使用 ARIA 属性 | MEDIUM | 为自定义控件应用适当的 ARIA 属性 |
| 7-3 | 支持键盘导航 | MEDIUM | 确保所有交互元素都可通过键盘访问 |
| 7-4 | 为路由器根链接设置 end 匹配 | HIGH | 添加 end 匹配,避免根链接在每个路由下都显示为当前链接 |
8. 测试(12 条规则)
| # | 规则 | 优先级 | 描述 |
|---|---|---|---|
| 8-1 | 为 Solid 配置 Vitest | CRITICAL | 配置 Vitest,使用 Solid 专属的解析条件和插件 |
| 8-2 | 用箭头函数包裹 Render | CRITICAL | 始终使用 render(() => <C />),不要使用 render(<C />) |
| 8-3 | 在 Root 中测试原语 | HIGH | 在 createRoot 或 renderHook 中包裹 signal/effect/memo 测试 |
| 8-4 | 处理测试中的异步操作 | HIGH | 对异步行为使用 findBy 查询和正确的计时器配置 |
| 8-5 | 使用无障碍查询 | MEDIUM | 优先使用 role 和 label 查询,而非测试 ID |
| 8-6 | 分离逻辑测试与 UI 测试 | MEDIUM | 独立于组件渲染测试原语和钩子 |
| 8-7 | 为依赖浏览器的行为使用浏览器模式 | HIGH | 对布局、焦点、原生 API 和自定义元素行为使用真实浏览器,因为 jsdom 无法忠实模拟这些行为 |
| 8-8 | 测试具有非标准 ARIA 的无头 UI 库 | MEDIUM | 无头 UI 库使用不直观的 ARIA 结构和 Portal;查询前先检查实际树结构 |
| 8-9 | 隔离浏览器原生 API 测试 | HIGH | 在测试之间清除 IndexedDB 和 localStorage;调用 deleteDatabase 前关闭连接 |
| 8-10 | 路由器集成测试 | HIGH | 使用 MemoryRouter 的 root 属性,为布局提供器提供路由器上下文 |
| 8-11 | TanStack Query 测试设置 | HIGH | 每项测试都创建新的 QueryClient,并禁用重试和缓存 |
| 8-12 | 结构化克隆前移除代理 | HIGH | 将数据写入 IndexedDB 前,移除所有响应式代理 |
9. Web Component 集成(8 条规则)
| # | 规则 | 优先级 | 描述 |
|---|---|---|---|
| 9-1 | 在应用入口注册自定义元素 | HIGH | 在任何 SolidJS 响应式上下文之前导入 /define 副作用 |
| 9-2 | 在插槽分配后处理 slotchange | HIGH | slotchange 会在微任务中触发;仅将需要延后生命周期阶段或渲染阶段处理的工作延后 |
| 9-3 | 将自定义元素与 SolidJS 响应性视为彼此独立 | MEDIUM | 使用单向数据流(SolidJS -> 属性/props -> 事件 -> SolidJS);切勿在 SolidJS 响应式上下文中读取自定义元素的内部状态 |
| 9-4 | 保持 Web Component 包装器精简 | HIGH | 包装器负责标签、布局、类型适配和表单衔接;自定义元素负责时序和原生同步 |
| 9-5 | 属性与特性绑定 | HIGH | 自定义元素上的动态 props 已经是属性;使用 prop:* 表达意图,使用 attr:* 处理 data-*、ID 引用型 ARIA 和 CSS 可见特性 |
| 9-6 | 向表单库注册自定义字段 | HIGH | 确保属性绑定的自定义字段进入表单库的惰性注册表 |
| 9-7 | 为大量使用 Web Component 的表单存储状态 | MEDIUM | 当自定义元素已负责字段交互时,优先使用 Solid store |
| 9-8 | 为仅受控 WC 使用包装器 Signal | HIGH | 当 WC 没有 defaultValue 时,在包装器中使用 createSignal,负责衔接非受控 → 受控状态 |
按任务选择规则
编写新组件
创建新的 Solid.js 组件时,加载以下规则:
| 规则 | 原因 |
|---|---|
| 1-1 | 确保以函数形式调用信号 |
| 2-1 | 防止响应性失效 |
| 2-6 | 不要提前返回;在 JSX 中使用控制流 |
| 2-9 | 不要将组件作为普通函数调用 |
| 2-2 | 正确处理默认 props |
| 2-3 | 分离本地 props 与转发的 props |
| 3-1 | 正确进行条件渲染 |
| 3-7 | 对表单和有状态子项使用 keyed |
| 3-2 | 高效渲染列表 |
| 5-3 | 防止内存泄漏 |
Web 组件集成
将 Lit 或其他自定义元素与 SolidJS 集成时,加载以下规则:
| 规则 | 原因 |
|---|---|
| 9-1 | 在任何 SolidJS 上下文挂载前完成注册 |
| 9-2 | 在浏览器派发 slotchange 后处理已分配的节点 |
| 9-3 | 采用单向数据流设计 |
| 9-4 | 让包装器专注于应用层需求 |
| 9-5 | 属性使用 prop:*,CSS 或 ARIA 必须可见的属性使用 attr:* |
| 9-6 | 在表单库注册表中注册属性绑定的字段 |
| 9-7 | 为包含大量自定义元素的表单存储状态 |
| 5-6 | 自定义元素事件使用 on: 命名空间 |
代码审查
代码审查时重点关注以下规则:
| 优先级 | 规则 |
|---|---|
| CRITICAL | 1-1、2-1、2-6、2-9 |
| HIGH | 1-2、1-3、1-7、2-7、5-2、5-3、5-5 |
性能优化
优化性能时,加载以下规则:
| 规则 | 关注点 |
|---|---|
| 1-2 | 避免不必要的重新计算 |
| 1-6 | 减少更新周期 |
| 4-2 | 细粒度更新存储 |
| 6-1 | 防止不必要的订阅 |
| 6-2 | 代码拆分 |
| 6-4 | 高效访问存储 |
状态管理
处理应用状态时,加载以下规则:
| 规则 | 关注点 |
|---|---|
| 4-1 | 选择合适的基本类型 |
| 4-2 | 高效更新 |
| 4-3 | 复杂变更 |
| 4-4 | 集成外部数据 |
| 4-5 | 跨组件状态 |
| 4-6 | 在存储中保存函数值 |
| 4-7 | 多个已挂载面板共享上下文 |
无障碍审计
审计无障碍功能时,加载以下规则:
| 规则 | 重点 |
|---|---|
| 7-1 | 语义结构 |
| 7-2 | 屏幕阅读器支持 |
| 7-3 | 键盘用户 |
| 7-4 | 路由链接的当前页面状态 |
编写测试
编写或审查测试时,加载以下规则:
| 规则 | 重点 |
|---|---|
| 8-1 | 正确配置 Vitest |
| 8-2 | 响应式渲染作用域 |
| 8-3 | 原语的响应式所有权 |
| 8-4 | 异步查询与计时器 |
| 8-5 | 无障碍查询选择 |
| 8-6 | 测试架构 |
| 8-7 | 何时使用浏览器模式而非 jsdom |
| 8-8 | Portal 与非标准 ARIA 结构 |
| 8-9 | IDB 和 localStorage 清理模式 |
| 8-10 | 集成测试的 MemoryRouter 设置 |
| 8-11 | 测试的 QueryClient 配置 |
| 8-12 | 写入 IndexedDB 的 Store 代理 |
集成 Web Components / Custom Elements
使用任何自定义元素库(Shoelace、FAST、Lion、Material Web
Components 等)或 <dialog> 和 Popover API 等原生浏览器 API 时,加载以下规则:
| 规则 | 原因 |
|---|---|
| 2-10 | 在 JSX 命名空间中声明自定义元素标签;为较新的 HTML 属性和实验性 CSS 属性添加类型 |
| 5-6 | 对所有自定义元素事件使用 on:;正确标注 CustomEvent 负载的类型 |
| 5-7 | 优先使用声明式 prop:*/on:wc-*;仅对原生或旧版 API 使用 refs |
| 6-6 | 按组件导入以支持 tree-shaking;仅在全局 CSS 中使用 ::part() 覆盖样式 |
| 9-8 | 当 WC 没有 defaultValue 时,使用 createSignal 管理非受控 → 受控桥接 |
| 5-8 | 将 Solid 的 DOM 更新放在 View Transition 快照回调中 |
| 5-10 | 原生 <progress>/<meter> 上的 value 为 undefined 时会抛出错误 |
轮播与滚动同步组件
当某个索引既要传入滚动容器,又要从滚动容器回传时,加载 5-9。
从 React 迁移
加载 signals、 组件执行、 响应式 props、 HTML 属性名、 样式语法、 控制流、 列表渲染和 事件处理器。
工具
如需在遵循这些最佳实践的同时进行自动化 lint 检查,请使用 eslint-plugin-solid。该插件可以捕获本技能涵盖的许多同类问题(解构 props、提前返回、React 专用 props、innerHTML 用法、style prop 格式等),并提供可自动修复的规则。