通过属性配置小部件
将属性传递给 VDOM 中节点的概念是 Dojo 的核心支柱。节点属性是通过应用程序传播状态的主要管道,将状态从父级传递到子级小部件,以及通过事件处理程序向上传递到层次结构。它们还充当消费者与小部件交互的主要 API,其中父级小部件传递属性来配置它们自己的 DOM 表示(当返回 VNode
时)以及它们可能管理的任何子级小部件(当返回 WNode
时)。
VNode
接受类型为 VNodeProperties
的属性,而 WNode
至少接受 WidgetProperties
。小部件作者通常会定义自己的属性接口,然后客户端需要传入该接口。
key
VDOM 节点 WidgetProperties
非常简单,包含一个可选的 key
属性,该属性在 VNodeProperties
中也很常见。
当小部件开始在 VDOM 中的同一级别输出多个相同类型元素时,需要为每个单独的元素指定一个 key
。例如,管理多个列表项的列表小部件需要为列表中的每个单独项指定一个 key
。
Dojo 使用虚拟节点的 key
来唯一标识重新渲染受影响的 VDOM 部分时的特定实例。如果没有 key
来区分 VDOM 中同一级别上的多个相同类型节点,Dojo 就无法准确地确定哪些节点子集可能受到无效更改的影响。
注意:虚拟节点
key
应该在多个渲染函数调用中保持一致。为每个渲染调用中应该相同输出节点生成不同的key
被认为是 Dojo 应用程序开发中的反模式,应该避免。
key
定义小部件 传统上,小部件的 key
属性由 Dojo 渲染引擎用于唯一标识和跟踪跨渲染的小部件。但是,更新 key
属性也是一种有效的方法,可以保证在下次渲染时,Dojo 的渲染引擎将重新创建小部件,而不是重用之前的实例。重新创建小部件时,所有先前状态将被重置。当处理基于小部件属性值管理逻辑的小部件时,此行为很有用。
Dojo 为小部件作者提供了一种机制,可以使用 create()
工厂中的 .key()
链式方法将小部件属性与小部件的标识相关联。
import { create } from '@dojo/framework/core/vdom';
interface MyWidgetProperties {
id: string;
}
const factory = create()
.properties<MyWidgetProperties>()
.key('id');
使用此工厂,如果 id
属性发生变化,Dojo 将重新创建小部件实例。此强大功能为小部件作者提供了保证,当定义的属性发生变化时,他们的 widget 将被重新创建,因此不必处理复杂的逻辑来根据属性刷新数据。
import { create } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';
interface MyWidgetProperties {
id: string;
}
const factory = create({ icache })
.properties<MyWidgetProperties>()
.key('id');
const MyWidget = factory(function MyWidget({ properties, middleware: { icache } }) {
const { id } = properties();
const data = icache.getOrSet('data', async () => {
const response = await fetch(`https://my-api/items/${id}`);
const json = await response.json();
return json.data;
});
if (!data) {
return <div>Loading Data...</div>;
}
return (
<div>
<ul>{data.map((item) => <li>{item}</li>)}</ul>
</div>
);
});
此示例演示了根据 id
属性获取数据。如果不使用 .key('id)
,小部件将需要管理 id
属性发生变化的情况。这将包括确定属性是否实际发生变化的逻辑,重新获取相关数据以及显示加载消息。使用 .key('id')
保证当 id
属性发生变化时,小部件将被重新创建并重置状态,并且小部件将显示“正在加载数据...”消息并根据更新的 id
获取数据。
VNode
配置 VNodeProperties
包含许多字段,这些字段充当与 DOM 中具体元素交互的主要 API。许多这些属性与 HTMLElement
上可用的属性相匹配,包括指定各种 oneventname
事件处理程序。
这些属性的应用被认为是单向的,因为 Dojo 将给定的集合应用于具体的 DOM 元素,但不会将对相应 DOM 属性的任何进一步更改同步回给定的 VNodeProperties
。任何此类更改应该改为通过事件处理程序传播回 Dojo 应用程序。当调用事件处理程序时,应用程序可以处理事件所需的任何状态更改,在输出其 VDOM 结构以进行渲染时更新其对相应 VNodeProperties
的视图,然后让 Dojo 的 Renderer
将任何相关更新与 DOM 同步。
更改属性和差异检测
Dojo 使用虚拟节点属性来确定给定节点是否已更新,因此需要重新渲染。具体来说,它使用差异检测策略来比较来自先前和当前渲染帧的属性集。如果在节点接收的最新属性集中检测到差异,则该节点将失效并在下一个绘制周期中重新渲染。
请注意,在属性差异检测期间会忽略函数属性,因为在每次渲染时实例化一个新函数是一种常见模式。考虑以下示例,其中子级小部件 ChildWidget
在每次渲染时都会收到一个新的 onClick
函数。
export const ParentWidget(function ParentWidget() {
return <ChildWidget onClick={() => {
console.log('child widget clicked.');
}} />
});
如果在差异检测期间检查函数,这将导致 ChildWidget
在 ParentWidget
每次渲染时都重新渲染。
注意:属性更改检测由框架在内部管理,并且依赖于小部件渲染函数的 VDOM 输出的声明式结构。尝试保留对属性的引用并在正常的 widget 渲染周期之外修改它们 被认为是 Dojo 应用程序开发中的反模式,应该避免。