Aurelia V1中如何通过DI在父类调用子类init时访问注入模块?
Aurelia V1 父类调用子类抽象方法时依赖未初始化的解决方案
问题根源
报错Cannot read properties of undefined (reading 'tr')的核心原因是JavaScript类继承的执行顺序限制:
- 子类
Child调用super()时,父类Parent的构造函数立即执行并调用this.init(); - 但子类构造函数中,参数属性
private i18n: I18N的赋值操作是在super()执行完成后才会进行; - 因此父类触发
init()时,子类的i18n还未被DI容器注入,处于undefined状态。
方案一:使用@postConstruct()装饰器(推荐的Aurelia式方案)
Aurelia提供的@postConstruct()装饰器会在构造函数执行完成、所有依赖注入完全初始化后触发,完美解决依赖未就绪的问题。只需修改父类,所有子类无需额外改动:
import { postConstruct } from 'aurelia-framework'; export abstract class Parent { protected constructor() { // 移除构造函数内的this.init()调用 } @postConstruct() private triggerInit() { this.init(); } protected abstract init(): void; }
子类代码保持原样即可:
@autoinject() export class Child extends Parent { constructor( private i18n: I18N ) { super(); } protected init() { this.i18n.tr(); // 此时i18n已正常注入,不会报错 } }
方案优势
- 符合Aurelia设计规范:
postConstruct是框架官方推荐的构造后初始化入口,专门处理依赖就绪后的逻辑; - 无侵入性:现有子类无需修改,只需继续实现
init()抽象方法; - 适配工具类场景:只要验证器类通过DI容器实例化(使用
@autoinject),postConstruct就会正常触发,与类的用途无关。
备选方案:延迟初始化到子类构造末尾
如果不想使用生命周期装饰器,可以让父类移除构造内的init()调用,改为子类在构造函数最后手动触发。但此方案需要修改所有子类,优雅度低于方案一:
父类修改:
export abstract class Parent { protected constructor() { // 移除this.init() } protected abstract init(): void; }
子类修改:
@autoinject() export class Child extends Parent { constructor( private i18n: I18N ) { super(); this.init(); // 子类构造最后调用,此时i18n已完成赋值 } protected init() { this.i18n.tr(); } }
不推荐的方案排除
- 父类添加
i18n并通过super传递:会导致父类耦合i18n,且需要修改所有子类的构造参数,扩展性极差,完全没必要; - 使用
Lazy解析器:虽然能延迟获取依赖,但需要在init()中手动调用lazy.get(),增加代码复杂度,不如postConstruct简洁。
内容的提问来源于stack exchange,提问作者DarkMikey
相关产品推荐
相关产品推荐

