可用中间件
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
- 根据键是否在缓存中设置返回
true
或false
。
- 根据键是否在缓存中设置返回
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
,返回类型可以从值类型推断出来。如果 getOrSet
的 value
是一个函数,则类型将从函数的返回类型推断出来。
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
。
- 返回小部件指定 DOM 元素的尺寸信息,该元素由节点的
返回的 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
属性标识。如果节点不存在于当前小部件(尚未渲染或指定了无效键),则返回一个结果,指示零交叉。
- 返回小部件指定 DOM 元素的交叉信息,该元素由节点的
options
参数允许更详细地控制交叉计算方式。可用字段与 交叉观察器 API 选项 的字段相同。
IntersectionResult
属性
属性 | 类型 | 描述 |
---|---|---|
intersectionRatio |
number |
与根元素视口相交的元素边界框的比例,从 0.0 到 1.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
结构。
- 返回小部件指定 DOM 元素的大小信息,该元素由节点的
breakpoint
允许小部件根据其虚拟节点之一的当前宽度确定匹配的特定宽度断点。此中间件在创建可以适应各种显示宽度的窗口小部件时很有用,例如在移动和桌面分辨率下都能正常工作的窗口小部件。
组合 resize
中间件以获取元素宽度,并在调整其宽度时自动使窗口小部件失效。
注意: 如果没有提供自定义宽度断点,Dojo 将默认使用以下集合
SM
: 0MD
: 576LG
: 768XL
: 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)
- 执行指定的块模块并返回其结果。