目錄
依賴注入做了什麼
依賴注入怎麼做?
新增依賴資訊
服務管理
#模組創建
總結
VS Code 原始碼位置:src/vs/platform/instantiation/common
首頁 開發工具 VSCode 簡單聊聊VSCode中依賴注入的原理

簡單聊聊VSCode中依賴注入的原理

Feb 07, 2023 pm 06:18 PM
vscode 前端 visual studio code

本篇文章給大家淺析VSCode中依賴注入的原理,聊聊依賴注入做了什麼?依賴注入怎麼做?希望對大家有幫助!

簡單聊聊VSCode中依賴注入的原理

團隊推行「依賴注入」有一段時間了,但每次使用時都覺得很陌生,有很多概念總是不知所雲:服務id,服務描述符,服務裝飾器等等。

可能是因為不懂得其中原理,使用時都有種「虛」的感覺,最近透過閱讀VS Code 源碼,拜讀團隊大佬的分享文章,力圖理清其中的原理,在這裡做一個簡單的核心邏輯介紹。

依賴注入做了什麼

假設以下情況:

  • 服務模組A,依賴服務B;

  • 服務模組B;

  • 功能模組Feature,依賴服務A 和B;

依照普通的寫法就是:

class B {}

class A {
    constructor() {
        // 在 A 的构造器中 new B
        this.b = new B();
    }
}

class Feature {
    constructor() {
        this.a = new A();
        this.b = new B();
    }
}

// 使用时
const feature = new Feature();

程式碼簡單明了,存在一些問題,例如:如果A 和Feature 所依賴的B 需要是同一個實例,以上的寫法將會初始化兩個B 實例。 【推薦學習:vscode教學程式設計教學

#簡單修改一下:

class A {
    constructor(b: B) {
        this.b = b;
    }
}

class Feature {
    constructor(a, b) {
        this.a = a;
        this.b = b;
    }
}

// 使用时
const b = new B();
const a = new A(b);
const feature = new Feature(a, b);

某個模組初始化時,先在外部將其所依賴的模組創建出來,透過參數的形式傳入功能模組。這樣的寫法就是「依賴注入」。

現在這種寫法的問題在於:手動傳參的形式,必須人工保證 new 的順序,也就是必須取得 a, b 實例才能執行 new Feature。

當依賴關係變得複雜時,創建一個功能模組之前很有可能需要無數個基礎模組,這時候複雜度將會非常高。類似於這種感覺:

簡單聊聊VSCode中依賴注入的原理

想像一種模式:存在一個模組控制器,或者說「服務管理員」來管理這些依賴關係:

class Feature {
    // 声明这个模块依赖 idA, idB
    idA
    idB
}

// 告知「服务管理器」,怎么找对应的模块
services[idA] = A;
services[idB] = B;

// 使用时
const feature = services.createInstance(Feature);

這個services 承載的不就是之前的「手工」過程嗎?
在createInstance(Feature) 時,分析Feature 所依賴的模組:

  • 如果所依賴的模組尚未建立實例,則遞歸創建出該服務實例,最終返回;

  • 如果所依賴的模組已有實例,返回該實例;

  • #找齊後透過參數注入Feature,完成初始化;
    VSCode 實現的正是這麼一套「依賴注入體系」。

依賴注入怎麼做?

要實現這樣一套功能,大致需要:

  • 一個類別如何聲明其依賴的服務id,即給定一個類,外部如何知道他依賴了哪些服務?

  • 如何管理管理服務?

  • 如何建立某個模組?

下文會實作一個最簡單的模型,涵蓋主體流程。

新增依賴資訊

如何給一個 類別 打上烙印,宣告它所依賴的服務呢?
將問題再次抽象化:如何為一個類別加上額外的資訊?
其實,每個類別在es5 下方都是Function,而每個Function 說到底也只是Object ,只要給Object 加上幾個欄位來識別所需的服務id,就可以完成所需的功能。
透過 「參數裝飾器」的寫法,可以很容易做到這一點:

// 参数装饰器 
const decorator = (
    target: Object, // 被装饰的目标,这里为 Feature
    propertyName: string, 
    index: number // 参数的位置索引
) => {
    target['deps'] = [{        index,        id: 'idA',    }];
}
class Feature {
    name = 'feature';
    a: any;
    constructor(
        // 参数装饰器
        @decorator a: any,
    ) {
        this.a = a;
    }
}
console.log('Feature.deps', Feature['deps']);
// [{ id: 'idA', index: 0 }]

透過這種方式,透過 Feature (之後會稱之為 建構器 ctor)就可以取得到 serviceId。

服務管理

使用 Map 來管理,一個 id 對應一個 服務 ctor。

class A {
    name = 'a';
}

// 服务集
class ServiceCollection {
    // 服务集合
    // key 为服务标识
    // value 为 服务ctor
    private entries = new Map<string, any>();

    set(id: string, ctor: any) {
        this.entries.set(id, ctor);   
    }

    get(id: string): any {
        return this.entries.get(id);
    }
}

const services = new ServiceCollection();

// 声明服务 A id 为 idA
services.set(&#39;idA&#39;, A);

示意圖如下:

現在,就可以透過Feature 來找到所依賴的服務的建構器了

// 通过 Feature 找到所依赖的 A
const serviceId = Feature[&#39;deps&#39;][0].id; // idA
console.log(
    &#39;Feature.deps&#39;, 
    services.get(serviceId) // A
);

#模組創建

具體思路為:

  • 如果所依賴的模組尚未建立出實例,則遞歸創建出該服務實例,最終返回;

  • 如果所依賴的模組已有實例,返回該實例;

  • #找齊後透過參數注入Feature,完成初始化;

這裡先上一個簡單的demo,只有一層的依賴(即所依賴的服務沒有依賴其他服務),簡單的講,就是沒有遞歸能力:

class InstantiationService {
    services: ServiceCollection;

    constructor(services: ServiceCollection) {
        this.services = services;
    }

    createInstance(ctor: any) {
        // 1. 获取 ctor 依赖的 服务id
        // 结果为: [&#39;idA&#39;]
        const depIds = ctor[&#39;deps&#39;].map((item: any) => item.id);

        // 2. 获取服务 id 对应的 服务构造器
        // 结果为:[A]
        const depCtors = depIds.map((id: string) => services.get(id));

        // 3. 获取服务实例
        // 结果为: [ A { name: &#39;a&#39;} ]
        const args = depCtors.map((ctor: any) => new ctor());

        // 4. 依赖的服务作为参数注入,实例化所需要模块
        // 结果为:[ Feature { name: &#39;feature&#39;, a }]
        const result = new ctor(...args);

        return result;
    }
}

const instantiation = new InstantiationService(services);

// 使用时
const feature = instantiation.createInstance(Feature);

至此,依賴注入的核心流程實現完畢,要使用Feature 時,只需要呼叫createInstance,不用管他所依賴的服務是否被初始化,instantiation 幫我們做了這個事情。

總結

本文簡單實作一個demo 層級的「依賴注入」模型,簡單實作了:

  • 模組宣告所需要的依賴;

  • 服務管理;

  • 模組建立;

##以此為基礎,可以拓展出一些進階功能:

  • 模組創建(遞歸):VSCode 用了堆疊圖做了這件事,演算法也不複雜;

  • 依賴收集:可用於分析每個模組的依賴,並且可以偵測是否存在「循環依賴」;

  • 模組銷毀:當模組銷毀時,遞歸銷毀他所依賴的服務實例;

  • 延遲初始化:建立依賴的服務時,選擇建立一個proxy ,當真正使用時才真正建立實例;

  • ##異步依賴:當依賴的服務的建立過程是異步的情況下,如何執行創建邏輯;
#原始碼位址

 本文程式碼看這裡。 完整功能
 參考 VSCode 整個依賴注入系統所寫的程式碼,進階可以看這裡。 參考資料

VS Code 原始碼位置:src/vs/platform/instantiation/common

本文借鑒了程式碼思路,且命名也高度一致(手動狗頭


更多關於VSCode的相關知識,請造訪:

vscode教學

!!#

以上是簡單聊聊VSCode中依賴注入的原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

PHP教程
1596
276
如何在Windows上安裝VSCODE 如何在Windows上安裝VSCODE Jul 27, 2025 am 03:16 AM

Gotohttps://code.visualstudio.comanddownloadtheWindowsUserInstaller.2.Runthe.exefile,allowchanges,andselectrecommendedoptionsincludingaddingtoPATHandcreatingadesktopshortcut.3.ClickFinishtolaunchVSCodeafterinstallation.4.Optionallyinstallusefulextens

Vscode和Visual Studio之間有什麼區別 Vscode和Visual Studio之間有什麼區別 Jul 30, 2025 am 02:38 AM

VSCodeisalightweight,cross-platformcodeeditorwithIDE-likefeaturesviaextensions,idealforwebandopen-sourcedevelopment;2.VisualStudioisafull-featured,Windows-onlyIDEdesignedforcomplex.NET,C ,andenterpriseapplications;3.VSCodeperformsfasteronlower-endma

如何在VSCODE中使用參數運行Python腳本 如何在VSCODE中使用參數運行Python腳本 Jul 30, 2025 am 04:11 AM

TorunaPythonscriptwithargumentsinVSCode,configurelaunch.jsonbyopeningtheRunandDebugpanel,creatingoreditingthelaunch.jsonfile,andaddingthedesiredargumentsinthe"args"arraywithintheconfiguration.2.InyourPythonscript,useargparseorsys.argvtoacce

如何將VSCODE與WSL(Linux的Windows子系統)一起使用 如何將VSCODE與WSL(Linux的Windows子系統)一起使用 Aug 01, 2025 am 06:26 AM

InstallWSLandaLinuxdistributionbyrunningwsl--installinPowerShellasAdministrator,thenrestartandsetuptheLinuxdistribution.2.Installthe"Remote-WSL"extensioninVSCodetoenableintegrationwithWSL.3.OpenaprojectinWSLbylaunchingtheWSLterminal,navigat

如何更改VSCODE中的字體大小? 如何更改VSCODE中的字體大小? Aug 02, 2025 am 02:37 AM

TochangethefontsizeinVSCode,useoneofthesemethods:1.OpenSettingsviaCtrl ,(orCmd ,onMac),searchfor"fontsize",andadjustthe"Editor:FontSize"value.2.OpenSettings(JSON)fromtheCommandPalette,thenaddormodify"editor.fontSize":e.g

如何在VSCODE中調試單元測試 如何在VSCODE中調試單元測試 Aug 01, 2025 am 06:12 AM

CreateModifyLaunch.JSONINVSCODEBYOPENEDTHERUNANDDEBUGVIEW,SELECTingYourenVironment(例如Python,Node.js)和ConconfiguringItfo ryourtestframework(例如,pytest,jest)。 2。 setbreakpointsinyourtestfile,selectthedebuggconfiguration,andstartdebuggingwithf5topaus

如何更改VSCODE中的字體大小 如何更改VSCODE中的字體大小 Jul 26, 2025 am 04:13 AM

tochangetheTsizeInvScode,gotofile>“首選項”>“設置”,搜索“ fontsize”,andmodifythe“ editor:fontsize” value.2

如何連接到VSCODE中的SQL數據庫 如何連接到VSCODE中的SQL數據庫 Jul 28, 2025 am 02:58 AM

安裝thesqltoolsextensies andthePpriatedRiverextensionForyourDataBaseTypeinvScode.2.openthecommandPalette,選擇“ sqltools:newConnection”,phessoyourdatabasetype,andEnterConnectionDetabassssssssssssuchashost,port,port,port,port,port,port,port,username,passuce and passwass and passwass anddatabasAbasEname。

See all articles