IOC
(控制反轉)是一種編程思想,可以解耦組件,提高組件複用性。
本文包括兩部分:
-
介紹 IOC
概念 -
IOC
在React
中的應用
IOC是什麼
讓我們來看個例子:
我們有個士兵的類,在類內部會實例化一種武器:
class Soldier {
constructor() {
// 這裏我們實例化一把步槍
this.weapon = new Rifle();
}
attack() {
this.weapon.attack();
}
}
士兵的武器應該是多種多樣的,但是在Soldier
類內部依賴了Rifle
。
所以當我們想將武器從步槍換爲手榴彈時,只能這樣改寫:
// ...
constructor() {
// 這裏我們實例化一把步槍
// this.weapon = new Rifle();
// 這裏我們實例化一個手榴彈
this.weapon = new Grenade();
}
// ...
理想的狀態是:士兵不依賴具體的武器,彈藥庫裏有什麼武器,就用什麼武器。
在這種情況下,IOC
作爲彈藥庫,就派上了用場。
讓我們來改寫代碼:
第一步:DI(Dependency Injection)
改寫的第一步是使士兵不依賴具體的武器,而是將武器作爲依賴注入給士兵:
class Soldier {
// 將武器作爲依賴注入
constructor(weapon) {
this.weapon = weapon;
}
attack() {
this.weapon.attack();
}
}
我們將武器的實例作爲Soldier
的參數傳入,於是可以如下調用:
const s1 = new Soldier(new Rifle());
const s2 = new Soldier(new Grenade());
這一步被稱爲DI
(依賴注入)。
第二步:IOC容器
那麼武器從哪兒來呢?接下來來打造我們的武器庫:
class Armory {
constructor() {
this.weapon = null;
}
setWeapon(weapon) {
this.weapon = weapon;
}
getWeapon() {
return this.weapon;
}
}
武器庫支持存武器(setWeapon
)和取武器(getWeapon
)。
現在,士兵不依賴具體武器,只需要去武器庫取武器:
const armory1 = new Armory();
class Soldier {
// 將武器作爲依賴注入
constructor(armory) {
this.weapon = armory.getWeapon();
}
attack() {
this.weapon.attack();
}
}
改造前的依賴關係:
士兵 --> 武器
改造前原先應用(士兵)擁有依賴的完全控制權。
改造後的依賴關係:
士兵 --> 武器庫 <-- 武器
改造後應用(士兵)與服務提供方(武器)解耦,他們通過IOC
容器(武器庫)聯繫。
從Demo
也能看出IOC
與DI
的關係:DI
是實現IOC
編程思想的一種方式。
除了DI
外,另一種實現方式是Dependency Lookup
(依賴查找),簡稱DL
。
IOC與React
在React
中,爲組件傳遞的props
就是一種DI
實現。
爲了跨層級傳遞數據,我們常使用Context API
:
function Name() {
const {name} = useContext(nameContext);
reutrn <h1>{name}</h1>;
}
context
將依賴提供方(name
)與依賴使用方(Name
)隔離,可以看作是一種IOC
實現。
所以說,合理使用React
可以充分利用IOC
的思想解耦代碼邏輯。
接下來我們看看專業的DI
庫如何與React
結合:
InversifyJS
InversifyJS[1]是一個強大、輕量的DI
庫。
首先我們實現依賴(武器的實現):
// armory.ts
import { injectable } from "inversify";
export interface IArmory<T> {
attack(): T;
}
@injectable()
export class Armory implements IArmory<string> {
attack() {
return "Rifle biubiubiu~";
}
}
通過inversify
提供的injectable decorator
標記該class
是可被注入的。
接下來實現需求方(士兵的實現):
import React from "react";
import { IArmory } from "./armory";
export class Soldier extends React.Component {
private readonly Armory: IArmory<string>;
render() {
return <h1 onClick={() => this.armory.attack()}>I am a soldier</h1>;
}
}
最後實例化IOC
容器,連接需求方與依賴:
import { Container } from "inversify";
import { IArmory, Armory } from "./armory";
// 實例化IOC容器
export const container = new Container();
// 將依賴方注入容器,其中armory爲該依賴的ID
container.bind<IArmory<string>>("armory").to(Armory);
至此,完成一個React
組件的簡單IOC
。
業務邏輯的更多依賴都可以通過注入IOC容器來實現解耦。
Hooks
同樣可以通過inversify
完成IOC
,參考Dependency injection in React using InversifyJS. Now with React Hooks[2]
參考資料
InversifyJS: https://github.com/inversify/InversifyJS
[2]Dependency injection in React using InversifyJS. Now with React Hooks: https://itnext.io/dependency-injection-in-react-using-inversifyjs-now-with-react-hooks-64f7f077cde6
回覆“加羣”與大佬們一起交流學習~
點擊“閱讀原文”查看 100+ 篇原創文章
本文分享自微信公衆號 - 前端自習課(FE-study)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。