背景
在前端项目中,随着项目越来越复杂,模块块之间的高耦合性导致项目越来越难以复用
简介
IoC 的全称叫做 Inversion of Control,可翻译为为「控制反转」或「依赖倒置」,它主要包含了三个准则
- 高层次的模块不应该依赖于低层次的模块,它们都应该依赖于抽象
- 抽象不应该依赖于具体实现,具体实现应该依赖于抽象
- 面向接口编程 而不要面向实现编程
class App {
constructor(options) {
this.options = options;
this.otherA = new OtherA();
this.otherB = new OtherB();
this.init();
}
init() {
window.addEventListener('DOMContentLoaded', () => {
this.otherA.showMsg('otherA');
this.otherB.showMsg('otherB');
});
}
}
class OtherA {
showMsg(msg) {
console.log(msg)
}
}
class OtherB {
showMsg(msg) {
console.log(msg)
}
}
new App({
onReady() {
// do something here...
},
});
从上面代码可以看出,如果在需求多变的情况下,如果子模块 OtherA 有变动需要传参数, App 这个高层次的模块也要改变,对于之前测试通过了的 App 来说,也必须重新测试,造成了高层次的模块依赖于低层次的模块,代码出现了耦合
依赖注入
依赖注入,也就把高层模块所依赖的模块通过传参的方式把依赖「注入」到模块内部,实现松耦合,代码改造一下:
class App {
constructor(options) {
this.options = options;
this.otherA = options.otherA;
this.otherB = options.OtherB;
this.init();
}
init() {
window.addEventListener('DOMContentLoaded', () => {
this.otherA.showMsg('otherA');
this.otherB.showMsg('otherB');
});
}
}
class OtherA {
showMsg(msg) {
console.log(msg)
}
}
class OtherB {
showMsg(msg) {
console.log(msg)
}
}
new App({
otherA: new OtherA(),
otherB: new OtherB(),
onReady() {
// do something here...
},
});
但是这样改造还是会有一些问题,如果再增加一个模块otherC的情况下,还是需要修改高层次的模块 App,需要进一步抽象 App 模块,代码改动如下:
constructor(options) {
this.options = options;
this.otherA = options.otherA;
this.otherB = options.OtherB;
this.onReady = options.onReady;
this.init();
}
static modules = []
init() {
window.addEventListener('DOMContentLoaded', () => {
this.registerModules();
this.onReady();
});
}
static use(module) {
Array.isArray(module) ? module.map(item => App.use(item)) : App.modules.push(module);
}
registerModules() {
App.modules.map(module => module.showMsg && typeof module.showMsg == 'function' && module.showMsg(
this));
}
}
class OtherA {
showMsg(app) {
console.log('OtherA')
}
}
class OtherB {
showMsg(app) {
console.log('OtherB')
}
}
class OtherC {
showMsg(app) {
console.log('OtherC')
}
}
App.use([new OtherA(), new OtherB(), new OtherC()]);
new App({
onReady() {
// do something here...
},
});
从上面代码可以看出, App.use 把所有的模块都存在 App 中的 static modules = [] 中, 在 App 模块加载之后,通过 registerModules 函数取出 modules 中的模块,依次执行各个低层次的子模块,依次调用他们的方法执行
概括
App 模块此时应该称之为「容器」比较合适了,跟业务已经没有任何关系了,它仅仅只是提供了一些方法来辅助管理注入的依赖和控制模块如何执行
App 作为一个核心模块,负责控制各个子模块,不实现具体业务,面向抽象编程