轉載至:https://blog.csdn.net/tkokof1/article/details/108984865
平時編寫 TypeScript 代碼時,一般都傾向於使用模塊(Module),通過結合使用 import 和 export 我們便可以方便的進行模塊的導入和導出.
舉個簡單的例子,假設我們有以下的 TypeScript 代碼文件(A.ts):
export class A { // methods here }
可以看到,上述代碼使用 export 導出了類型 A,如果我們需要在另外的 TypeScript 代碼文件(B.ts)中使用類型 A,我們可以直接使用 import :
import { A } from "./A.ts" class B { // use A here }
接着我們讓代碼變的復雜一些,假設現在類型 A 也要使用類型 B 了,那么相關的代碼可能會變成這樣:
import { B } from "./B.ts" export class A { // use B here } import { A } from "./A.ts" export class B { // use A here }
此時,類型 A 與 類型 B 便產生了循環引用,一般來講是應該盡量避免的,但是在較大型的項目中往往又很難規避,所以我們需要一種可以處理循環引用問題的方法(之前關於這個話題自己也寫過一篇博文),而實際上,TypeScript 中的 import 和 export 是可以處理循環引用的:
當 import 遇到導入完畢或者說正在導入的模塊(文件)時,是直接返回導入結果的(盡管這個結果可能是不完整的),而不是遞歸的進行模塊的導入操作,還是拿上面的代碼舉例,假設我們首先導入 A 模塊:
A 模塊嘗試導入 B 模塊
由於 B 模塊尚未導入,程序開始導入 B 模塊
B 模塊嘗試導入 A 模塊
由於 A 模塊正在導入,所以程序直接返回當前導入結果(盡管當前結果是不完整的)
將類型 B 加入到 B 模塊的導出數據中(export class B)
B 模塊導入完成,繼續 A 模塊的導入
將類型 A 加入到 A 模塊的導出數據中(export class A)
A 模塊導入完成
值得注意的是,上述的這種循環引用處理方式是不完備的,該方式並不能正確處理更復雜一些的循環引用情況(主要是在一些需要及時訪問模塊導出數據的情況下,譬如類繼承(extends),靜態引用等等)
考慮下面的循環引用情況:
import { C } from "./C.ts" export class A { // use C here } import { A } from "./A.ts" export class B extends A { // methods here } import { B } from "./B.ts" export class C extends B { // methods here }
假設我們首先導入 A.ts,我們來分析下導入流程:
A 模塊嘗試導入 C 模塊
由於 C 模塊尚未導入,所以我們開始導入 C 模塊
C 模塊嘗試導入 B 模塊
由於 B 模塊尚未導入,所以我們開始導入 B 模塊
B 模塊嘗試導入 A 模塊
由於 A 模塊正在導入,所以程序直接返回當前導入結果
B 模塊繼承 A 模塊,嘗試在當前(A 模塊)導入結果中訪問類型 A 的定義
但是當前(A 模塊)導入結果中並沒有類型 A 的定義(因為當前 A 模塊的導入還沒有進行到 export class A)
Ops,導入出錯(找不到類型 A 的定義) …
對於上面這種情況,其實有一個技巧可以解決上面的問題:在不需要及時訪問模塊導出數據的情況下,我們可以將模塊的導入操作后置.
就上面的例子來講,我們可以這么修改代碼:
export class A { // use C here } // put import after export import { C } from "./C.ts" import { A } from "./A.ts" export class B extends A { // methods here } import { B } from "./B.ts" export class C extends B { // methods here }
我們再來分析下上面代碼的導入流程(仍然假設首先導入 A.ts):
A 模塊將類型 A 加入到 A 模塊的導出數據中(export class A)
A 模塊嘗試導入 C 模塊
由於 C 模塊尚未導入,所以我們開始導入 C 模塊
C 模塊嘗試導入 B 模塊
由於 B 模塊尚未導入,所以我們開始導入 B 模塊
B 模塊嘗試導入 A 模塊
由於 A 模塊正在導入,所以程序直接返回當前導入結果
類型 B 繼承 類型 A ,嘗試在當前(A 模塊)導入結果中訪問類型 A 的定義
當前(A 模塊)導入結果中存在類型 A 的定義, 類型 B 可以正常定義導出
B 模塊將類型 B 加入到 B 模塊的導出數據中(export class B)
B 模塊導入完成,繼續 C 模塊的導入
類型 C 繼承 類型 B,嘗試在當前(B 模塊)導入結果中訪問類型 B 的定義
當前(B 模塊)導入結果中存在類型 B 的定義, 類型 C 可以正常定義導出
C 模塊導入完成, 繼續 A 模塊的導入
A 模塊導入完成
但是如果我們嘗試首先導入 B 模塊(B.ts)的話,仍然會遇到導入出錯的問題:
B 模塊嘗試導入 A 模塊
由於 A 模塊尚未導入,所以我們開始導入 A 模塊
A 模塊嘗試導入 C 模塊
由於 C 模塊尚未導入,所以我們開始導入 C 模塊
C 模塊嘗試導入 B 模塊
由於 B 模塊正在導入,所以程序直接返回當前導入結果
類型 C 繼承 類型 B,嘗試在當前(B 模塊)導入結果中訪問類型 B 的定義
但是當前(B 模塊)導入結果中並沒有類型 B 的定義(因為當前 B 模塊的導入還沒有進行到 export class B)
Ops,導入出錯(找不到類型 B 的定義) …
這種情況下,我們已經不能通過后置 import 來解決問題了(因為類型 B 和 類型 C 的定義導出都需要及時訪問導入模塊的導出數據),我們只能通過改變模塊的導入順序來規避導入出錯的問題 …