Ember同引擎父组件下跨组件调用方法及数据下传可行性咨询
为什么继承组件不可行
直接让SecondComponent继承FirstComponent是错误的做法——Ember组件的生命周期钩子是继承共享的,所以继承后会触发FirstComponent的willDestroyElement(),完全不符合你的需求,这种方式直接排除。
可行方案1:父组件作为通信中间层(符合data-down模式)
你提到的data-down可以实现这个场景,核心是让父组件作为中间桥梁,把调用SecondComponent方法的逻辑封装成动作,再传递给FirstComponent。具体步骤如下:
父模板(application.hbs)修改:
给SecondComponent添加引用传递,同时把封装好的动作传给FirstComponent:{{! 假设SecondComponent在父模板中始终渲染 }} <SecondComponent @passRef={{this.setSecondComponentRef}} /> {{#if this.invokeFirstCompo}} <FirstComponent @triggerSecondAction={{this.callSecondAction}} /> {{/if}}父组件逻辑(application.js):
保存SecondComponent的实例,同时封装调用其方法的动作:import Component from '@glimmer/component'; import { action } from '@ember/object'; export default class ApplicationComponent extends Component { secondComponentInstance = null; @action setSecondComponentRef(component) { this.secondComponentInstance = component; } @action callSecondAction() { this.secondComponentInstance?.actionFromSecondCompo(); } }SecondComponent逻辑调整:
在插入DOM时,把自身实例传给父组件:import Component from '@glimmer/component'; import { action } from '@ember/object'; import { didInsertElement } from '@ember/modifier'; export default class SecondComponent extends Component { @action actionFromSecondCompo() { console.log('SecondComponent的动作已执行'); } @didInsertElement passInstanceToParent() { this.args.passRef?.(this); } }FirstComponent调用动作:
在FirstComponent的模板中直接调用父组件传递来的动作:<button {{on "click" @triggerSecondAction}}>调用SecondComponent的方法</button>
这种方式完全符合Ember的data-down理念——父组件把可执行的动作(数据的一种)向下传递给FirstComponent,FirstComponent只需调用该动作即可,无需关心底层是怎么调用到SecondComponent的。
可行方案2:使用Ember Service共享逻辑
如果组件间通信不需要依赖父组件,或者逻辑需要被多个组件复用,用Service是更灵活的选择:
创建共享Service:
新建app/services/component-shared-actions.js,把SecondComponent的动作逻辑迁移到这里:import Service from '@ember/service'; import { action } from '@ember/object'; export default class ComponentSharedActionsService extends Service { @action actionFromSecondCompo() { console.log('SecondComponent的动作已执行'); } }SecondComponent对接Service:
注入Service,保留原方法作为代理:import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class SecondComponent extends Component { @service('component-shared-actions') sharedActions; @action actionFromSecondCompo() { this.sharedActions.actionFromSecondCompo(); } }FirstComponent调用Service方法:
注入Service后直接调用目标方法:import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; export default class FirstComponent extends Component { @service('component-shared-actions') sharedActions; @action triggerSecondAction() { this.sharedActions.actionFromSecondCompo(); } }
这种方式彻底解耦了两个组件,不管它们在父模板中的渲染状态如何,都能正常调用目标逻辑。
内容的提问来源于stack exchange,提问作者AARTHY

