preact-options-hooks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChinesePreact Options Hooks
Preact Options Hooks
Preact exposes a mutable object () that acts as a global hook system into the rendering pipeline. Any tool, library, or devtool can monkey-patch these hooks to observe or intercept every phase of the diff/commit cycle — no fibers, no DevTools protocol, no .
optionsimport { options } from 'preact'bippyPreact 暴露了一个可变的对象(),它作为渲染流水线中的全局钩子系统。任何工具、库或开发者工具都可以通过猴子补丁的方式使用这些钩子,来观察或拦截diff/commit周期的每个阶段——无需 fibers,无需DevTools协议,也无需。
optionsimport { options } from 'preact'bippyHow to chain hooks
如何链式调用钩子
Always save the previous value before overwriting so other plugins (and Preact's own debug addon) keep working:
ts
import { options } from 'preact';
const prev = options.diffed;
options.diffed = (vnode) => {
// your logic
prev?.(vnode);
};To unhook, never restore the saved reference as other plugins might get lost. Instead, use a bail flag so the options chain stays intact:
ts
let active = true;
const prev = options.diffed;
options.diffed = (vnode) => {
if (active) {
// your logic
}
prev?.(vnode);
};
// To stop observing without breaking other plugins:
active = false;在覆盖钩子前,请务必保存之前的值,这样其他插件(以及Preact自身的调试插件)才能继续正常工作:
ts
import { options } from 'preact';
const prev = options.diffed;
options.diffed = (vnode) => {
// 你的逻辑
prev?.(vnode);
};要取消钩子,切勿直接恢复保存的引用,否则可能导致其他插件失效。请改用一个开关标志,保持钩子链的完整性:
ts
let active = true;
const prev = options.diffed;
options.diffed = (vnode) => {
if (active) {
// 你的逻辑
}
prev?.(vnode);
};
// 停止监听且不破坏其他插件:
active = false;Available hooks and their fire order
可用钩子及其触发顺序
During a single render cycle the hooks fire in this order:
| Hook | Mangled name | Fires when | Signature |
|---|---|---|---|
| before-diff | | Just before a VNode starts diffing (called for every VNode, host and component). Good place to detect start of a commit batch. | |
| before-render | | Immediately before a component VNode's | |
| diffed | | After a VNode (and all its children) have been diffed. The DOM is updated at this point. Best place to stop a timer and read the resulting DOM. | |
| commit | | After the entire tree's diff is done and the commit queue is about to flush (lifecycle methods / effects). Signals end of a commit batch. | |
| unmount | | Before a VNode is removed from the tree. | |
| hook | | When a hook (useState, useEffect, etc.) is invoked inside a component. | |
在单个渲染周期中,钩子的触发顺序如下:
| 钩子 | 混淆名称 | 触发时机 | 签名 |
|---|---|---|---|
| before-diff | | 就在VNode开始diff之前触发(对所有VNode触发,包括宿主元素和组件)。适合用于检测提交批次的开始。 | |
| before-render | | 就在组件VNode的 | |
| diffed | | 在VNode(及其所有子节点)完成diff后触发。此时DOM已更新。适合用于停止计时器并读取最终的DOM。 | |
| commit | | 在整棵树的diff完成且提交队列即将刷新(生命周期方法/副作用)时触发。标志着提交批次的结束。 | |
| unmount | | 在VNode从树中移除前触发。 | |
| hook | | 当组件内部调用钩子(useState、useEffect等)时触发。 | |
Less common hooks
不太常用的钩子
| Hook | Mangled name | Purpose |
|---|---|---|
| | Called when a VNode is created ( |
| | Called before DOM events are processed. |
| | Called so renders can be batched, by default this is |
| 钩子 | 混淆名称 | 用途 |
|---|---|---|
| | 当VNode被创建时( |
| | 在DOM事件被处理前触发。 |
| | 触发时可对渲染进行批处理,默认是 |
Accessing internal VNode properties
访问VNode的内部属性
Preact 10.x mangles internal properties. The key ones on a VNode:
| Property | Mangled | Type | Meaning |
|---|---|---|---|
| component | | | The class/function component instance |
| dom | | | First DOM node produced by this VNode |
| children | | | Child VNodes |
| parent | | | Parent VNode |
| flags | | | Internal diff flags / start offset |
| index | | | Index in parent's children array |
Specifically for , they have a certain bit-wise meaning, where means that the vnode is suspended and means that the node is hydrating.
flags1<<71<<5Preact 10.x会混淆内部属性。VNode上的关键属性如下:
| 属性 | 混淆名称 | 类型 | 含义 |
|---|---|---|---|
| component | | | 类/函数组件实例 |
| dom | | | 该VNode生成的第一个DOM节点 |
| children | | | 子VNode列表 |
| parent | | | 父VNode |
| flags | | | 内部diff标志/起始偏移量 |
| index | | | 在父节点子数组中的索引 |
特别说明,它们具有特定的位运算含义,其中表示vnode处于挂起状态,表示节点正在进行hydrate。
flags1<<71<<5Accessing internal Component properties
访问Component的内部属性
| Property | Mangled | Type | Meaning |
|---|---|---|---|
| vnode | | | The VNode this component rendered |
| nextState | | | Pending state (before commit) |
| dirty | | | Whether component is queued for re-render |
| force | | | Whether |
| hooks | | | Hooks state container. |
Each has (the current value) and (deps array, if applicable).
HookState____H| 属性 | 混淆名称 | 类型 | 含义 |
|---|---|---|---|
| vnode | | | 该组件渲染的VNode |
| nextState | | | 待处理状态(提交前) |
| dirty | | | 组件是否已排队等待重新渲染 |
| force | | | 是否调用了 |
| hooks | | | 钩子状态容器。 |
每个包含(当前值)和(依赖数组,如果有的话)。
HookState____HGetting a component's display name
获取组件的显示名称
ts
function getDisplayName(vnode) {
const type = vnode.type;
if (typeof type === 'string') return null; // host element like 'div'
if (typeof type === 'function') {
return type.displayName || type.name || null;
}
return null;
}ts
function getDisplayName(vnode) {
const type = vnode.type;
if (typeof type === 'string') return null; // 宿主元素如'div'
if (typeof type === 'function') {
return type.displayName || type.name || null;
}
return null;
}Finding the DOM node for a component VNode
查找组件VNode对应的DOM节点
A component VNode's may be (fragments, Providers, etc.). Walk the child VNode list:
__enullts
function getDOMNode(vnode) {
if (vnode.__e instanceof Element) return vnode.__e;
for (const child of vnode.__k || []) {
if (child?.__e instanceof Element) return child.__e;
}
return null;
}组件VNode的可能为(比如片段、Providers等)。此时需要遍历子VNode列表:
__enullts
function getDOMNode(vnode) {
if (vnode.__e instanceof Element) return vnode.__e;
for (const child of vnode.__k || []) {
if (child?.__e instanceof Element) return child.__e;
}
return null;
}Detecting mount vs update
区分挂载与更新
A component is mounting if there is no previous props snapshot / no . The simplest approach: track whether you've seen the component instance before (e.g., via a WeakMap or a custom property on the ).
alternatevnode.__c如果组件没有之前的props快照/没有,则表示该组件正在挂载。最简单的方法是:通过WeakMap或上的自定义属性,跟踪你是否已经见过该组件实例。
alternatevnode.__cCommit batching
提交批处理
options.__boptions.__conCommitStartonCommitFinishoptions.__boptions.__conCommitStartonCommitFinishImportant notes
重要注意事项
- These hooks are synchronous — they run inline during diffing. Keep them fast.
- fires for all VNodes (host elements too), not just components. Filter with
options.__bwhen you only care about components,typeof vnode.type === 'function'for dom-nodes or null for text-nodes.string - The mangled names (,
__b,__r,__c,__h, etc.) are stable across Preact 10.x and are the canonical way to access internals — Preact's own addons (__e,preact/debug) use them.preact/devtools - fires bottom-up (children before parents).
options.diffedfires top-down.options.__b - Always chain (save + call previous hook) to avoid breaking other addons.
Some examples of real-world usage:
- 这些钩子是同步的——它们在diff过程中同步运行。请确保它们的执行速度很快。
- 会对所有VNode触发(包括宿主元素),而不仅仅是组件。当你只关心组件时,可以用
options.__b过滤,宿主元素用typeof vnode.type === 'function',文本节点用null。string - 混淆后的名称(,
__b,__r,__c,__h等)在Preact 10.x中是稳定的,是访问内部属性的标准方式——Preact自身的插件(__e,preact/debug)也在使用它们。preact/devtools - 会自底向上触发(子节点先于父节点)。
options.diffed会自顶向下触发。options.__b - 务必链式调用(保存并调用之前的钩子),避免破坏其他插件。
一些实际应用的例子: