preact-options-hooks

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Preact Options Hooks

Preact Options Hooks

Preact exposes a mutable
options
object (
import { options } from 'preact'
) 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
bippy
.
Preact 暴露了一个可变的
options
对象(
import { options } from 'preact'
),它作为渲染流水线中的全局钩子系统。任何工具、库或开发者工具都可以通过猴子补丁的方式使用这些钩子,来观察或拦截diff/commit周期的每个阶段——无需 fibers,无需DevTools协议,也无需
bippy

How 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:
HookMangled nameFires whenSignature
before-diff
options.__b
Just before a VNode starts diffing (called for every VNode, host and component). Good place to detect start of a commit batch.
(vnode: VNode) => void
before-render
options.__r
Immediately before a component VNode's
render()
/ function body executes. Best place to start a performance timer.
(vnode: VNode) => void
diffed
options.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.
(vnode: VNode) => void
commit
options.__c
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.
(vnode: VNode, commitQueue: Component[]) => void
unmount
options.unmount
Before a VNode is removed from the tree.
(vnode: VNode) => void
hook
options.__h
When a hook (useState, useEffect, etc.) is invoked inside a component.
hookType
is an integer identifying the hook kind.
(component: Component, index: number, hookType: number) => void
在单个渲染周期中,钩子的触发顺序如下:
钩子混淆名称触发时机签名
before-diff
options.__b
就在VNode开始diff之前触发(对所有VNode触发,包括宿主元素和组件)。适合用于检测提交批次的开始。
(vnode: VNode) => void
before-render
options.__r
就在组件VNode的
render()
/函数体执行前触发。适合用于启动性能计时器。
(vnode: VNode) => void
diffed
options.diffed
在VNode(及其所有子节点)完成diff后触发。此时DOM已更新。适合用于停止计时器并读取最终的DOM。
(vnode: VNode) => void
commit
options.__c
在整棵树的diff完成且提交队列即将刷新(生命周期方法/副作用)时触发。标志着提交批次的结束。
(vnode: VNode, commitQueue: Component[]) => void
unmount
options.unmount
在VNode从树中移除前触发。
(vnode: VNode) => void
hook
options.__h
当组件内部调用钩子(useState、useEffect等)时触发。
hookType
是一个标识钩子类型的整数。
(component: Component, index: number, hookType: number) => void

Less common hooks

不太常用的钩子

HookMangled namePurpose
options.vnode
options.vnode
Called when a VNode is created (
createElement
/ JSX). Can mutate the vnode.
options.event
options.event
Called before DOM events are processed.
options.debounceRendering
options.debounceRendering
Called so renders can be batched, by default this is
queueMicrotask
.
钩子混淆名称用途
options.vnode
options.vnode
当VNode被创建时(
createElement
/JSX)触发。可以修改vnode。
options.event
options.event
在DOM事件被处理前触发。
options.debounceRendering
options.debounceRendering
触发时可对渲染进行批处理,默认是
queueMicrotask

Accessing internal VNode properties

访问VNode的内部属性

Preact 10.x mangles internal properties. The key ones on a VNode:
PropertyMangledTypeMeaning
component
__c
Component | null
The class/function component instance
dom
__e
Element | Text | null
First DOM node produced by this VNode
children
__k
VNode[] | null
Child VNodes
parent
__
VNode | null
Parent VNode
flags
__b
number
Internal diff flags / start offset
index
__i
number
Index in parent's children array
Specifically for
flags
, they have a certain bit-wise meaning, where
1<<7
means that the vnode is suspended and
1<<5
means that the node is hydrating.
Preact 10.x会混淆内部属性。VNode上的关键属性如下:
属性混淆名称类型含义
component
__c
Component | null
类/函数组件实例
dom
__e
Element | Text | null
该VNode生成的第一个DOM节点
children
__k
VNode[] | null
子VNode列表
parent
__
VNode | null
父VNode
flags
__b
number
内部diff标志/起始偏移量
index
__i
number
在父节点子数组中的索引
特别说明
flags
,它们具有特定的位运算含义,其中
1<<7
表示vnode处于挂起状态,
1<<5
表示节点正在进行hydrate。

Accessing internal Component properties

访问Component的内部属性

PropertyMangledTypeMeaning
vnode
__v
VNode
The VNode this component rendered
nextState
__s
object
Pending state (before commit)
dirty
__d
boolean
Whether component is queued for re-render
force
__f
boolean
Whether
forceUpdate()
was called
hooks
__H
{ __: HookState[], __h: HookState[] } | null
Hooks state container.
__
is the ordered list;
__h
is pending effects.
Each
HookState
has
__
(the current value) and
__H
(deps array, if applicable).
属性混淆名称类型含义
vnode
__v
VNode
该组件渲染的VNode
nextState
__s
object
待处理状态(提交前)
dirty
__d
boolean
组件是否已排队等待重新渲染
force
__f
boolean
是否调用了
forceUpdate()
hooks
__H
{ __: HookState[], __h: HookState[] } | null
钩子状态容器。
__
是有序列表;
__h
是待处理的副作用。
每个
HookState
包含
__
(当前值)和
__H
(依赖数组,如果有的话)。

Getting 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
__e
may be
null
(fragments, Providers, etc.). Walk the child VNode list:
ts
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的
__e
可能为
null
(比如片段、Providers等)。此时需要遍历子VNode列表:
ts
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
alternate
. The simplest approach: track whether you've seen the component instance before (e.g., via a WeakMap or a custom property on the
vnode.__c
).
如果组件没有之前的props快照/没有
alternate
,则表示该组件正在挂载。最简单的方法是:通过WeakMap或
vnode.__c
上的自定义属性,跟踪你是否已经见过该组件实例。

Commit batching

提交批处理

options.__b
fires for every VNode in a diff pass. The first call marks the start of a commit.
options.__c
fires once at the end with the full commit queue. This lets you implement
onCommitStart
/
onCommitFinish
semantics.
options.__b
会在diff过程中对每个VNode触发。第一次调用标志着提交的开始。
options.__c
会在结束时触发一次,并传入完整的提交队列。这让你可以实现
onCommitStart
/
onCommitFinish
的语义。

Important notes

重要注意事项

  • These hooks are synchronous — they run inline during diffing. Keep them fast.
  • options.__b
    fires for all VNodes (host elements too), not just components. Filter with
    typeof vnode.type === 'function'
    when you only care about components,
    string
    for dom-nodes or null for text-nodes.
  • The mangled names (
    __b
    ,
    __r
    ,
    __c
    ,
    __h
    ,
    __e
    , etc.) are stable across Preact 10.x and are the canonical way to access internals — Preact's own addons (
    preact/debug
    ,
    preact/devtools
    ) use them.
  • options.diffed
    fires bottom-up (children before parents).
    options.__b
    fires top-down.
  • Always chain (save + call previous hook) to avoid breaking other addons.
Some examples of real-world usage:
  • 这些钩子是同步的——它们在diff过程中同步运行。请确保它们的执行速度很快。
  • options.__b
    会对所有VNode触发(包括宿主元素),而不仅仅是组件。当你只关心组件时,可以用
    typeof vnode.type === 'function'
    过滤,宿主元素用
    string
    ,文本节点用null。
  • 混淆后的名称(
    __b
    ,
    __r
    ,
    __c
    ,
    __h
    ,
    __e
    等)在Preact 10.x中是稳定的,是访问内部属性的标准方式——Preact自身的插件(
    preact/debug
    ,
    preact/devtools
    )也在使用它们。
  • options.diffed
    会自底向上触发(子节点先于父节点)。
    options.__b
    会自顶向下触发。
  • 务必链式调用(保存并调用之前的钩子),避免破坏其他插件。
一些实际应用的例子: