渲染部件

    应用程序只需要关心,将它们的期望的输出结构声明为有层级的虚拟 DOM 节点即可,通常是作为部件的渲染函数的返回值来完成的。然后,框架的 组件会将期望的输出同步为 DOM 中的具体元素。也可以通过给虚拟 DOM 节点传入属性,从而配置部件和元素,以及为部件和元素提供状态。

    Dojo 支持树的部分子节点渲染,这意味着当状态发生变化时,框架能够定位到受变化影响的 VDOM 节点的对应子集。然后,只更新 DOM 树中受影响的子树,从而响应变化、提高渲染性能并改善用户的交互体验。

    Dojo 支持使用 jsx 语法扩展,在 TypeScript 中被称为 tsx。此语法能更方便的描述 VDOM 的输出,并且更接近于构建的应用程序中的 HTML。

    可以通过 轻松搭建出允许使用 TSX 的项目。

    对于不是通过这种方式搭建的 Dojo 项目,可以通过在项目的 TypeScript 配置中添加以下内容来启用 TSX:

    ./tsconfig.json

    TSX 部件示例

    具有 .tsx 文件扩展名的部件,要在渲染函数中输出 TSX,只需要导入 @dojo/framework/core/vdom 模块中的 tsx 函数:

    src/widgets/MyTsxWidget.tsx

    基于函数的部件:

    1. import { create, tsx } from '@dojo/framework/core/vdom';
    2. const factory = create();
    3. export default factory(function MyTsxWidget() {
    4. return <div>Hello from a TSX widget!</div>;
    5. });

    基于类的部件:

    1. import WidgetBase from '@dojo/framework/core/WidgetBase';
    2. import { tsx } from '@dojo/framework/core/vdom';
    3. protected render() {
    4. return <div>Hello from a TSX widget!</div>;
    5. }
    6. }

    若部件需要返回多个顶级 TSX 节点,则可以将它们包裹在 <virtual> 容器元素中。这比返回节点数组更清晰明了,因为这样支持更自然的自动格式化 TSX 代码块。如下:

    基于函数的部件:

    VDOM 节点类型

    Dojo 会在 VDOM 中识别出两类节点:

    • VNode,或称为 Virtual Nodes,是具体 DOM 元素的虚拟表示,作为所有 Dojo 应用程序最底层的渲染输出。
    • WNode,或称为 Widget Nodes,将 Dojo 部件关联到 VDOM 的层级结构上。

    Dojo 的虚拟节点中,VNodeWNode 都可看作 DNode 的子类型,但应用程序通常不处理抽象层面的 DNode。推荐使用 TSX 语法,因为它能以统一的语法渲染两类虚拟节点。

    如果不想使用 TSX,在部件中可以导入 @dojo/framework/core/vdom 模块中的 v()w() 函数。它们分别创建 VNodeWNode,并可作为返回值的一部分。它们的签名,抽象地说,如下:

    • v(tagName | VNode, properties?, children?):
    • w(Widget | constructor, properties, children?)

    虚拟节点示例

    以下示例部件包含一个更有代表性的渲染函数,它返回一个 VNode。它期望的结构描述为,一个简单的 div DOM 元素下包含一个文本节点:

    src/widgets/MyWidget.ts

    基于函数的部件:

    1. import { create, v } from '@dojo/framework/core/vdom';
    2. const factory = create();
    3. export default factory(function MyWidget() {
    4. return v('div', ['Hello, Dojo!']);
    5. });

    基于类的部件:

    1. import WidgetBase from '@dojo/framework/core/WidgetBase';
    2. import { v } from '@dojo/framework/core/vdom';
    3. export default class MyWidget extends WidgetBase {
    4. protected render() {
    5. return v('div', ['Hello, Dojo!']);
    6. }
    7. }

    组合部件的示例

    类似地,也可以使用 w() 方法组合部件,还可以混合使用两种类型的节点来输出多个节点,以形成更复杂的层级结构:

    src/widgets/MyComposingWidget.ts

    基于函数的部件:

    基于类的部件:

    1. import { v, w } from '@dojo/framework/core/vdom';
    2. import MyWidget from './MyWidget';
    3. export default class MyComposingWidget extends WidgetBase {
    4. protected render() {
    5. return v('div', ['This widget outputs several virtual nodes in a hierarchy', w(MyWidget, {})]);
    6. }
    7. }

    应用程序通常在主入口点 (main.tsx/main.ts) 调用 renderer() 函数,然后将返回的 Renderer 对象挂载到应用程序的 HTML 页面中指定的 DOM 元素上。如果挂载应用程序时没有指定元素,则默认挂载到 document.body 下。

    例如:

    1. import renderer, { tsx } from '@dojo/framework/core/vdom';
    2. import MyComposingWidget from './widgets/MyComposingWidget';
    3. const r = renderer(() => <MyComposingWidget />);
    4. r.mount();

    Renderer.mount() 方法接收一个可选参数 MountOptions,该参数用于配置如何执行挂载操作。

    例如,将一个 Dojo 应用程序挂载到一个指定的 DOM 元素,而不是 document.body 下:

    src/index.html

    src/main.tsx

    1. import renderer, { tsx } from '@dojo/framework/core/vdom';
    2. import MyComposingWidget from './widgets/MyComposingWidget';
    3. const dojoAppRootElement = document.getElementById('my-dojo-app') || undefined;
    4. const r = renderer(() => <MyComposingWidget />);
    5. r.mount({ domNode: dojoAppRootElement });

    Dojo 可以包装外部的 DOM 元素,有效地将它们引入到应用程序的 VDOM 中,用作渲染输出的一部分。这是通过 @dojo/framework/core/vdom 模块中的 dom() 工具方法完成的。它的工作原理与 v() 类似,但它的主参数使用的是现有的 DOM 节点而不是元素标记字符串。在返回 VNode 时,它会引用传递给它的 DOM 节点,而不是使用 v() 新创建的元素。

    一旦 dom() 返回的 VNode 添加到应用程序的 VDOM 中,Dojo 应用程序就实际获得了被包装 DOM 节点的所有权。请注意,此过程仅适用于 Dojo 应用程序的外部节点,如挂载应用程序元素的兄弟节点,或与主网页的 DOM 断开连接的新创建的节点。如果包装的节点是挂载了应用程序的元素的祖先或子孙节点,将无效。

    dom() API

    • dom({ node, attrs = {}, props = {}, on = {}, diffType = 'none', onAttach })

    检测外部 DOM 节点的变化

    通过 dom() 添加的外部节点是从常规的虚拟 DOM 节点中移除的,因为它们可能会在 Dojo 应用程序之外被处理。这意味着 Dojo 不能主要使用 VNode 的属性设置元素的状态,而是必须依赖 DOM 节点本身的 JavaScript 属性(properties)和 HTML 属性(attributes)。

    dom() 接收 diffType 属性,允许用户为包装的节点指定属性变更检测策略。一个指定的策略,会指明如何使用包装的节点,以帮助 Dojo 来确定 JavaScript 属性和 HTML 属性是否已变化,然后将变化应用到包装的 DOM 节点上。默认的策略是 none,意味着 Dojo 只需在每个渲染周期将包装好的 DOM 元素添加到应用程序输出中。

    注意: 所有的策略都使用前一次 VNode 中的事件,以确保它们会被正确的删除并应用到每个渲染中。