成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

精讀《Inject Instance 源碼》

hsluoyz / 1600人閱讀

摘要:引言本周精讀的源碼是這個庫。這個庫的目的是為了實現(xiàn)的依賴注入。精讀那么開始源碼的解析,首先是整體思路的分析。討論地址是精讀源碼如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀幫你篩選靠譜的內(nèi)容。

1. 引言

本周精讀的源碼是 inject-instance 這個庫。

這個庫的目的是為了實現(xiàn) Class 的依賴注入。

比如我們通過 inject 描述一個成員變量,那么在運行時,這個成員變量的值就會被替換成對應(yīng) Class 的實例。這等于讓 Class 具備了申明依賴注入的能力:

import {inject} from "inject-instance"
import B from "./B"

class A {
  @inject("B") private b: B
  public name = "aaa"

  say() {
    console.log("A inject B instance", this.b.name)
  }
}

試想一下,如果成員函數(shù) b 是通過 New 出來的:

class A {
  private b = new B()

  say() {
    console.log("A inject B instance", this.b.name)
  }
}

這個 b 就不具備依賴注入的特點,因為被注入的 b 是外部已經(jīng)初始化好的,而不是實例化 A 時動態(tài)生成的。

需要依賴注入的一般都是框架級代碼,比如定義數(shù)據(jù)流,存在三個 Store 類,他們之間需要相互調(diào)用對方實例:

class A {
  @inject("B") private b: B
}

class B {
  @inject("C") private c: C
}

class C {
  @inject("A") private a: A
}

那么對于引用了數(shù)據(jù)流 A、B、C 的三個組件,要保證它們訪問到的是同一組實例 A B C 該怎么辦呢?

這時候我們需要通過 injectInstance 函數(shù)統(tǒng)一實例化這些類,保證拿到的實例中,成員變量都是屬于同一份實例:

import injectInstance from "inject-instance"

const instances = injectInstance(A, B, C)
instances.get("A")
instances.get("B")
instances.get("C")

那么框架底層可以通過調(diào)用 injectInstance 方式初始化一組 “正確注入依賴關(guān)系的實例”,拿 React 舉例,這個動作可以發(fā)生在自定義數(shù)據(jù)流的 Provider 函數(shù)里:


  

那么在 Provider 函數(shù)內(nèi)部通過 injectInstance 實例化的數(shù)據(jù)流,可以保證 A B C 操作的注入實例都是當前 Provider 實例中的那一份。

2. 精讀

那么開始源碼的解析,首先是整體思路的分析。

我們需要準備兩個 API: injectinjectInstance。

inject 用來描述要注入的類名,值是與 Class 名相同的字符串,injectInstance 是生成一系列實例的入口函數(shù),需要生成最終生效的實例,并放在一個 Map 中。

inject

inject 是個裝飾器,它的目的有兩個:

修改 Class 基類信息,使其實例化的實例能拿到對應(yīng)字段注入的 Class 名稱。

增加一個字段描述注入了那些 Key。

const inject = (injectName: string): any => (target: any, propertyKey: string, descriptor: PropertyDescriptor): any => {
    target[propertyKey] = injectName

    // 加入一個標注變量
    if (!target["_injectDecorator__injectVariables"]) {
        target["_injectDecorator__injectVariables"] = [propertyKey]
    } else {
        target["_injectDecorator__injectVariables"].push(propertyKey)
    }

    return descriptor
}

target[propertyKey] = injectName 這行代碼中,propertyKey 是申明了注入的成員變量名稱,比如 Class A 中,propertyKey 等于 b,而 injectName 表示這個值需要的對應(yīng)實例的 Class 名,比如 Class A 中,injectName 等于 B。

_injectDecorator__injectVariables 是個數(shù)組,為 Class 描述了這個類參與注入的 key 共有哪些,這樣可以在后面 injectInstance 函數(shù)中拿到并依次賦值。

injectInstance

這個函數(shù)有兩個目的:

生成對應(yīng)的實例。

將實例中注入部分的成員變量替換成對應(yīng)實例。

代碼不長,直接貼出來:

const injectInstance = (...classes: Array) => {
    const classMap = new Map()
    const instanceMap = new Map()

    classes.forEach(eachClass => {
      if (classMap.has(eachClass.name)) {
        throw `duplicate className: ${eachClass.name}`
      }
      classMap.set(eachClass.name, eachClass)
    })

    // 遍歷所有用到的類
    classMap.forEach((eachClass: any) => {
      // 實例化
      instanceMap.set(eachClass.name, new eachClass())
    })

    // 遍歷所有實例
  instanceMap.forEach((eachInstance: any, key: string) => {
    // 遍歷這個類的注入實例類名
    if (eachInstance["_injectDecorator__injectVariables"]) {
      eachInstance["_injectDecorator__injectVariables"].forEach((injectVariableKey: string) => {
        const className = eachInstance.__proto__[injectVariableKey];
        if (!instanceMap.get(className)) {
          throw Error(`injectName: ${className} not found!`);
        }

        // 把注入名改成實際注入對象
        eachInstance[injectVariableKey] = instanceMap.get(className);
      });
    }

    // 刪除這個臨時變量
    delete eachInstance["_injectDecorator__injectVariables"];
  });

  return instanceMap
}

可以看到,首先我們將傳入的 Class 依次初始化:

// 遍歷所有用到的類
classMap.forEach((eachClass: any) => {
  // 實例化
  instanceMap.set(eachClass.name, new eachClass())
})

這是必須提前完成的,因為注入可能存在循環(huán)依賴,我們必須在解析注入之前就生成 Class 實例,此時需要注入的字段都是 undefined

第二步就是將這些注入字段的 undefined 替換為剛才實例化 Map instanceMap 中對應(yīng)的實例了。

我們通過 __proto__ 拿到 Class 基類在 inject 函數(shù)中埋下的 injectName,配合 _injectDecorator__injectVariables 拿到 key 后,直接遍歷所有要替換的 key, 通過類名從 instanceMap 中提取即可。

__proto__ 僅限框架代碼中使用,業(yè)務(wù)代碼不要這么用,造成額外理解成本。

所以總結(jié)一下,就是提前實例化 + 根據(jù) inject 埋好的信息依次替換注入的成員變量為剛才實例化好的實例。

3. 總結(jié)

希望讀完這篇文章,你能理解依賴注入的使用場景,使用方式,以及一種實現(xiàn)思路。

框架實現(xiàn)依賴注入都是提前收集所有類,統(tǒng)一初始化,通過注入函數(shù)打標后全局替換,這是一種思維套路。

如果有其他更有意思的依賴注入實現(xiàn)方案,歡迎討論。

討論地址是:精讀《Inject Instance 源碼》 · Issue #176 · dt-fe/weekly

如果你想?yún)⑴c討論,請 點擊這里,每周都有新的主題,周末或周一發(fā)布。前端精讀 - 幫你篩選靠譜的內(nèi)容。

關(guān)注 前端精讀微信公眾號

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/105421.html

相關(guān)文章

  • 精讀源碼學習》

    摘要:精讀原文介紹了學習源碼的兩個技巧,并利用實例說明了源碼學習過程中可以學到許多周邊知識,都讓我們受益匪淺。討論地址是精讀源碼學習如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1. 引言 javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是: 精讀《Immer.js》源...

    aboutU 評論0 收藏0
  • [源碼閱讀]純粹極簡的react狀態(tài)管理組件unstated

    摘要:此處繼承了上面的可以注入現(xiàn)成的狀態(tài)管理實例,添加到之中。返回值寫成的意義簡單一句話概括,這么寫可以避免改變導(dǎo)致子組件的重復(fù)渲染。就是創(chuàng)建狀態(tài)管理組件時默認傳遞的監(jiān)聽函數(shù),用的是的更新一個空對象。返回值寫成的意義。 簡介 unstated是一個極簡的狀態(tài)管理組件 看它的簡介:State so simple, it goes without saying 對比 對比redux: 更加靈活...

    FrancisSoung 評論0 收藏0
  • 理解 React 輕量狀態(tài)管理庫 Unstated

    摘要:返回,用來包裹頂層組件,向應(yīng)用中注入狀態(tài)管理實例,可做數(shù)據(jù)的初始化。方法返回創(chuàng)建的狀態(tài)管理實例,作為參數(shù)傳遞給調(diào)用的函數(shù),函數(shù)拿到實例,操作或顯示數(shù)據(jù)。用來實現(xiàn)一個狀態(tài)管理類。為中的狀態(tài)管理實例數(shù)據(jù)。 個人網(wǎng)站: https://www.neroht.com 在React寫應(yīng)用的時候,難免遇到跨組件通信的問題?,F(xiàn)在已經(jīng)有很多的解決方案。 React本身的Context Redux結(jié)合...

    Profeel 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<