如何配置Ember Data实现异步hasMany关联记录单请求批量获取?
嘿,这个问题我太熟了!异步hasMany默认逐个发起请求加载关联记录,确实会在评论多的时候把请求数拉满,非常影响性能。别担心,Ember Data有几种靠谱的方式能帮你用单个请求批量搞定,我给你一步步拆解:
方法1:利用JSON API的include参数(推荐)
如果你的后端遵循JSON API规范,这是最省心的方案。只需要在请求父模型(比如Post)时,带上include参数指定要一起加载的关联记录,后端就会在同一个响应里返回Post和它的所有Comments,Ember Data会自动把这些评论缓存起来,之后访问post.comments就不会再发请求了。
代码示例
在路由的model钩子或者任何需要加载Post的地方,加上include选项:
// routes/post.js import Route from '@ember/routing/route'; export default class PostRoute extends Route { async model(params) { // 一次性加载Post和关联的Comments return this.store.findRecord('post', params.post_id, { include: 'comments' }); } }
注意:你的后端需要支持JSON API的include参数,返回的响应里要包含data(Post数据)和included(所有关联的Comments数组)。
方法2:后端返回时侧载(Sideload)关联数据
如果后端用的是REST风格而非JSON API,你可以让后端在返回Post数据的同时,把关联的Comments一起放在响应里。比如后端返回的JSON格式大概是这样:
{ "post": { "id": "1", "comment_ids": ["1", "2", "3"] }, "comments": [ {"id": "1", "body": "第一条评论"}, {"id": "2", "body": "第二条评论"}, {"id": "3", "body": "第三条评论"} ] }
这种情况下,Ember Data的RESTSerializer会自动识别并缓存这些Comments,之后访问post.comments时直接用缓存,不会发单个请求。你只需要确保模型里的关联定义正确:
// models/post.js import Model, { hasMany } from '@ember-data/model'; export default class PostModel extends Model { @hasMany('comment', { async: true }) comments; }
(对了,你的评论模型文件名应该是comment.js而不是comments.js哦,Ember Data默认按单数模型名识别,这个小细节别搞错!)
方法3:手动批量查询关联记录
如果后端暂时没法支持上面两种方案,你可以手动获取所有关联记录的ID,然后用store.query()一次性请求。这样也能避免逐个发请求的问题。
代码示例
在路由里先加载Post,拿到所有评论ID后批量请求:
// routes/post.js import Route from '@ember/routing/route'; export default class PostRoute extends Route { async model(params) { const post = await this.store.findRecord('post', params.post_id); // 获取Post关联的所有评论ID const commentIds = post.hasMany('comments').ids(); if (commentIds.length > 0) { // 批量请求所有评论,后端需要支持按ID列表查询 await this.store.query('comment', { ids: commentIds }); } return post; } }
这样处理后,当你在模板或控制器里访问post.comments时,Ember Data已经从缓存里拿到了所有评论,不会再发起单个请求了。
小提醒
不管用哪种方法,都要确保你的关联定义是正确的:Post模型里的hasMany指向comment(单数),Comment模型里如果有反向关联,要写belongsTo('post')。
内容的提问来源于stack exchange,提问作者Willem de Wit

