有几种技术可以在路由的model-Hook 中加载多个资源。哪些最适合您的需求,甚至可能在很大程度上取决于您的应用。尤其是使用的后端 API 的功能,如果您的 Ember 应用程序使用 Ember Data 或普通的 fetch/ajax 请求,会产生很大的不同。
最简单的情况是使用 Ember Data 以及遵循 JSON:API specification 并支持 inclusion of related resources 的 REST API:
import Route from '@ember/routing/route';
export default Route.extend({
model({ post_id }) {
return this.store.findRecord('post', post_id, { include: 'comments' });
}
});
如果你使用普通的fetch,你可以使用Promise.all()并行加载多条记录:
import Route from '@ember/routing/route';
export default Route.extend({
async model({ post_id }) {
let [post, comments] = await Promise.all([
fetch(`/posts/${post_id}`),
fetch(`/posts/${post_id}/comments`),
]);
return { post, comments };
}
});
如果您不喜欢 Promise.all() 带有数组破坏的语法,您可能想看看 RSVP.hash()。 rsvp 默认与 ember 捆绑在一起。
如果使用 Ember Data 执行此操作,但您的 API 不支持侧加载,则有点棘手,因为您需要使用查询来加载 cmets。这取决于您的适配器配置,但我想它看起来像这样:
import Route from '@ember/routing/route';
export default Route.extend({
async model({ post_id }) {
let [post, comments] = await Promise.all([
this.store.findRecord('post', post_id),
this.store.query('comment', {
filter: {
post: post_id
}
})
]);
return { post, comments };
}
});
您不能使用多个model-Hooks 并行加载资源。 model-父子路由的Hooks按设计顺序执行。子路由的model-Hook 在其父model-Hook 返回的Promise 解决之前不会被触发。话虽如此,仍然有一些技术可以仅加载所需的数据并缓存在不同子路由之间共享的数据。
让我们以问题中的示例为例,并在 cmets 中对此答案进行更详细的说明:我们的前端应显示一条帖子,其中包括一条路线上的 cmets 和用于在另一条路线上编辑同一帖子的表单。两条路线都需要相同的帖子资源,但只有一条还需要帖子的评论。如果用户从一个路由转换到另一个路由,应用程序不应再次加载帖子,如果用户转换到编辑视图,应用程序不应加载 cmets。
一个天真的尝试是加载post 资源的父路由和两个子路由,一个用于包含 cmets 的视图,一个用于编辑表单。我将这种尝试称为“幼稚”,因为它以三种方式失败:
- post 和 cmets 未并行加载。
- 如果用户在路由之间转换到第三个路由,则不会缓存资源。
- 它将视觉设计与数据加载相结合。
第三点可能令人困惑。事实上,这是对 Ember 中嵌套路由的常见误解。它们不是用于建模数据层次结构,但应该用于在子路由之间共享可视化 UI。重要的不是model-Hook,而是父{{outlet}}中子模板的渲染。
您的所有顾虑都可以通过在客户端缓存资源的服务轻松解决。这是 Ember Data 的主要功能之一。这也是 GraphQL 的 Apollo 客户端最受宣传的功能之一。就这么简单:大多数复杂的前端应用程序都需要一个客户端缓存层来防止资源过度获取。如果您面临该要求,我建议您使用现有解决方案之一。对于简单的用例,您还可以构建自己的服务。基本实现可能如下所示:
import Service from '@ember/service';
export default class StoreService extend Sevice({
postCache = new Map();
loadPost(id) {
let { postCache } = this;
if (postCache.has(id)) {
return postCache.get(id);
}
let request = fetch(`/posts/${id}`);
postCache.set(id, request);
return request;
}
});