dojo 龙形主logo

Dojo 资源

Dojo 资源旨在提供一致的模式,使小部件“资源感知”。资源可以配置为与任何类型的数据源一起使用。资源本质上是一个 Dojo 管理的数据存储,具有内置的缓存、分页、过滤和自定义 API。结合 resource 中间件,资源允许小部件一致地、与源无关地访问数据,而无需小部件了解获取实现或原始数据格式。

功能 描述
支持任何类型的数据源 资源可以使用预加载数据或来自外部源的数据作为后端。
单个数据源 资源允许为给定模板创建一个单个数据源,该数据源可以使用数据中间件在多个小部件之间共享。
支持异步和同步数据读取 资源模板可以以任何方式读取数据 - 一旦数据可用,资源中间件就会对任何受影响的小部件进行响应式失效。
数据转换 允许指定小部件所需的数据格式,并透明地将源数据转换为小部件消费的预期输出格式
一致的资源选项 资源选项对象被传递给旨在读取数据的 API。
可共享的资源选项 资源选项可以通过资源中间件在小部件之间共享,允许多个小部件对资源更改(如页面更改)做出反应

基本用法

为了使用 Dojo 资源,小部件需要使用 resource 中间件,该中间件使用 @dojo/framework/middleware/resources 中的 createResourceMiddleware 工厂创建。有两种类型的“资源感知”小部件:在属性 API 上公开 resource 的小部件和小部件需要在内部使用资源。相同的工厂用于创建这两种类型的中间件,但主要区别在于,对于需要通过属性传递资源的小部件,在创建时需要资源类型。

interface ResourceData {
	id: string;
	name: string;
}

// Add `resource` to the widgets API
const resource = createResourceMiddleware<ResourceData>();

// For using resources internally only, no property is added to the
// widget property API
const resource = createResourceMiddleware();

使用 resource 中间件可以让你在小部件中使用资源模板。资源模板使用 @dojo/framework/middleware/resources 中的 createResourceTemplate 工厂创建。如果未将自定义模板传递给 createResourceTemplate 工厂,则将使用默认资源模板。

要创建一个默认模板,需要一个泛型来定义资源数据的类型和资源数据的 idKey,这意味着资源将视为数据的唯一 ID 的属性。

interface ResourceData {
	id: string;
	name: string;
}

const template = createResourceTemplate<ResourceData>('id');

默认模板需要使用 id 初始化以标识资源实例和资源的数据数组,这可以在使用已加载的数据集时使用。需要初始化的模板需要被调用以创建可用于资源的“已加盖印章”模板。

template({ id: 'id', data: [{ id: '1', name: 'First' }] });

对于基本用法场景,传递模板不需要在小部件内部导入和使用 resource 中间件。如果资源数据接口和小部件所需的数据接口匹配,并且不需要自定义选项,则可以将模板直接传递给 resource 属性。如果需要默认模板,则不需要模板,因为 Dojo 资源将自动使用传递给资源的对象中的 iddataidKey 创建默认模板。

App.tsx

import { create, tsx } from '@dojo/framework/core/vdom';
import { createResourceTemplate } from '@dojo/framework/core/middleware/resources';
import ResourceAwareWidget from './ResourceAwareWidget';

interface ResourceData {
	id: string;
	name: string;
}

const myTemplate = createResourceTemplate<{ id: string }>();
const factory = create();

const App = factory(function App({ id }) {
	return (
		<div>
			<ResourceAwareWidget resource={template({ id, data })} />
			<ResourceAwareWidget resource={{ id, data, idKey: 'id' }} />
		</div>
});

自定义模板的数据源

Dojo 资源可以配置为用户定义的数据源,例如 RESTful API。这可以通过使用 createResourceTemplate 工厂创建具有 read API 自定义实现的资源模板来完成。read 函数接收包含详细信息的请求,包括偏移量、页面大小和一组控件,其中包括用于“设置”读取响应的 put

userResourceTemplate.tsx

import { createResourceTemplate } from '@dojo/framework/core/middleware/resources';

interface RemoteResourceData {
	id: string;
	name: string;
}

export default createResourceTemplate<RemoteResourceData>({
	idKey: 'id',
	read: async (request: ResourceReadRequest, controls: ResourceControls) => {
		// The template is injected with read request, offset, size and query
		const { offset, size } = request;
		// The request details are used to determine the data to fetch
		const response = await fetch(`https://my.user.endpount.com?offset=${offset}&size=${size}`);
		const data = await response.json();
		// The template needs to set the response using the resource controls put function
		// along with the original request
		controls.put({ data: data.data, total: data.total }, request);
	}
});

在小部件中访问数据

“资源感知”小部件需要使用 resource 中间件,该中间件提供了一个 API 来使用资源模板。resource 中间件需要使用 @dojo/framework/core/middleware/resources 中的 createResourceMiddleware 工厂创建,并传递一个接口,该接口定义了小部件的预期 resource 数据结构。

在小部件中访问数据是使用 get 函数从返回值中执行的,方法是将模板传递给 resource.template 工厂。get 函数需要标准选项 offsetsizequery。Dojo 资源提供了一种标准机制 createOptions(来自资源中间件),用于创建和管理选项,包括确保在“共享”选项更新时使小部件失效。createOptions 工厂接受一个函数,该函数在选项更新时被调用,允许选项的所有者控制更改,并将当前选项和下一个选项传递给 setter。

const { createOptions } = resource;

// by default, the setter will normally need to merge the next
// options over the current options
const options = createOptions((curr, next) => {
	return { ...curr, ...next };
});

// however if certain queries are always required for the resource
// such as an "id' that can be always set
const options = createOptions((curr, next) => {
	return { ...curr, ...next, query: { ...next.query, id: 'my-id' } };
});

默认情况下,get 函数不会尝试从模板读取,只会尝试使用现有数据来满足请求。为了确保 Dojo 资源在无法满足请求时尝试读取数据,需要将“read”函数传递给 get 函数。

const {
	createOptions,
	get,
	template: { read }
} = resource.template(myTemplate);

const options = createOptions((curr, next) => ({ ...curr, ...next }));
const data = get(options(), { read });

如果资源模板上的 read 函数是异步的,则在获取数据时结果可能是 undefined,并且当数据可用时,小部件将重新渲染。

ResourceAwareWidget.tsx

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

interface ResourceData {
	value: string;
}

const resource = createResourceMiddleware<ResourceData>('value');

const factory = create({ resource });

export const DataAwareWidget = factory(function DataAwareWidget({ id, properties, middleware: { resource } }) {
	const {
		resource: { template, options = resource.createOptions(id) }
	} = properties();
	const {
		get,
		template: { read }
	} = resource.template(template);

	const items = get(options(), { read });
	if (items) {
		return <ul>{items.map((item) => <li>{item.value}</li>)}</ul>;
	}
	return <div>Loading...</div>;
});