SolidJS 组件生命周期有哪些钩子?与 React 有什么区别?
SolidJS 有哪些生命周期钩子?
SolidJS 的生命周期设计与 React、Vue 等框架截然不同。它的组件函数只会执行一次,后续的状态变更通过细粒度的响应式系统直接更新 DOM,而不需要重新执行组件函数。这种设计使得 SolidJS 只需要少量的生命周期钩子就能覆盖绝大部分场景。
SolidJS 提供三个核心生命周期函数:onMount、onCleanup、onError,以及响应式原语 createEffect 来处理副作用。
onMount:组件挂载后执行一次
onMount 在组件首次渲染完成之后执行,且只执行一次。它本质上是 createEffect 的一个不追踪依赖的变体,内部实现相当于 createEffect(() => untrack(fn))。
适用场景包括:数据请求、DOM 操作、订阅初始化等只需要在挂载时执行一次的逻辑。
javascriptimport { onMount, createSignal } from "solid-js"; function UserProfile(props) { const [user, setUser] = createSignal(null); onMount(async () => { const res = await fetch(`/api/users/${props.id}`); setUser(await res.json()); }); return <div>{user()?.name}</div>; }
注意:onMount 的回调函数不支持返回清理函数。如果需要清理,请在 onMount 内部调用 onCleanup。
onCleanup:响应式作用域销毁时执行
onCleanup 注册一个清理函数,当所在的响应式作用域被销毁或重新计算时触发。它可以在组件体、createEffect、onMount 等任何响应式上下文中使用。
javascriptimport { onCleanup, createSignal } from "solid-js"; function Timer() { const [seconds, setSeconds] = createSignal(0); const interval = setInterval(() => setSeconds(s => s + 1), 1000); onCleanup(() => clearInterval(interval)); return <div>Elapsed: {seconds()}s</div>; }
关键细节:onCleanup 在组件卸载时触发;在 createEffect 内使用时,每次 effect 重新执行前也会触发上一次注册的清理函数。在 SSR 环境中,onMount 和 createEffect 不会执行,但直接在组件体中调用的 onCleanup 仍会执行,这可能导致意外行为。
onError:子作用域错误捕获
onError 注册一个错误处理函数,当子作用域抛出异常时触发。只有最近的父级 onError 会执行,类似 JavaScript 的异常冒泡机制。如果在处理器中重新抛出错误,它会继续向上传播。
javascriptimport { onError, createSignal } from "solid-js"; function SafeComponent() { const [data, setData] = createSignal(null); onError((err) => { console.error("Child scope error:", err); }); return <ChildThatMightThrow />; }
createEffect:自动追踪依赖的响应式副作用
createEffect 是 SolidJS 响应式系统的核心原语。它会自动追踪回调函数中读取的所有 Signal,当任意依赖变化时重新执行。第一次执行总是在组件挂载之后。
javascriptimport { createEffect, createSignal } from "solid-js"; function SearchBox() { const [query, setQuery] = createSignal(""); createEffect(() => { console.log("Searching:", query()); // 自动追踪 query 这个 Signal 的依赖 }); return <input onInput={(e) => setQuery(e.target.value)} />; }
与 React 的 useEffect 不同,createEffect 不需要手动声明依赖数组,也不支持返回清理函数。需要清理时,在 createEffect 内部调用 onCleanup。
SolidJS 与 React 生命周期对比
两者在设计哲学上存在根本性差异:
组件执行机制
React 的组件函数在每次状态更新时都会重新执行,hooks 依靠调用顺序来维持状态。SolidJS 的组件函数只执行一次,状态更新通过 Signal 直接触发 DOM 更新。
依赖追踪方式
React 的 useEffect 需要手动维护依赖数组,遗漏依赖是常见的 bug 来源。SolidJS 的 createEffect 自动追踪依赖,读取了哪些 Signal 就订阅哪些,无需开发者手动管理。
副作用清理
React 在 useEffect 回调中返回清理函数。SolidJS 使用独立的 onCleanup 函数,可以在任何响应式上下文中调用,更加灵活。
Hooks 调用限制
React 的 hooks 不能在条件语句、循环或嵌套函数中调用(Rules of Hooks)。SolidJS 没有这个限制,因为组件只执行一次,不存在调用顺序依赖的问题。
| 对比项 | React | SolidJS |
|---|---|---|
| 组件函数执行 | 每次渲染重新执行 | 只执行一次 |
| 副作用钩子 | useEffect | createEffect |
| 依赖管理 | 手动声明依赖数组 | 自动追踪 |
| 挂载钩子 | useEffect(fn, []) | onMount(fn) |
| 清理机制 | useEffect 返回函数 | onCleanup(fn) |
| 错误处理 | Error Boundary 组件 | onError(fn) |
| 条件调用 hooks | 不允许 | 允许 |
| 更新粒度 | 组件级重渲染 | 细粒度 DOM 更新 |
实际开发中的注意事项
避免在组件函数体中直接读取 Signal。在组件函数体(非 createEffect 等响应式上下文)中读取 Signal 只会拿到初始值,不会建立响应式绑定。响应式逻辑应放在 createEffect、createMemo 或 JSX 表达式中。
onMount 中的异步操作。onMount 支持异步回调,但如果异步操作完成后组件已卸载,更新 Signal 不会报错但也不会反映到 DOM。建议在 onMount 中配合 onCleanup 设置取消标记。
SSR 中的行为差异。onMount 和 createEffect 在服务端渲染时不会执行,但组件函数体中的 onCleanup 会执行。需要确保清理逻辑不会依赖仅客户端存在的资源。