5月27日 15:52

SolidJS 组件生命周期有哪些钩子?与 React 有什么区别?

SolidJS 有哪些生命周期钩子?

SolidJS 的生命周期设计与 React、Vue 等框架截然不同。它的组件函数只会执行一次,后续的状态变更通过细粒度的响应式系统直接更新 DOM,而不需要重新执行组件函数。这种设计使得 SolidJS 只需要少量的生命周期钩子就能覆盖绝大部分场景。

SolidJS 提供三个核心生命周期函数:onMountonCleanuponError,以及响应式原语 createEffect 来处理副作用。

onMount:组件挂载后执行一次

onMount 在组件首次渲染完成之后执行,且只执行一次。它本质上是 createEffect 的一个不追踪依赖的变体,内部实现相当于 createEffect(() => untrack(fn))

适用场景包括:数据请求、DOM 操作、订阅初始化等只需要在挂载时执行一次的逻辑。

javascript
import { 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 注册一个清理函数,当所在的响应式作用域被销毁或重新计算时触发。它可以在组件体、createEffectonMount 等任何响应式上下文中使用。

javascript
import { 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 环境中,onMountcreateEffect 不会执行,但直接在组件体中调用的 onCleanup 仍会执行,这可能导致意外行为。

onError:子作用域错误捕获

onError 注册一个错误处理函数,当子作用域抛出异常时触发。只有最近的父级 onError 会执行,类似 JavaScript 的异常冒泡机制。如果在处理器中重新抛出错误,它会继续向上传播。

javascript
import { 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,当任意依赖变化时重新执行。第一次执行总是在组件挂载之后。

javascript
import { 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 没有这个限制,因为组件只执行一次,不存在调用顺序依赖的问题。

对比项ReactSolidJS
组件函数执行每次渲染重新执行只执行一次
副作用钩子useEffectcreateEffect
依赖管理手动声明依赖数组自动追踪
挂载钩子useEffect(fn, [])onMount(fn)
清理机制useEffect 返回函数onCleanup(fn)
错误处理Error Boundary 组件onError(fn)
条件调用 hooks不允许允许
更新粒度组件级重渲染细粒度 DOM 更新

实际开发中的注意事项

避免在组件函数体中直接读取 Signal。在组件函数体(非 createEffect 等响应式上下文)中读取 Signal 只会拿到初始值,不会建立响应式绑定。响应式逻辑应放在 createEffectcreateMemo 或 JSX 表达式中。

onMount 中的异步操作onMount 支持异步回调,但如果异步操作完成后组件已卸载,更新 Signal 不会报错但也不会反映到 DOM。建议在 onMount 中配合 onCleanup 设置取消标记。

SSR 中的行为差异onMountcreateEffect 在服务端渲染时不会执行,但组件函数体中的 onCleanup 会执行。需要确保清理逻辑不会依赖仅客户端存在的资源。

标签:SolidJS