-->
依赖注入是前端开发者(包括 Angular 开发者)一道很难迈过去的坎,依赖注入到底是什么?为什么要依赖注入?Angular 的依赖注入怎么有那么多概念,看了官方文档一遍后感觉是懂了,但是过一段时间发现又不懂了,这是前端开发者普遍遇到的问题。
一提起依赖注入,大家就会和控制反转联系在一起,我在看过很多文章之后发现知乎上的一个回答 Spring IoC有什么好处呢? 介绍的特别详细和易懂,大家不了解的可以阅读一下,简单总结一下就是:
以下是一个通过构造函数实现注入的示例代码,那么除了构造函数注入外,还会有 setter 注入和接口注入。
class Logger {
log(message: string) {}
}
class HeroesService {
constructor(logger: Logger) {}
}
const logger = new Logger();
const heroesService = new HeroesService(logger);
通过上述示例发现,HeroesService
不直接创建Logger
类的实例,统一在外层创建后通过构造函数参数传递给 HeroesService
,如果应用的类成千上万,那么实例化类的工作就会变得相当繁琐,会有一大推样板代码,为了管理创建依赖工作,一般会使用 控制反转容器(IoC Container) 进行管理。只需要通过如下一行代码即可实现HeroesService
的创建, IocContainer
会通过HeroesService
的构造函数寻找依赖,找到Logger
并实例化。
const heroesService = IocContainer.get(HeroesService);
如果类很多,依赖层级比较深,那么IocContainer
会帮我们统一管理依赖,IocContainer
其实也叫注入器Injector
, 说的其实就是一回事,Angular 框架中叫Injector
。
关于控制反转和依赖注入更多参考:
Inversion of Control vs Dependency Injection
Wikipedia Dependency Injection
依赖注入的优势:
依赖注入的缺点:
前面我已经说过,只有程序到达一定的复杂度,才会需要各种设计模式和原则等工程化方法提升程序的可维护性,那么 Angular.js 起初是为了解决谷歌内部复杂中大型的前端应用,同时是一批 Java 程序打造的,所以首次在前端中大胆引入了依赖注入,那么 Angular 是基于 Angular.js 打造的新一代前端框架,所以延续了依赖注入特性,并改善了层级注入器,同时采用了更优雅的 TypeScript 装饰器提供 API 形式。
Angular 为了解决数据共享和逻辑复用问题,引入了服务的概念,服务简单理解就是一个带有特定功能的类,Angular 提倡把与视图无关的逻辑抽取到服务中,这样可以让组件类更加精简、高效,组件的工作只管用户体验,把业务逻辑相关功能(比如:从服务器获取数据,验证用户输入或直接往控制台中写日志等)委托给各种服务,最后通过 Angular 的依赖注入,这些带有特定功能的服务类可以被任何组件注入并使用。
Angular 依赖注入: 连接服务的桥梁,在需要的地方(组件/指令/其他服务)通过构造函数注入依赖的服务,依赖注入 + 服务的组合造就了使用 Angular 可以轻松组织和管理复杂应用。
可以说有,也可以说没有,React 为了解决全局数据的共享问题,提供了 Context,创建好 Context 后需要在上层组件通过 <MyContext.Provider value={/* 某个值 */}>
提供依赖值(这个依赖值可以是纯数据对象,也可以是带有方法的对象),然后在任何的子组件中通过<MyContext.Consumer>
进行消费(Vue 中也有类似的provide
和inject
),其实这些都可以狭隘的理解成最简单的依赖注入,只不过 Context 只解决了数据共享的问题,虽然也可以作为逻辑复用,但是官方不推荐,其次就是必须要结合 JSX 才可以使用,React 官方先后推出 Mixin、高阶组件、Render Props 以及最新的 Hooks 用来解决逻辑复用问题。
<MyContext.Consumer>
{value => /* 基于 context 值进行渲染*/}
</MyContext.Consumer>
那么回到 Angular 框架来说,Angular 的服务 + 依赖注入完美解决了数据共享和逻辑复用问题,本质上和 React Hooks 没有太多的区别,只是 API 形态不一样,一个是通过函数形式一个是通过类+依赖注入,因为这两个框架的底层机制和思想不一样,导致了 API 表现形式的不同,但是目的一致,最终都是在解决数据共享和逻辑复用的问题。