🔬 响应式原理

SolidJS 使用自动依赖追踪实现细粒度响应式:

📊 工作流程

  1. Signal 被读取时,记录当前执行的 Effect/Memo
  2. Signal 更新时,通知所有订阅者
  3. 订阅者重新执行,自动追踪新依赖
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