dojo dragon main logo

可用中间件

Dojo 提供了各种可选中间件,小部件在需要实现特定需求时可以包含这些中间件。

icache

一个使用 invalidator 中间件功能提供缓存的中间件,该缓存支持延迟值解析和值可用后自动小部件失效。默认情况下,缓存将在缓存中设置值时失效,但是设置 API 上有一个可选的第三个参数,可以在需要时使用它来跳过失效。

API

import icache from '@dojo/framework/core/middleware/icache';
  • icache.getOrSet<T = any>(key: any, value: any, invalidate: boolean = true): T | undefined
    • 检索给定 key 的缓存值(如果存在),否则设置 value。在这两种情况下,如果缓存值尚未解析,则返回 undefined
  • icache.get<T = any>(key: any): T | undefined
    • 检索给定 key 的缓存值,如果未设置值或值仍在等待解析,则返回 undefined
  • icache.set(key: any, value: any, invalidate: boolean = true): any
    • 为给定的 key 设置提供的 value。如果 value 是一个函数,它将被调用以获取要缓存的实际值。如果函数返回一个 promise,则将缓存一个“待处理”值,直到最终值完全解析。在所有情况下,一旦值可用并已存储在缓存中,小部件将被标记为无效,以便可以使用最终值重新渲染。
  • icache.has(key: any): boolean
    • 根据键是否在缓存中设置返回 truefalse
  • icache.delete(key: any, invalidate: boolean = true): void
    • 从缓存中删除该项。
  • icache.clear(invalidate: boolean = true)
    • 清除当前存储在小部件本地缓存中的所有值。
  • icache.pending(key: string)
    • 返回键的异步设置器的状态

当将函数传递给 icache.set 时,将传递当前缓存值,下面的示例演示了如何使用当前值来实现递增数字。

icache.set('key', (current) => {
	if (current) {
		return current + 1;
	}
	return 1;
});

icache 可以用两种不同的方式进行类型化。一种方法使用泛型来使返回类型能够在调用站点指定,对于 getOrSet,返回类型可以从值类型推断出来。如果 getOrSetvalue 是一个函数,则类型将从函数的返回类型推断出来。

import { create, tsx } from '@dojo/framework/core/vdom';
import icache from '@dojo/framework/core/middleware/icache';

const factory = create({ icache });

interface FetchResult {
	foo: string;
}

const MyIcacheWidget = factory(function MyIcacheWidget({ middleware: { icache } }) {
	// `results` will infer the type of the resolved promise, `FetchResult | undefined`
	const results = icache.getOrSet('key', async () => {
		const response = await fetch('url');
		const body: FetchResult = await response.json();
		return body;
	});

	return <div>{results}</div>;
});

但是,这种方法不会为缓存键提供任何类型化。对 icache 进行类型化的首选方法是使用 createICacheMiddleware 创建一个预类型化的中间件。这允许传递一个接口,该接口将创建一个专门针对传递的接口进行类型化的 icache 中间件,并为缓存键提供类型安全性。

import { create, tsx } from '@dojo/framework/core/vdom';
import { createICacheMiddleware } from '@dojo/framework/core/middleware/icache';

interface FetchResult {
	foo: string;
}

interface MyIcacheWidgetState {
	key: FetchResult;
}

const icache = createICacheMiddleware<MyIcacheWidgetState>();
const factory = create({ icache });

const MyIcacheWidget = factory(function MyIcacheWidget({ middleware: { icache } }) {
	// `results` will be typed to `FetchResult | undefined` based on the `MyIcacheWidgetState`
	const results = icache.getOrSet('key', async () => {
		const response = await fetch('url');
		const body: FetchResult = await response.json();
		return body;
	});

	return <div>{results}</div>;
});

theme

允许小部件在渲染时对它们的 CSS 类进行主题化,并为应用程序提供设置主题和确定当前设置的主题(如果有)的能力。

Dojo 样式和主题参考指南 中详细描述。

API

import theme from '@dojo/framework/core/middleware/theme';
  • theme.classes<T extends ClassNames>(css: T): T
    • 小部件可以传入一个或多个 CSS 类名,并将收到当前设置的主题的更新名称,这些名称可以在返回小部件虚拟节点时使用。
  • theme.set(css: Theme)
    • 允许应用程序设置特定主题。
  • theme.get(): Theme | undefined
    • 返回当前设置的主题,如果未设置主题,则返回 undefined。通常在应用程序的根小部件中使用。

i18n

允许小部件在渲染时本地化其消息文本,并为应用程序提供设置语言环境和确定当前设置的语言环境(如果有)的能力。

Dojo 国际化参考指南 中详细描述。

API

import i18n from '@dojo/framework/core/middleware/i18n';
  • i18n.localize<T extends Messages>(bundle: Bundle<T>, useDefaults = false): LocalizedMessages<T>
    • 从指定的 bundle 中返回一组消息,这些消息被本地化为当前设置的语言环境。useDefaults 控制是否在当前语言环境中没有相应值时返回默认语言的消息。默认值为 false,在这种情况下,将返回空值而不是默认语言的消息。
  • i18n.set(localeData?: LocaleData)
    • 允许应用程序设置特定语言环境。
  • i18n.get()
    • 返回当前设置的语言环境,如果未设置语言环境,则返回 undefined。通常在应用程序的根小部件中使用。

dimensions

提供有关小部件底层节点的各种大小和位置信息。

API

import dimensions from '@dojo/framework/core/middleware/dimensions';
  • dimensions.get(key: string | number): Readonly<DimensionResults>
    • 返回小部件指定 DOM 元素的尺寸信息,该元素由节点的 key 属性标识。如果节点不存在于当前小部件(尚未渲染或指定了无效键),则所有返回的值均为 0

返回的 DimensionResults 包含以下属性,这些属性从指定的 DOM 元素的源映射而来

属性 来源
client.left node.clientLeft
client.top node.clientTop
client.width node.clientWidth
client.height node.clientHeight
position.bottom node.getBoundingClientRect().bottom
position.left node.getBoundingClientRect().left
position.right node.getBoundingClientRect().right
position.top node.getBoundingClientRect().top
size.width node.getBoundingClientRect().width
size.height node.getBoundingClientRect().height
scroll.left node.scrollLeft
scroll.top node.scrollTop
scroll.height node.scrollHeight
scroll.width node.scrollWidth
offset.left node.offsetLeft
offset.top node.offsetTop
offset.width node.offsetWidth
offset.height node.offsetHeight

intersection

使用 Intersection Observer API 提供有关节点是否在特定视口中可见的信息。

由于 Intersection Observer API 仍然是一个新兴的 Web 标准,因此框架会自动确保在浏览器中运行应用程序时提供底层 API,该浏览器尚不支持该 API。请注意,从 Dojo Framework 的 v6 版本开始,尚不支持 Intersection Observer API v2

API

import intersection from '@dojo/framework/core/middleware/intersection';
  • intersection.get(key: string | number, options: IntersectionGetOptions = {}): IntersectionResult
    • 返回小部件指定 DOM 元素的交叉信息,该元素由节点的 key 属性标识。如果节点不存在于当前小部件(尚未渲染或指定了无效键),则返回一个结果,指示零交叉。

options 参数允许更详细地控制交叉计算方式。可用字段与 交叉观察器 API 选项 的字段相同。

IntersectionResult 属性

属性 类型 描述
intersectionRatio number 与根元素视口相交的元素边界框的比例,从 0.01.0。默认情况下,根元素被认为是浏览器的视口,除非通过 options.root 参数指定了元素。
isIntersecting boolean true 值表示目标元素与根元素的视口相交(表示过渡到交叉状态)。false 值表示从交叉到非交叉的过渡。

resize

允许小部件使用 ResizeObserver 对其 DOM 节点的调整大小事件做出响应,并在调整大小发生时提供有关节点新大小的更新信息。使用此中间件是创建对各种视口大小做出响应的应用程序的有效方法。

由于 Resize Observer 仍然是一个新兴的 Web 标准,因此框架会自动确保在浏览器中运行应用程序时提供底层 API,该浏览器尚不支持该 API。

API

import resize from '@dojo/framework/core/middleware/resize';
  • resize.get(key: string | number): DOMRectReadOnly | null
    • 返回小部件指定 DOM 元素的大小信息,该元素由节点的 key 属性标识。如果节点不存在于当前小部件(尚未渲染或指定了无效键),则返回 null。返回的对象是标准的 DOMRectReadOnly 结构。

breakpoint

允许小部件根据其虚拟节点之一的当前宽度确定匹配的特定宽度断点。此中间件在创建可以适应各种显示宽度的窗口小部件时很有用,例如在移动和桌面分辨率下都能正常工作的窗口小部件。

组合 resize 中间件以获取元素宽度,并在调整其宽度时自动使窗口小部件失效。

注意: 如果没有提供自定义宽度断点,Dojo 将默认使用以下集合

  • SM: 0
  • MD: 576
  • LG: 768
  • XL: 960

API

import breakpoint from '@dojo/framework/core/middleware/breakpoint';
interface Breakpoints {
	[index: string]: number;
}
  • breakpoint.get(key: string | number, breakpoints: Breakpoints = defaultBreakpoints)
    • 返回窗口小部件的指定输出节点(由其 key 标识)匹配的断点,该断点基于节点的当前宽度。可以通过 breakpoints 参数提供自定义断点。返回值是一个包含 breakpoint 属性的对象,该属性标识匹配的断点的名称,以及一个 contentRect 属性,该属性包含与调用 resize.get(key) 相同的值。

在许多位置使用相同的断点集时,最好只定义一次断点集,而不是需要将其传递给每个 breakpoint.get() 调用。应用程序可以通过以下方式定义自己的自定义断点中间件,并提供适当的默认值:

src/middleware/myCustomBreakpoint.ts

import { createBreakpointMiddleware } from '@dojo/framework/core/middleware/breakpoint';

const myCustomBreakpoint = createBreakpointMiddleware({ Narrow: 0, Wide: 500 });

export default myCustomBreakpoint;

inert

通过 key 启用在节点上设置 inert 属性。这将确保所讨论的节点不会响应诸如焦点、鼠标事件等操作。对于诸如附加到 document.body 的对话框之类的场景,可以将 inert 反转到 key 节点的所有兄弟节点上。

API

import inert from '@dojo/framework/core/middleware/inert';
  • inert.set(key: string | number, enable: boolean, invert: boolean = false): void;
    • 将节点的惰性属性设置为请求的值。当传递 invert 时,该值将设置在所有节点的兄弟节点上。

src/widgets/Dialog.tsx

import { tsx, create } from '@dojo/framework/core/vdom';
import inert from '@dojo/framework/core/middleware/inert';
import icache from '@dojo/framework/core/middleware/icache';

import * as css from './App.m.css';

const dialogFactory = create({ inert, icache }).properties<{
	open: boolean;
	onRequestClose: () => void;
}>();

const Dialog = dialogFactory(function Dialog({ children, properties, middleware: { inert } }) {
	const { open } = properties();

	inert.set('dialog', open, true);

	if (!open) {
		return null;
	}

	return (
		<body>
			<div
				key="dialog"
				styles={{
					background: 'red',
					width: '400px',
					height: '400px',
					marginLeft: '-200px',
					marginTop: '-200px',
					position: 'absolute',
					left: '50%',
					top: '50%'
				}}
			>
				<button
					onclick={() => {
						properties().onRequestClose();
					}}
				>
					Close
				</button>
				{children()}
			</div>
		</body>
	);
});

const factory = create({ icache });

export default factory(function App({ middleware: { icache } }) {
	return (
		<div classes={[css.root]}>
			<input />
			<button
				onclick={() => {
					icache.set('open', true);
				}}
			>
				Open
			</button>
			<Dialog
				open={icache.getOrSet('open', false)}
				onRequestClose={() => {
					icache.set('open', false);
				}}
			>
				<div>
					<input />
					<input />
					<button>button</button>
					Content
				</div>
			</Dialog>
		</div>
	);
});

store

在使用 Dojo 存储组件时,为窗口小部件提供对其外部化状态的访问权限。

Dojo 存储参考指南 中详细描述。

API

import store from '@dojo/framework/core/middleware/store';
  • store.get<U = any>(path: Path<S, U>): U
    • 从存储中检索指定 path 上的值。当关联的值发生更改时,组合窗口小部件也将失效并重新渲染。
  • store.path(path: any, ...segments: any): StatePaths<S>
    • 返回从指定根开始并包含多个附加段的存储路径。
  • store.at<U = any>(path: Path<S, U[]>, index: number)
    • 返回在访问存储的数组值时包含数字索引的存储路径。
  • store.executor<T extends Process<any, any>>(process: T): ReturnType<T>
    • 在组合窗口小部件的存储中执行给定的 process 并返回结果。

focus

允许窗口小部件在与 VDOM 焦点基元 组合时,检查和控制其生成的 DOM 输出中的焦点。

API

import focus from '@dojo/framework/core/middleware/focus';
  • focus.shouldFocus(): boolean
    • 如果应该在当前渲染周期内指定焦点,则返回 true。仅在第一次返回 true 后,才会从未来的调用中返回 false,直到再次调用 focus.focus() 为止。此函数通常作为 focus 属性传递给特定的 VDOM 节点,允许窗口小部件直接指定应应用焦点的目标。
  • focus.focus()
    • 可以调用此函数来指示窗口小部件或其子级在下一个渲染周期中需要焦点。此函数通常作为 onfocus 事件处理程序传递给输出的 VDOM 节点,允许窗口小部件响应用户驱动的焦点更改事件。
  • focus.isFocused(key: string | number): boolean
    • 如果由指定 key 标识的窗口小部件的 VDOM 节点当前具有焦点,则返回 true。如果相关 VDOM 节点没有焦点或当前窗口小部件不存在,则返回 false

焦点委托示例

以下展示了在窗口小部件层次结构和输出 VNode 之间委托和控制焦点的示例

src/widgets/FocusableWidget.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import focus from '@dojo/framework/core/middleware/focus';
import icache from '@dojo/framework/core/middleware/icache';

/*
	The input's `onfocus()` event handler is assigned to a method passed in
	from a parent widget, via the child's create().properties<MyPropertiesInterface>
	API, allowing user-driven focus changes to propagate back into the application.
*/
const childFactory = create({ focus }).properties<{ onfocus: () => void }>();

const FocusInputChild = childFactory(function FocusInputChild({ middleware: { focus }, properties }) {
	const { onfocus } = properties();
	return <input onfocus={onfocus} focus={focus.shouldFocus} />;
});

const factory = create({ focus, icache });

export default factory(function FocusableWidget({ middleware: { focus, icache } }) {
	const keyWithFocus = icache.get('key-with-focus') || 0;

	const childCount = 5;
	function focusPreviousChild() {
		let newKeyToFocus = (icache.get('key-with-focus') || 0) - 1;
		if (newKeyToFocus < 0) {
			newKeyToFocus = childCount - 1;
		}
		icache.set('key-with-focus', newKeyToFocus);
		focus.focus();
	}
	function focusNextChild() {
		let newKeyToFocus = (icache.get('key-with-focus') || 0) + 1;
		if (newKeyToFocus >= childCount) {
			newKeyToFocus = 0;
		}
		icache.set('key-with-focus', newKeyToFocus);
		focus.focus();
	}
	function focusChild(key: number) {
		icache.set('key-with-focus', key);
		focus.focus();
	}

	return (
		<div>
			<button onclick={focusPreviousChild}>Previous</button>
			<button onclick={focusNextChild}>Next</button>
			<FocusInputChild
				key="0"
				onfocus={() => focusChild(0)}
				focus={keyWithFocus == 0 ? focus.shouldFocus : undefined}
			/>
			<FocusInputChild
				key="1"
				onfocus={() => focusChild(1)}
				focus={keyWithFocus == 1 ? focus.shouldFocus : undefined}
			/>
			<FocusInputChild
				key="2"
				onfocus={() => focusChild(2)}
				focus={keyWithFocus == 2 ? focus.shouldFocus : undefined}
			/>
			<FocusInputChild
				key="3"
				onfocus={() => focusChild(3)}
				focus={keyWithFocus == 3 ? focus.shouldFocus : undefined}
			/>
			<FocusInputChild
				key="4"
				onfocus={() => focusChild(4)}
				focus={keyWithFocus == 4 ? focus.shouldFocus : undefined}
			/>
		</div>
	);
});

validity

允许检索有关节点的 有效性状态 的特定信息,这对于使用浏览器的内置方法验证表单输入并提供基于区域设置的错误消息很有用。

API

import validity from '@dojo/framework/core/middleware/validity';
  • validity.get(key: string, value: string) - 返回 DOM 元素的有效性状态,该元素由节点的 key 属性标识。如果当前窗口小部件不存在指定的 DOM 元素,则返回 { valid: undefined, message: '' }。否则,它将返回一个 ValidityState 对象。

ValidityState 对象包含以下属性

属性 类型 描述
valid boolean 节点的 validity.valid 属性的值,表示节点的值是否满足所有验证约束。
validationMessage string 节点的 validationMessage 属性的值,一个本地化的消息,描述节点值的失败约束。

injector

允许从 Dojo 注册表中检索注入器,并将失效回调函数分配给它们。

注意: 注入器和注册表是高级概念,在编写 Dojo 应用程序时通常不需要它们。它们主要由框架内部使用,以实现更高级的用户界面功能,例如 Dojo 存储

API

import injector from '@dojo/framework/core/middleware/injector';
  • injector.subscribe(label: RegistryLabel, callback: Function = invalidator)
    • 针对指定的注册表 label 注入器(如果存在)订阅给定的 callback 失效函数。如果未指定 callback,则默认使用 invalidator 中间件,以便在注入器提供其数据时,当前窗口小部件将被标记为失效并重新渲染。
  • injector.get<T>(label: RegistryLabel): T | null
    • 检索与给定注册表 label 关联的当前注入器,如果不存在此类注入器,则返回 null

block

允许窗口小部件在构建时在 Node.js 中执行称为的模块。通常用作构建时渲染的一部分。

构建参考指南 中详细描述。

API

import block from '@dojo/framework/core/middleware/block';
  • block<T extends (...args: any[]) => any>(module: T)
    • 执行指定的块模块并返回其结果。