自定义节点开发

Comfy 钩子(Hooks)

扩展钩子 在 Comfy 执行的不同阶段,应用会调用 invokeExtensionsAsync 或 invokeExtensions ,并传入钩子的名称。 这些方法会在所有已注册的扩展上调用同名方法(如果存在),比如上面例子中的 setup 。 Comfy 提供了多种钩子,供自定义扩展代码使用,以修改客户端行为。 这些钩子会在 Comfy 客户端元素的创建

扩展钩子

在 Comfy 执行的不同阶段,应用会调用 #invokeExtensionsAsync#invokeExtensions,并传入钩子的名称。 这些方法会在所有已注册的扩展上调用同名方法(如果存在),比如上面例子中的 setup

Comfy 提供了多种钩子,供自定义扩展代码使用,以修改客户端行为。

下面介绍了一些最重要的钩子。由于 Comfy 仍在积极开发中,新的钩子会不时加入,因此可以在 app.js 中搜索 #invokeExtensions 以查找所有可用钩子。

另请参阅钩子的调用顺序

常用钩子

beforeRegisterNodeDef 开始,大多数扩展都会用到它,通常也是唯一需要的钩子。

beforeRegisterNodeDef()

对每种节点类型(即 AddNode 菜单中可用的节点列表)调用一次,用于修改节点的行为。

Javascript
async beforeRegisterNodeDef(nodeType, nodeData, app)

传入的 nodeType 参数本质上是该类型所有节点的模板,因此对 nodeType.prototype 的修改会应用到所有该类型节点上。nodeData 封装了 Python 代码中定义的节点相关信息,如类别、输入和输出。app 是主 Comfy app 对象的引用(你应该已经导入了它!)

常见做法是检查 nodeType.ComfyClass,它保存了该节点对应的 Python 类名,以判断是否需要修改该节点。通常这意味着只修改你自己添加的自定义节点,但有时也可能需要修改其他节点(或其他自定义节点也可能修改你的节点!),此时要注意兼容性。

beforeRegisterNodeDef 中非常常见的做法是"劫持"已有方法:

Javascript
async beforeRegisterNodeDef(nodeType, nodeData, app) {
	if (nodeType.comfyClass=="MyNodeClass") {
		const onConnectionsChange = nodeType.prototype.onConnectionsChange;
		nodeType.prototype.onConnectionsChange = function (side,slot,connect,link_info,output) {
			const r = onConnectionsChange?.apply(this, arguments);
			console.log("Someone changed my connection!");
			return r;
		}
	}
}

这种做法是先保存原型上的原方法,然后替换为新方法。新方法会调用原方法(?.apply 保证即使没有原方法也不会出错),然后执行额外操作。根据你的代码逻辑,可能需要在新方法的其他位置调用 apply,甚至有条件地调用。

以这种方式劫持方法时,建议查看核心 comfy 代码(断点调试很有用),以确保方法签名一致。

nodeCreated()

Javascript
async nodeCreated(node)

当某个节点实例被创建时调用(就在 nodeType 上的 ComfyNode() 构造函数结束时)。在这个钩子里你可以修改节点的具体实例。

init()

Javascript
async init()

当 Comfy 网页被加载(或重新加载)时调用。调用时机是在图对象已创建,但还未注册或创建任何节点之前。可以用来劫持 app 或 graph(LiteGraph 对象)的方法,从而修改核心 Comfy 行为。详见Comfy 对象与劫持

setup()

Javascript
async setup()

在启动流程结束时调用。适合添加事件监听器(无论是 Comfy 事件还是 DOM 事件),或添加全局菜单,相关内容在其他地方有详细介绍。

调用顺序

以下顺序是通过在 Comfy app.js 文件中插入日志代码获得的。你也可以用类似方法帮助理解执行流程。

Javascript
/* 截至目前大约在第 220 行: */
	#invokeExtensions(method, ...args) {
		console.log(`invokeExtensions      ${method}`) // 此行为新增
		// ...
	}
/* 截至目前大约在第 250 行: */
	async #invokeExtensionsAsync(method, ...args) {
		console.log(`invokeExtensionsAsync ${method}`) // 此行为新增
		// ...
	}

网页加载时

text
invokeExtensionsAsync init
invokeExtensionsAsync addCustomNodeDefs
invokeExtensionsAsync getCustomWidgets
invokeExtensionsAsync beforeRegisterNodeDef    [多次重复]
invokeExtensionsAsync registerCustomNodes
invokeExtensionsAsync beforeConfigureGraph
invokeExtensionsAsync nodeCreated
invokeExtensions      loadedGraphNode
invokeExtensionsAsync afterConfigureGraph
invokeExtensionsAsync setup

加载工作流

text
invokeExtensionsAsync beforeConfigureGraph
invokeExtensionsAsync beforeRegisterNodeDef   [0、1 或多次]
invokeExtensionsAsync nodeCreated             [多次重复]
invokeExtensions      loadedGraphNode         [多次重复]
invokeExtensionsAsync afterConfigureGraph

添加新节点

text
invokeExtensionsAsync nodeCreated

官方原文

本页来自 Comfy-Org 官方中文文档的固定版本,并转换为 xueai 静态页面。内容以官方持续更新的页面为准。

内容授权与修改

官方文档采用 GPL-3.0。本站保留许可证、固定提交号和修改说明,不代表 ComfyUI 或 Comfy-Org 对本站背书。

GPL-3.0来源和修改说明