咨询Ember路由中Model随Service值变化自动刷新的实现方案
没问题,这种需求完全可以实现,而且非常符合Ember的最佳实践——毕竟Service本来就是用来共享跨路由/组件的状态和逻辑的嘛!结合你给出的current-contact.js代码片段,我给你拆解一下具体实现步骤和最佳实践:
1. 先让Service的状态变得“可追踪”
你的Service目前只有一个list()方法,但要让Route能感知到它的变化,得把需要监听的状态(比如合同列表、选中的联系人)存成可追踪的属性,而不是只在方法里临时处理。用Ember Octane及以上版本的话,推荐用@tracked装饰器,比旧的观察者高效多了:
// services/current-contact.js import Service from '@ember/service'; import { inject as service } from '@ember/service'; import { tracked } from '@glimmer/tracking'; export default class CurrentContactService extends Service { @service session; @service store; @tracked currentContrats = []; // 用tracked存要监听的合同列表 async list() { if (this.session.isAuthenticated) { const contrats = await this.store.findAll('contrat'); this.currentContrats = contrats; // 更新tracked属性,触发后续的响应 // 这里可以加你的其他逻辑,比如筛选特定联系人之类的 } else { this.currentContrats = []; // 未登录时清空列表 } } // 举个例子:如果有切换选中联系人的需求,也用tracked @tracked selectedContact = null; updateSelectedContact(contact) { this.selectedContact = contact; } }
2. 在Route中关联Service状态,实现自动刷新
有两种常用的方式,根据你的需求选:
方式一:响应式返回Model(无需手动调用refresh)
如果只是需要模板展示的数据随Service变化,不需要重新调用store获取数据,那直接在model()里返回依赖Service追踪属性的对象就行,Ember会自动帮你更新:
// routes/your-target-route.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class YourTargetRoute extends Route { @service currentContact; async model() { // 先初始化一次数据 await this.currentContact.list(); // 返回一个关联Service属性的对象,当currentContrats变化时,Model自动更新 return { get contrats() { return this.currentContact.currentContrats; }, get selectedContact() { return this.currentContact.selectedContact; } }; } }
模板里直接用{{model.contrats}}或者{{model.selectedContact}},数据变了模板自动更新,非常省心。
方式二:监听Service变化,手动刷新Model
如果需要在Service状态变化时,重新调用store获取最新数据(比如后端数据更新了),那可以监听Service的属性变化,调用this.refresh():
// routes/your-target-route.js import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; export default class YourTargetRoute extends Route { @service currentContact; async model() { // 每次refresh都会重新执行这里的逻辑 await this.currentContact.list(); return this.currentContact.currentContrats; } activate() { super.activate(...arguments); // 监听Service的currentContrats变化,触发refresh this.currentContact.addObserver('currentContrats', this, this.refresh); } willDestroy() { super.willDestroy(...arguments); // 记得移除观察者,避免内存泄漏! this.currentContact.removeObserver('currentContrats', this, this.refresh); } }
或者更灵活的方式:让Service触发自定义事件,Route监听事件再刷新。比如在Service的list()方法里加this.trigger('contratsUpdated'),然后Route里用this.currentContact.on('contratsUpdated', this, this.refresh),这样可以更精准地控制什么时候刷新。
3. 聊聊最佳实践
- 优先用Tracked,少用观察者:Tracked是Ember现在主推的状态管理方式,比观察者更高效,也不容易出内存泄漏的问题。
- 职责分离:Service只管状态和数据逻辑,Route只管获取Model和路由层面的控制,组件只管渲染——别把逻辑混在一起。
- 能不刷新Route就别刷新:如果只是模板需要更新,直接在组件里注入Service用Tracked属性就行,不需要刷新整个Route的Model,这样性能更好。比如组件里直接写:
// components/your-component.js import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; export default class YourComponent extends Component { @service currentContact; get contrats() { return this.currentContact.currentContrats; } }
模板里用{{this.contrats}},数据变了自动更新,比刷新Route轻量多了。
总结
完全可以通过Service实现Model随状态变化自动刷新,这不仅可行,还是Ember里处理跨路由共享状态的标准做法。核心就是让Service的状态可追踪,然后在Route或组件里关联这个状态就行。
内容的提问来源于stack exchange,提问作者El Poisen

