2026-03-20 01:40:48
在 Ember.js 中,Extend 和 Mixin 是两种不同的代码复用方式,选择哪种方式取决于是否需要共享状态(shared states)。以下是关键区别和最佳实践:
核心区别Mixin
适用场景:需要共享状态(shared states)时使用。
特点:
将方法或属性添加到类的原型(prototype)上,所有使用该 Mixin 的实例会共享这些状态。
可能导致状态污染(如多个组件混入同一 Mixin 时,共享的属性可能被意外修改)。
示例:export default Ember.Mixin.create({ sharedProperty: 'value', // 所有混入此 Mixin 的实例共享该属性 sharedMethod() { /* ... */ }});
Extend
适用场景:不共享状态时使用(通过继承扩展类)。
特点:
创建子类,每个实例拥有独立的状态。
适合定义基础类,再通过继承扩展功能。
示例:export default Ember.Component.extend({ // 独立的状态,不与其他实例共享 localProperty: 'value'});
如果不想共享状态(即不希望属性写入原型),可以通过以下方式实现:
使用 init 初始化实例属性在 Mixin 的 init 方法中通过 setProperties 为每个实例设置独立属性:
export default Ember.Mixin.create({ init() { this._super(...arguments); this.setProperties({ instanceProperty: 'uniqueValue' // 每个实例独立拥有 }); }});使用计算属性(Computed Property)计算属性基于依赖动态计算值,天然隔离状态:
export default Ember.Mixin.create({ instanceProperty: Ember.computed('dependency', function() { return this.get('dependency') + '-computed'; })});当多个 Mixin 被混入同一个类时,它们的初始化顺序遵循以下规则:
父类的 init 最先执行(通过 this._super(...arguments) 调用)。
Mixin 的 init 按混入顺序依次执行(先混入的先执行)。
最终执行类自身的 init。
示例:
const MixinA = Ember.Mixin.create({ init() { this._super(...arguments); console.log('MixinA'); } });const MixinB = Ember.Mixin.create({ init() { this._super(...arguments); console.log('MixinB'); } });export default Ember.Component.extend(MixinA, MixinB, { init() { this._super(...arguments); console.log('Component'); }});// 输出顺序:MixinA → MixinB → Component最佳实践建议避免在 Mixin 中定义可变状态(如数组、对象),除非明确需要共享。
通过 init 或计算属性实现实例隔离。
通过合理选择和设计,可以平衡代码复用与状态隔离的需求。