【React】895- 使用 IOC 解耦 React 組件

IOC(控制反轉)是一種編程思想,可以解耦組件,提高組件複用性。

本文包括兩部分:

  1. 介紹 IOC概念
  2. IOCReact中的應用

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也能看出IOCDI的關係: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]

參考資料

[1]

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

1. JavaScript 重溫系列(22篇全)
2. ECMAScript 重溫系列(10篇全)
3. JavaScript設計模式 重溫系列(9篇全)
4.  正則 / 框架 / 算法等 重溫系列(16篇全)
5.  Webpack4 入門(上) ||  Webpack4 入門(下)
6.  MobX 入門(上)  ||   MobX 入門(下)
7. 100 +篇原創系列彙總

回覆“加羣”與大佬們一起交流學習~

點擊“閱讀原文”查看 100+ 篇原創文章


本文分享自微信公衆號 - 前端自習課(FE-study)。
如有侵權,請聯繫 [email protected] 刪除。
本文參與“OSC源創計劃”,歡迎正在閱讀的你也加入,一起分享。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章