基于 solid-js 源码深度分析
2026-03-20 | 技术深度解读
第一部分:核心概念
第二部分:依赖追踪
第三部分:调度系统
第四部分:组件系统
SolidJS 是一个声明式、高效且灵活的 JavaScript UI 库
核心特点:
// 典型 SolidJS 组件
function Counter() {
const [count, setCount] = createSignal(0);
return (
<button onClick={() => setCount(c => c + 1)}>
Count: {count()}
</button>
);
}
┌─────────────────────────────────────────────────────────────┐
│ SolidJS 响应式系统 │
├─────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Signal │───▶│ Computation │───▶│ DOM │ │
│ │ (状态源) │ │ (计算单元) │ │ (渲染目标) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │
│ │ ┌─────────────▼─────────────┐ │
│ │ │ Scheduler │ │
│ └───▶│ (调度器 - 批量更新) │ │
│ └───────────────────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Transition │ │
│ │ (过渡 - 并发更新) │ │
│ └─────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
| 原语 | 用途 | 执行时机 |
|---|---|---|
| createSignal | 创建响应式状态 | 立即 |
| createEffect | 副作用(DOM 操作等) | 渲染后 |
| createMemo | 缓存计算值 | 依赖变化时 |
| createComputed | 同步计算 | 渲染前 |
| createRenderEffect | 渲染阶段副作用 | 渲染时 |
Signal 是 SolidJS 响应式系统的基础单元
Signal 的组成:
核心特性:
const [count, setCount] = createSignal(0);
// count() 读取值(追踪依赖)
// setCount(1) 设置值(触发更新)
export function createSignal<T>(
value?: T,
options?: SignalOptions<T | undefined>
): Signal<T | undefined> {
options = options ? Object.assign({}, signalOptions, options) : signalOptions;
// 创建 Signal 状态对象
const s: SignalState<T | undefined> = {
value, // 当前值
observers: null, // 观察者列表
observerSlots: null, // 观察者的槽位索引
comparator: options.equals || undefined // 比较函数
};
// 开发模式注册到图
if (IS_DEV) {
if (options.name) s.name = options.name;
if (!options.internal) registerGraph(s);
}
// 创建 setter 函数
const setter: Setter<T | undefined> = (value?: unknown) => {
if (typeof value === "function") {
// 支持函数式更新
value = Transition?.running ? value(s.tValue) : value(s.value);
}
return writeSignal(s, value);
};
return [readSignal.bind(s), setter];
}
export interface SignalState<T> extends SourceMapValue {
value: T; // 当前值
observers: Computation<any>[] | null; // 观察者数组
observerSlots: number[] | null; // 观察者在 sources 中的索引
tValue?: T; // Transition 期间的临时值
comparator?: (prev: T, next: T) => boolean; // 自定义比较器
internal?: true; // 内部信号标记
}
双向依赖关系:
export function readSignal(this: SignalState<any> | Memo<any>) {
const runningTransition = Transition && Transition.running;
// 如果是 Memo 且状态过期,先更新自己
if ((this as Memo<any>).sources &&
(runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state)) {
if ((runningTransition ? (this as Memo<any>).tState : (this as Memo<any>).state) === STALE) {
updateComputation(this as Memo<any>);
} else {
const updates = Updates;
Updates = null;
runUpdates(() => lookUpstream(this as Memo<any>), false);
Updates = updates;
}
}
// 核心:建立依赖关系
if (Listener) {
const sSlot = this.observers ? this.observers.length : 0;
// Listener 记录依赖
if (!Listener.sources) {
Listener.sources = [this];
Listener.sourceSlots = [sSlot];
} else {
Listener.sources.push(this);
Listener.sourceSlots!.push(sSlot);
}
// Signal 记录观察者
if (!this.observers) {
this.observers = [Listener];
this.observerSlots = [Listener.sources.length - 1];
} else {
this.observers.push(Listener);
this.observerSlots!.push(Listener.sources.length - 1);
}
}
return runningTransition && Transition!.sources.has(this)
? this.tValue
: this.value;
}
export function writeSignal(
node: SignalState<any> | Memo<any>,
value: any,
isComp?: boolean
) {
let current = Transition?.running && Transition.sources.has(node)
? node.tValue
: node.value;
// 相等性检查
if (!node.comparator || !node.comparator(current, value)) {
// Transition 模式:写入 tValue
if (Transition) {
const TransitionRunning = Transition.running;
if (TransitionRunning || (!isComp && Transition.sources.has(node))) {
Transition.sources.add(node);
node.tValue = value;
}
if (!TransitionRunning) node.value = value;
} else {
node.value = value;
}
// 通知所有观察者
if (node.observers && node.observers.length) {
runUpdates(() => {
for (let i = 0; i < node.observers!.length; i += 1) {
const o = node.observers![i];
const TransitionRunning = Transition && Transition.running;
if (TransitionRunning && Transition!.disposed.has(o)) continue;
if (TransitionRunning ? !o.tState : !o.state) {
if (o.pure) Updates!.push(o);
else Effects!.push(o);
if ((o as Memo<any>).observers) markDownstream(o as Memo<any>);
}
if (!TransitionRunning) o.state = STALE;
else o.tState = STALE;
}
// 防止无限循环
if (Updates!.length > 10e5) {
Updates = [];
throw new Error("Potential Infinite Loop Detected.");
}
}, false);
}
}
return value;
}
SolidJS 使用运行时自动依赖收集,无需显式声明
┌────────────────────────────────────────────────────────┐
│ 依赖追踪流程 │
├────────────────────────────────────────────────────────┤
│ │
│ Effect 执行时 │
│ │ │
│ ▼ │
│ ┌─────────┐ 设置 Listener = 当前 Computation │
│ │ 读取 │─────────────────────────────────▶ │
│ │ Signal │ │
│ └─────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────┐ │
│ │ readSignal() 检测到 Listener 存在 │ │
│ │ │ │
│ │ 1. Signal.observers.push(Listener) │ │
│ │ 2. Listener.sources.push(Signal) │ │
│ │ 3. 记录双向索引 │ │
│ └─────────────────────────────────────┘ │
│ │
└────────────────────────────────────────────────────────┘
Listener 是当前正在执行的 Computation 引用
let Listener: Computation<any> | null = null;
function runComputation(node: Computation<any>, value: any, time: number) {
let nextValue;
const owner = Owner,
listener = Listener;
// 设置当前 Listener
Listener = Owner = node;
try {
nextValue = node.fn(value);
} catch (err) {
// 错误处理
return handleError(err);
} finally {
// 恢复之前的 Listener
Listener = listener;
Owner = owner;
}
// ...
}
Listener 的作用:
export interface Computation<Init, Next extends Init = Init> extends Owner {
fn: EffectFunction<Init, Next>; // 计算函数
state: ComputationState; // 当前状态
tState?: ComputationState; // Transition 状态
sources: SignalState<Next>[] | null; // 依赖的 Signal 列表
sourceSlots: number[] | null; // 在 Signal.observers 中的索引
value?: Init; // 当前计算值
updatedAt: number | null; // 最后更新时间戳
pure: boolean; // 是否纯计算(Memo)
user?: boolean; // 是否用户 Effect
suspense?: SuspenseContextType; // Suspense 上下文
}
export type ComputationState = 0 | 1 | 2;
// 0 = CLEAN(干净,无需更新)
// 1 = STALE(过期,需要重新计算)
// 2 = PENDING(待定,依赖可能变化)
export function createEffect<Next, Init>(
fn: EffectFunction<Init | Next, Next>,
value?: Init,
options?: EffectOptions & { render?: boolean }
): void {
runEffects = runUserEffects;
// 创建 Computation
const c = createComputation(fn, value!, false, STALE, IS_DEV ? options : undefined),
s = SuspenseContext && useContext(SuspenseContext);
if (s) c.suspense = s;
if (!options || !options.render) c.user = true;
// 添加到 Effects 队列或立即执行
Effects ? Effects.push(c) : updateComputation(c);
}
createEffect 特点:
export function createMemo<Next extends Prev, Init, Prev>(
fn: EffectFunction<Init | Prev, Next>,
value?: Init,
options?: MemoOptions<Next>
): Accessor<Next> {
options = options ? Object.assign({}, signalOptions, options) : signalOptions;
// 创建 Memo(既是 Signal 又是 Computation)
const c: Partial<Memo<Init, Next>> = createComputation(
fn,
value!,
true, // pure = true
0, // 初始状态 CLEAN
IS_DEV ? options : undefined
) as Partial<Memo<Init, Next>>;
c.observers = null;
c.observerSlots = null;
c.comparator = options.equals || undefined;
// 立即执行或加入队列
if (Scheduler && Transition && Transition.running) {
c.tState = STALE;
Updates!.push(c as Memo<Init, Next>);
} else {
updateComputation(c as Memo<Init, Next>);
}
return readSignal.bind(c as Memo<Init, Next>);
}
export function createComputed<Next, Init>(
fn: EffectFunction<Init | Next, Next>,
value?: Init,
options?: EffectOptions
): void {
const c = createComputation(fn, value!, true, STALE, IS_DEV ? options : undefined);
if (Scheduler && Transition && Transition.running) {
Updates!.push(c);
} else {
updateComputation(c);
}
}
createComputed
createEffect
function updateComputation(node: Computation<any>) {
if (!node.fn) return;
// 清理旧依赖
cleanNode(node);
const time = ExecCount;
// 执行计算
runComputation(
node,
Transition && Transition.running && Transition.sources.has(node as Memo<any>)
? (node as Memo<any>).tValue
: node.value,
time
);
// Transition 完成后同步
if (Transition && !Transition.running && Transition.sources.has(node as Memo<any>)) {
queueMicrotask(() => {
runUpdates(() => {
Transition && (Transition.running = true);
Listener = Owner = node;
runComputation(node, (node as Memo<any>).tValue, time);
Listener = Owner = null;
}, false);
});
}
}
Computation 状态转换图:
依赖变化
┌─────────┐ ───────────────▶ ┌─────────┐
│ CLEAN │ │ STALE │
│ (0) │ ◀─────────────── │ (1) │
└─────────┘ 重新计算完成 └────┬────┘
▲ │
│ │
│ ┌───────────────────┘
│ │ 依赖也是 STALE
│ ▼
│ ┌──────────┐
└────│ PENDING │
│ (2) │
└──────────┘
状态含义:
- CLEAN (0):无需更新
- STALE (1):需要重新计算
- PENDING (2):依赖可能变化,需要向上查找
SolidJS 通过 runUpdates 实现批量更新
let Updates: Computation<any>[] | null = null;
let Effects: Computation<any>[] | null = null;
function runUpdates<T>(fn: () => T, init: boolean): T | undefined {
if (Updates) return fn(); // 已在更新周期中
Updates = [];
Effects = [];
ExecCount++;
try {
const ret = fn();
// 处理 Updates 队列
finishPending(Updates);
Updates = null;
// 处理 Effects 队列
if (Effects.length) {
runEffects(Effects);
Effects = null;
}
return ret;
} finally {
// 清理
}
}
function finishPending(queue: Computation<any>[]) {
for (let i = 0; i < queue.length; i++) {
const node = queue[i];
// 如果状态不是 STALE,说明是 PENDING,需要向上查找
if ((Transition && Transition.running ? node.tState : node.state) === PENDING) {
lookUpstream(node);
}
// 如果状态变成 STALE,执行更新
if ((Transition && Transition.running ? node.tState : node.state) === STALE) {
runTop(node);
}
}
}
批量更新优势:
SolidJS 的调度器参考了 React 的 Scheduler 实现
export interface Task {
id: number;
fn: ((didTimeout: boolean) => void) | null;
startTime: number;
expirationTime: number;
}
let taskQueue: Task[] = [];
let isCallbackScheduled = false;
let isPerformingWork = false;
let currentTask: Task | null = null;
调度器目标:
// 使用二分查找插入,按 expirationTime 排序
function enqueue(taskQueue: Task[], task: Task) {
function findIndex() {
let m = 0;
let n = taskQueue.length - 1;
while (m <= n) {
const k = (n + m) >> 1;
const cmp = task.expirationTime - taskQueue[k].expirationTime;
if (cmp > 0) m = k + 1;
else if (cmp < 0) n = k - 1;
else return k;
}
return m;
}
taskQueue.splice(findIndex(), 0, task);
}
export function requestCallback(fn: () => void, options?: { timeout: number }): Task {
if (!scheduleCallback) setupScheduler();
let startTime = performance.now(),
timeout = options?.timeout ?? maxSigned31BitInt;
const newTask: Task = {
id: taskIdCounter++,
fn,
startTime,
expirationTime: startTime + timeout
};
enqueue(taskQueue, newTask);
// ...调度执行
}
function setupScheduler() {
const channel = new MessageChannel(),
port = channel.port2;
scheduleCallback = () => port.postMessage(null);
channel.port1.onmessage = () => {
if (scheduledCallback !== null) {
const currentTime = performance.now();
deadline = currentTime + yieldInterval; // 5ms
maxDeadline = currentTime + maxYieldInterval; // 300ms
try {
const hasMoreWork = scheduledCallback(currentTime);
if (!hasMoreWork) {
scheduledCallback = null;
} else {
port.postMessage(null); // 继续下一帧
}
} catch (error) {
port.postMessage(null);
throw error;
}
}
};
}
为什么用 MessageChannel?
if (navigator?.scheduling?.isInputPending) {
const scheduling = navigator.scheduling;
shouldYieldToHost = () => {
const currentTime = performance.now();
if (currentTime >= deadline) {
// 有用户输入待处理,必须让出
if (scheduling.isInputPending()) {
return true;
}
// 没有输入,可以继续到最大时间
return currentTime >= maxDeadline;
}
return false;
};
} else {
// 回退方案:固定时间切片
shouldYieldToHost = () => performance.now() >= deadline;
}
isInputPending API:
function workLoop(initialTime: number) {
let currentTime = initialTime;
currentTask = taskQueue[0] || null;
while (currentTask !== null) {
// 检查是否需要让出主线程
if (currentTask.expirationTime > currentTime && shouldYieldToHost()) {
break; // 时间片用完,让出
}
const callback = currentTask.fn;
if (callback !== null) {
currentTask.fn = null;
const didUserCallbackTimeout = currentTask.expirationTime <= currentTime;
callback(didUserCallbackTimeout);
currentTime = performance.now();
if (currentTask === taskQueue[0]) {
taskQueue.shift();
}
} else {
taskQueue.shift();
}
currentTask = taskQueue[0] || null;
}
// 返回是否还有工作
return currentTask !== null;
}
Transition 是 SolidJS 的并发更新机制
export interface TransitionState {
sources: Set<SignalState<any>>; // 参与的 Signal
effects: Computation<any>[]; // 待执行 Effect
promises: Set<Promise<any>>; // 异步 Promise
disposed: Set<Computation<any>>; // 已销毁的 Computation
queue: Set<Computation<any>>; // 待处理队列
scheduler?: (fn: () => void) => unknown;
running: boolean; // 是否正在运行
done?: Promise<void>; // 完成信号
resolve?: () => void; // resolve 函数
}
export function startTransition(fn: () => unknown): Promise<void> {
if (Transition && Transition.running) {
fn();
return Transition.done!;
}
const l = Listener;
const o = Owner;
return Promise.resolve().then(() => {
Listener = l;
Owner = o;
let t: TransitionState | undefined;
if (Scheduler || SuspenseContext) {
t = Transition || (Transition = {
sources: new Set(),
effects: [],
promises: new Set(),
disposed: new Set(),
queue: new Set(),
running: true
});
t.done ||= new Promise(res => (t!.resolve = res));
t.running = true;
}
runUpdates(fn, false);
Listener = Owner = null;
return t ? t.done : undefined;
});
}
const [transPending, setTransPending] = /*@__PURE__*/ createSignal(false);
export function useTransition(): Transition {
return [transPending, startTransition];
}
// 使用示例
function App() {
const [isPending, start] = useTransition();
const [tab, setTab] = createSignal('about');
const updateTab = () => {
start(() => {
setTab('posts'); // 低优先级更新
});
};
return (
<div>
{isPending() && <Spinner />}
<button onClick={updateTab}>Switch Tab</button>
</div>
);
}
SolidJS 组件是纯函数,只在初始化时执行一次
// 组件类型定义
export type Component<P extends Record<string, any> = {}> =
(props: P) => JSX.Element;
export type ParentComponent<P = {}> =
Component<P & { children?: JSX.Element }>;
export type FlowComponent<P = {}, C = JSX.Element> =
Component<P & { children: C }>;
组件特点:
export function createComponent<T extends Record<string, any>>(
Comp: Component<T>,
props: T
): JSX.Element {
if (hydrationEnabled) {
if (sharedConfig.context) {
const c = sharedConfig.context;
setHydrateContext(nextHydrateContext());
const r = IS_DEV
? devComponent(Comp, props || ({} as T))
: untrack(() => Comp(props || ({} as T)));
setHydrateContext(c);
return r;
}
}
// 生产环境:untrack 下执行组件
if (IS_DEV) return devComponent(Comp, props || ({} as T));
return untrack(() => Comp(props || ({} as T)));
}
export function mergeProps<T extends unknown[]>(...sources: T): MergeProps<T> {
let proxy = false;
// 检查是否有 proxy 或函数
for (let i = 0; i < sources.length; i++) {
const s = sources[i];
proxy = proxy || (!!s && $PROXY in (s as object));
sources[i] = typeof s === "function"
? ((proxy = true), createMemo(s as EffectFunction<unknown>))
: s;
}
// Proxy 实现(支持响应式 props)
if (SUPPORTS_PROXY && proxy) {
return new Proxy({
get(property) {
for (let i = sources.length - 1; i >= 0; i--) {
const v = resolveSource(sources[i])[property];
if (v !== undefined) return v;
}
},
has(property) { /* ... */ },
keys() { /* ... */ }
}, propTraps) as unknown as MergeProps<T>;
}
// 非 Proxy 回退
// ...
}
export function splitProps<
T extends Record<any, any>,
K extends [readonly (keyof T)[], ...(readonly (keyof T)[])[]]
>(props: T, ...keys: K): SplitProps<T, K> {
const len = keys.length;
if (SUPPORTS_PROXY && $PROXY in props) {
const blocked = len > 1 ? keys.flat() : keys[0];
// 为每个 key 组创建 Proxy
const res = keys.map(k => {
return new Proxy({
get(property) {
return k.includes(property) ? props[property as any] : undefined;
},
has(property) {
return k.includes(property) && property in props;
},
keys() {
return k.filter(property => property in props);
}
}, propTraps);
});
// 剩余 props
res.push(new Proxy({
get(property) {
return blocked.includes(property) ? undefined : props[property as any];
},
// ...
}, propTraps));
return res as SplitProps<T, K>;
}
// ...
}
export function createRoot<T>(fn: RootFunction<T>, detachedOwner?: typeof Owner): T {
const listener = Listener,
owner = Owner,
unowned = fn.length === 0,
current = detachedOwner === undefined ? owner : detachedOwner;
// 创建新的 Owner
const root: Owner = unowned
? IS_DEV
? { owned: null, cleanups: null, context: null, owner: null }
: UNOWNED
: {
owned: null,
cleanups: null,
context: current ? current.context : null,
owner: current
};
Owner = root;
Listener = null;
try {
return runUpdates(updateFn as () => T, true)!;
} finally {
Listener = listener;
Owner = owner;
}
}
Owner 树管理组件生命周期:
createRoot
│
▼
App (Owner)
│
├── Component A (owned)
│ │
│ ├── createEffect
│ │
│ └── createMemo
│
└── Component B (owned)
│
├── createEffect
│
└── Component C (owned)
│
└── createEffect
清理规则:
- 父组件销毁时,所有子组件和 Effect 自动清理
- onCleanup 按照创建的逆序执行
- context 沿着 Owner 树向上查找
export function onCleanup<T extends () => any>(fn: T): T {
if (Owner === null) {
IS_DEV && console.warn(
"cleanups created outside a `createRoot` or `render` will never be run"
);
} else if (Owner.cleanups === null) {
Owner.cleanups = [fn];
} else {
Owner.cleanups.push(fn);
}
return fn;
}
function cleanNode(node: Owner) {
// 执行清理函数
if (node.cleanups) {
for (let i = 0; i < node.cleanups.length; i++) {
node.cleanups[i]();
}
node.cleanups = null;
}
// 清理所有子节点
if (node.owned) {
for (let i = 0; i < node.owned.length; i++) {
cleanNode(node.owned[i]);
}
node.owned = null;
}
}
export interface Context<T> {
id: symbol;
Provider: ContextProviderComponent<T>;
defaultValue: T;
}
export function createContext<T>(
defaultValue?: T,
options?: EffectOptions
): Context<T | undefined> {
const id = Symbol("context");
return { id, Provider: createProvider(id, options), defaultValue };
}
export function useContext<T>(context: Context<T>): T {
let value: undefined | T;
return Owner && Owner.context && (value = Owner.context[context.id]) !== undefined
? value
: context.defaultValue;
}
Context 特点:
export function createResource<T, S, R>(
source: ResourceSource<S>,
fetcher: ResourceFetcher<S, T, R>,
options?: ResourceOptions<T, S>
): ResourceReturn<T, R> {
// ...
const [value, setValue] = createSignal<T | undefined>(options.initialValue),
[error, setError] = createSignal<unknown>(undefined),
[state, setState] = createSignal<"unresolved" | "pending" | "ready" | "refreshing">(
resolved ? "ready" : "unresolved"
);
function read() {
const c = SuspenseContext && useContext(SuspenseContext);
if (err !== undefined && !pr) throw err;
// Suspense 集成
if (Listener && !Listener.user && c) {
createComputed(() => {
track();
if (pr) {
c.increment!();
contexts.add(c);
}
});
}
return value();
}
// ...
}
export type SuspenseContextType = {
increment?: () => void; // 增加挂起计数
decrement?: () => void; // 减少挂起计数
inFallback?: () => boolean;
effects?: Computation<any>[];
resolved?: boolean;
};
let SuspenseContext: SuspenseContext;
export function getSuspenseContext() {
return SuspenseContext || (SuspenseContext = createContext<SuspenseContextType | undefined>());
}
Suspense 工作原理:
export function lazy<T extends Component<any>>(
fn: () => Promise<{ default: T }>
): T & { preload: () => Promise<{ default: T }> } {
let comp: () => T | undefined;
let p: Promise<{ default: T }> | undefined;
const wrap: T & { preload?: () => void } = ((props: any) => {
const ctx = sharedConfig.context;
if (ctx) {
// SSR hydration
const [s, set] = createSignal<T>();
(p || (p = fn())).then(mod => {
set(() => mod.default);
});
comp = s;
} else if (!comp) {
// 客户端懒加载
const [s] = createResource<T>(() =>
(p || (p = fn())).then(mod => mod.default)
);
comp = s;
}
return createMemo(() => comp() ? untrack(() => comp()(props)) : "");
}) as T;
wrap.preload = () => p || ((p = fn()).then(mod => (comp = () => mod.default)), p);
return wrap as T & { preload: () => Promise<{ default: T }> };
}
export function createDynamic<T extends ValidComponent>(
component: () => T | undefined,
props: ComponentProps<T>
): JSX.Element {
const cached = createMemo<Function | string | undefined>(component);
return createMemo(() => {
const component = cached();
switch (typeof component) {
case "function":
if (isDev) Object.assign(component, { [$DEVCOMP]: true });
return untrack(() => component(props));
case "string":
const isSvg = SVGElements.has(component);
const el = sharedConfig.context
? getNextElement()
: createElement(component, isSvg, untrack(() => props.is));
spread(el, props, isSvg);
return el;
}
}) as unknown as JSX.Element;
}
// 使用
<Dynamic component={multiline() ? 'textarea' : 'input'} value={value()} />
export function Portal(props: {
mount?: Node;
useShadow?: boolean;
isSVG?: boolean;
children: JSX.Element;
}) {
const marker = document.createTextNode(""),
mount = () => props.mount || document.body,
owner = getOwner();
let content: undefined | (() => JSX.Element);
createEffect(() => {
content || (content = runWithOwner(owner, () => createMemo(() => props.children)));
const el = mount();
if (el instanceof HTMLHeadElement) {
// Head 元素特殊处理
createRoot(dispose => insert(el, () => (!clean() ? content!() : dispose()), null));
} else {
// 创建容器
const container = createElement(props.isSVG ? "g" : "div", props.isSVG),
renderRoot = props.useShadow
? container.attachShadow({ mode: "open" })
: container;
insert(renderRoot, content);
el.appendChild(container);
onCleanup(() => el.removeChild(container));
}
});
return marker;
}
用于高效列表选中状态的优化原语
export function createSelector<T, U = T>(
source: Accessor<T>,
fn: EqualityCheckerFunction<T, U> = equalFn as TODO
): (key: U) => boolean {
const subs = new Map<U, Set<Computation<any>>>();
const node = createComputation(
(p: T | undefined) => {
const v = source();
// 只通知状态变化的观察者
for (const [key, val] of subs.entries()) {
if (fn(key, v) !== fn(key, p!)) {
for (const c of val.values()) {
c.state = STALE;
if (c.pure) Updates!.push(c);
else Effects!.push(c);
}
}
}
return v;
},
undefined,
true,
STALE
) as Memo<any>;
updateComputation(node);
return (key: U) => {
// 动态订阅
if (Listener) {
let l = subs.get(key) || subs.set(key, new Set([Listener])).get(key)!;
l.add(listener);
onCleanup(() => { l.delete(listener); !l.size && subs.delete(key); });
}
return fn(key, node.value!);
};
}
untrack 在执行时暂停依赖追踪
export function untrack<T>(fn: Accessor<T>): T {
if (!ExternalSourceConfig && Listener === null) return fn();
const listener = Listener;
Listener = null;
try {
if (ExternalSourceConfig) return ExternalSourceConfig.untrack(fn);
return fn();
} finally {
Listener = listener;
}
}
// 使用示例
createEffect(() => {
const value = signalA(); // 被追踪
const other = untrack(() => signalB()); // 不被追踪
// effect 只依赖 signalA
});
export function batch<T>(fn: Accessor<T>): T {
return runUpdates(fn, false) as T;
}
// 使用示例
function updateAll() {
batch(() => {
setFirstName("John");
setLastName("Doe");
setAge(30);
// 所有更新合并为一次渲染
});
}
batch 的效果:
export function on<S, Next>(
deps: AccessorArray<S> | Accessor<S>,
fn: OnEffectFunction<S, undefined | NoInfer<Prev>, Next>,
options?: OnOptions
): EffectFunction<undefined | NoInfer<Next>> {
const isArray = Array.isArray(deps);
let prevInput: S;
let defer = options && options.defer;
return prevValue => {
let input: S;
if (isArray) {
input = Array(deps.length) as unknown as S;
for (let i = 0; i < deps.length; i++)
(input as unknown as any[])[i] = deps[i]();
} else {
input = deps();
}
if (defer) {
defer = false;
return prevValue;
}
const result = untrack(() => fn(input, prevInput, prevValue));
prevInput = input;
return result;
};
}
// 使用
createEffect(on([a, b], ([v1, v2]) => {
// 只在 a 或 b 变化时执行
}));
| 框架 | 更新机制 | 首次渲染 | 更新速度 |
|---|---|---|---|
| SolidJS | 细粒度响应式 | 极快 | 极快 |
| React | Virtual DOM | 快 | 中等 |
| Vue 3 | 响应式 + VDOM | 快 | 快 |
| Svelte | 编译时 | 极快 | 极快 |
SolidJS 性能优势:
React Hooks
SolidJS Primitives
// React - 每次渲染都执行
useEffect(() => {
console.log(count); // 闭包问题
}, [count]);
// SolidJS - 只执行一次,自动追踪
createEffect(() => {
console.log(count()); // 总是最新值
});
Vue 3 Reactivity
SolidJS Reactivity
// Vue 3
const count = ref(0);
console.log(count.value);
// SolidJS
const [count, setCount] = createSignal(0);
console.log(count());
DO
DON'T
// ❌ 错误
const value = signal(); // 失去响应性
// ✅ 正确
const value = signal(); // 在需要时调用
// 陷阱 1:解构失去响应性
const { name } = props; // ❌
// 正确:props.name
// 陷阱 2:条件中使用 Signal
if (signal()) { } // 只在初始化时读取
// 正确:createMemo(() => signal() && ...)
// 陷阱 3:事件处理器中的闭包
onClick={() => console.log(count())} // ✅ 正确,每次都读取最新值
// 陷阱 4:忘记清理
createEffect(() => {
const timer = setInterval(...);
// 忘记 onCleanup
});
// 正确:
createEffect(() => {
const timer = setInterval(...);
onCleanup(() => clearInterval(timer));
});
SolidJS 响应式系统核心:
设计哲学:
💎 SolidJS - 简单、高效、强大