relative 和 non-relative import
Relative Import以/
, ./
或../
為開(kāi)頭. 如
import Entry from "./components/Entry";
import { DefaultHeaders } from "../constants/http";
import "/mod";
所有其他的都是為non-relative import, 如:
import * as $ from "jquery";
import { Component } from "@angular/core";
relative import引入相對(duì)于當(dāng)前文件路徑的文件, 且無(wú)法引入ambient module declarations. 使用relative import來(lái)引入你自己實(shí)現(xiàn)的文件.
non-relative import引入相對(duì)于baseUrl
路徑, 或者存在于路徑映射(Path Mapping)中的文件, 可以引入ambient module declarations. 使用non-relative import來(lái)引入外部依賴(lài).
模塊解析策略
有兩種策略: Node或Classic. 通過(guò)--moduleResolution
來(lái)指定策略. 對(duì)于--module AMD | System | ES2015
來(lái)說(shuō), 默認(rèn)是Classic.
Classic (經(jīng)典策略)
這曾是TypeScript的默認(rèn)路徑解析策略. 現(xiàn)在它主要是為了維護(hù)向后兼容性了.
對(duì)于relative import, 在文件/root/src/folder/A.ts
中`import { b } from "./moduleB", 會(huì)進(jìn)行如下查找:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
對(duì)于non-relative import, 查找過(guò)程會(huì)從當(dāng)前文件沿著文檔樹(shù)向上查找. 在文件/root/src/folder/A.ts
中import { b } from "moduleB"
, 會(huì)進(jìn)行如下查找:
/root/src/folder/moduleB.ts
/root/src/folder/moduleB.d.ts
/root/src/moduleB.ts
/root/src/moduleB.d.ts
/root/moduleB.ts
/root/moduleB.d.ts
/moduleB.ts
/moduleB.d.ts
Node策略
這個(gè)策略模仿Node.js的模塊解析策略. Node.js的完整解析算法見(jiàn)這里.
Node.js如何解析模塊
Node.js中調(diào)用require
方法來(lái)引入模塊.
require
的路徑為相對(duì)路徑
文件/root/src/moduleA.js
中通過(guò)import var x = require("./moduleB");
引入模塊, 查找過(guò)程如下:
- 文件
/root/src/moduleB.js
- 文件夾
/root/src/moduleB
, 且其中有package.json
文件指定了"main"
模塊. 如/root/src/moduleB/package.json
中有{ "main": "lib/mainModule.js" }
, 那么Node.js會(huì)引入/root/src/moduleB/lib/mainModule.js
- 文件夾
/root/src/moduleB
, 且其中有index.js
. 這是默認(rèn)的"main"
模塊.
require
的路徑為絕對(duì)路徑
Node會(huì)從node_modules
文件夾中尋找模塊. node_modules
可能是在源文件當(dāng)前目錄中, 也可能是在文件樹(shù)的上層. Node會(huì)沿著文件樹(shù)逐層向上尋找node_modules
中的模塊.
文件/root/src/moduleA.js
通過(guò)import var x = require("moduleB");
引入模塊, 查找過(guò)程如下:
/root/src/node_modules/moduleB.js
-
/root/src/node_modules/moduleB/package.json
(if it specifies a"main"
property) /root/src/node_modules/moduleB/index.js
/root/node_modules/moduleB.js
-
/root/node_modules/moduleB/package.json (if it specifies a
"main"` property) /root/node_modules/moduleB/index.js
/node_modules/moduleB.js
-
/node_modules/moduleB/package.json
(if it specifies a"main"
property) /node_modules/moduleB/index.js
更多見(jiàn)從node_modules
引入模塊
TypeScript如何解析模塊
TypeScript模仿了Node的解析策略, 不過(guò)針對(duì)的是.ts
, .tsx
和.d.ts
文件, 而且package.json
中用"types"
對(duì)應(yīng)于Node中的"main"
.
相對(duì)路徑
文件/root/src/moduleA.ts
中引入import { b } from "./moduleB"
, 查找過(guò)程如下:
/root/src/moduleB.ts
/root/src/moduleB.tsx
/root/src/moduleB.d.ts
-
/root/src/moduleB/package.json
(if it specifies a"types"
property) /root/src/moduleB/index.ts
/root/src/moduleB/index.tsx
/root/src/moduleB/index.d.ts
絕對(duì)路徑
文件/root/src/moduleA.ts
中引入import { b } from "moduleB"
, 查找過(guò)程如下:
/root/src/node_modules/moduleB.ts
/root/src/node_modules/moduleB.tsx
/root/src/node_modules/moduleB.d.ts
-
/root/src/node_modules/moduleB/package.json
(if it specifies a"types"
property) /root/src/node_modules/moduleB/index.ts
/root/src/node_modules/moduleB/index.tsx
/root/src/node_modules/moduleB/index.d.ts
/root/node_modules/moduleB.ts
/root/node_modules/moduleB.tsx
/root/node_modules/moduleB.d.ts
-
/root/node_modules/moduleB/package.json
(if it specifies a"types"
property) /root/node_modules/moduleB/index.ts
/root/node_modules/moduleB/index.tsx
/root/node_modules/moduleB/index.d.ts
/node_modules/moduleB.ts
/node_modules/moduleB.tsx
/node_modules/moduleB.d.ts
-
/node_modules/moduleB/package.json
(if it specifies a"types"
property) /node_modules/moduleB/index.ts
/node_modules/moduleB/index.tsx
/node_modules/moduleB/index.d.ts
模塊解析參數(shù)
通常, 項(xiàng)目中都會(huì)通過(guò)一系列的構(gòu)建程序?qū)⒃茨夸浿械奈募? 打包/壓縮到目標(biāo)目錄中. 這個(gè)過(guò)程可能導(dǎo)致文件名或目錄結(jié)構(gòu)發(fā)生變化. 因此TypeScript編譯器有如下幾個(gè)參數(shù)應(yīng)對(duì)這種變化.
Base URL
AMD模塊加載器, 如requireJS, 常使用baseUrl
. 源文件可以在多個(gè)目錄中, 但目標(biāo)文件會(huì)被放到同一個(gè)目錄中.
所有non-relative import都相對(duì)于baseUrl
.
baseUrl
的值可以是:
- baseUrl的命令行參數(shù) (如果參數(shù)為相對(duì)路徑, 則該參數(shù)相對(duì)于當(dāng)前路徑)
-
tsconfig.json
中的baseUrl
(如果參數(shù)為相對(duì)路徑, 則該參數(shù)相對(duì)于tsconfig.json
的目錄)
Path Mapping (路徑映射)
你還可以使用tsconfig.json
中的"paths"
屬性, 定義Path Mapping. 如:
{
"compilerOptions": {
"baseUrl": ".", // This must be specified if "paths" is.
"paths": {
"jquery": ["node_modules/jquery/dist/jquery"] // This mapping is relative to "baseUrl"
}
}
}
注意Path Mapping是相對(duì)于baseUrl
的.
"paths"
可以用來(lái)實(shí)現(xiàn)路徑回退(Fallback)查找. 舉個(gè)例子.
projectRoot
├── folder1
│ ├── file1.ts (imports 'folder1/file2' and 'folder2/file3')
│ └── file2.ts
├── generated
│ ├── folder1
│ └── folder2
│ └── file3.ts
└── tsconfig.json
tsconfig.json
內(nèi)容如下
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"*": [
"*",
"generated/*"
]
}
}
}
這個(gè)配置讓編譯器對(duì)滿(mǎn)足模式"*"
(即所有文件)的模塊引用, 進(jìn)行如下查找:
-
"*"
: meaning the same name unchanged, so map<moduleName>
=><baseUrl>/<moduleName>
-
"generated/*"
meaning the module name with an appended prefix“generated”
, so map<moduleName>
=><baseUrl>/generated/<moduleName>
所以, 對(duì)于file1.ts
中的兩個(gè)引用:
-
import ‘folder1/file2’
-
"*"
捕獲文件名folder1/file2
- 嘗試第一種替換, 即:
"*"
=>folder1/file2
- 得到的是絕對(duì)路徑, 將其與
baseUrl
結(jié)合, 得到projectRoot/folder1/file2.ts
- 文件找到, 結(jié)束.
-
-
import ‘folder2/file3’
-
"*"
捕獲文件名folder2/file3
- 嘗試第一種替換, 即:
"*"
=>folder2/file3
- 得到的是絕對(duì)路徑, 將其與
baseUrl
結(jié)合, 得到projectRoot/folder2/file3.ts
- 文件不存在, 嘗試第二種替換
"generated/*"
=>generated/folder2/file3
- 得到的是絕對(duì)路徑, 將其與
baseUrl
結(jié)合, 得到projectRoot/generated/folder2/file3.ts
- 文件找到, 結(jié)束.
-
import后面的花括號(hào)
export分為兩種, Named Export(有名導(dǎo)出)和Default Export(默認(rèn)導(dǎo)出). 花括號(hào)是用于Named Export的.
Named Export
// module.ts
export function A() { ... };
export function B() { ... };
// app.ts
import { A, B as BB } from 'module.ts';
其中, 函數(shù)A
和B
是Named Export模式, 導(dǎo)入時(shí)需要加花括號(hào), 而且可以使用as
改名.
Default Export
// module.ts
export default function C () { ... };
// app.ts
import myFunc from 'module';
// or
import { default as myFunc } from 'module';
函數(shù)C
以default
方式導(dǎo)出, 引入的時(shí)候不用加花括號(hào), myFunc
即為C
.
其實(shí)default
導(dǎo)出也是一種Named Export, 只不過(guò)它導(dǎo)出的變量被加了默認(rèn)的名字default
, 所以app.ts
中的兩句話效果一樣.
export =
和import = require()
CommonJS 和 AMD 都有exports
對(duì)象的概念, exports
對(duì)象包含了所有導(dǎo)出的內(nèi)容. 它們也都支持將exports
對(duì)象替換為自己指定的一個(gè)對(duì)象/函數(shù)/變量等.
相應(yīng)地, Typescript使用export =
, 導(dǎo)出的內(nèi)容可以是class
, interface
, namespace
, function
或者enum
.
使用export =
時(shí)必須相應(yīng)地使用import module = require("module")
.
// module.ts
class A { ... }
export = A;
// app.ts
import A = requrie("module");
import * as X from "XXX"
是什么意思
// pet.ts
export class Dog { ... }
export class Cat { ... }
// app.ts
import * as Pet from "./pet.ts";
let x = new Pet.Dog();
可以看到, import * as X from "XXX"
的作用就是從"XXX"
文件中, 引入所有導(dǎo)出的內(nèi)容作為X
的屬性.
與import X = require("XXX")
作用相同.
ambient module declarations
var x = require
VS import x from
在Node.js (CommonJS)中使用 var x = require("XXX")
, 其中var
也可以用const
等修飾詞替換. 與之配套的是module.exports
和exports
.
exports.A = function() {...}
module.exports.A = function() {...}
module.exports = { ... }
ES6采用import x from
的形式, 與之配套的是export
:
export function A() { ... }
export = { ... }
var x = require
和import x = require
的區(qū)別?
我現(xiàn)在項(xiàng)目中常看到import x = require
.