🔬 响应式原理
SolidJS 使用自动依赖追踪实现细粒度响应式:
📊 工作流程
- Signal 被读取时,记录当前执行的 Effect/Memo
- Signal 更新时,通知所有订阅者
- 订阅者重新执行,自动追踪新依赖
javascript
const [a, setA] = createSignal(1);
const [b, setB] = createSignal(2);
const sum = createMemo(() => {
console.log('Computing sum');
return a() + b();
});
createEffect(() => {
console.log('Sum:', sum());
});
// 依赖图:
// a -> sum -> effect
// b -> sum -> effect
setA(10); // 只触发一次计算
🎯 依赖追踪
自动追踪
javascript
const [show, setShow] = createSignal(false);
const [count, setCount] = createSignal(0);
createEffect(() => {
// 只追踪条件分支中实际访问的信号
if (show()) {
console.log('Count:', count());
} else {
console.log('Hidden');
}
});
setShow(true); // effect 重新执行
setCount(1); // 现在 count 被追踪
setShow(false); // count 不再被追踪
setCount(2); // effect 不执行
手动依赖
javascript
import { on } from 'solid-js';
// 只追踪 count,忽略其他
createEffect(on(count, (value) => {
console.log('Count:', value);
}));
// 立即执行
createEffect(on(count, fn, { defer: false }));
📦 批量更新
javascript
import { batch } from 'solid-js';
const [firstName, setFirstName] = createSignal('John');
const [lastName, setLastName] = createSignal('Doe');
const fullName = createMemo(() => `${firstName()} ${lastName()}`);
createEffect(() => {
console.log('Name:', fullName());
});
// 不使用 batch - 触发两次
setFirstName('Jane'); // "Name: Jane Doe"
setLastName('Smith'); // "Name: Jane Smith"
// 使用 batch - 只触发一次
batch(() => {
setFirstName('Jane');
setLastName('Smith');
});
// "Name: Jane Smith"
🔄 响应式原语
| 原语 | 说明 | 返回值 |
|---|---|---|
| createSignal | 响应式状态 | [getter, setter] |
| createMemo | 计算属性 | getter |
| createEffect | 副作用 | cleanup fn |
| createComputed | 立即执行的 Memo | - |
| createRenderEffect | 渲染时执行 | - |
| createReaction | 手动触发副作用 | tracker |
🎭 派生状态
javascript
const [items, setItems] = createSignal([
{ id: 1, done: false },
{ id: 2, done: true },
{ id: 3, done: false }
]);
// 派生计算 - 自动缓存
const completed = createMemo(() =>
items().filter(item => item.done)
);
const pending = createMemo(() =>
items().filter(item => !item.done)
);
const stats = createMemo(() => ({
total: items().length,
done: completed().length,
pending: pending().length
}));
⚠️ 注意事项
- 在 Effect 外读取 Signal 不会建立依赖
- 条件分支会改变依赖关系
- 异步操作需要特殊处理
- 避免在 Effect 中修改依赖的 Signal