小編這次要給大家分享的是如何使用Typescript和ES模塊發(fā)布Node模塊,文章內(nèi)容豐富,感興趣的小伙伴可以來(lái)了解一下,希望大家閱讀完這篇文章之后能夠有所收獲。
網(wǎng)站建設(shè)哪家好,找成都創(chuàng)新互聯(lián)!專(zhuān)注于網(wǎng)頁(yè)設(shè)計(jì)、網(wǎng)站建設(shè)、微信開(kāi)發(fā)、微信平臺(tái)小程序開(kāi)發(fā)、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶(hù)創(chuàng)新互聯(lián)還提供了定結(jié)免費(fèi)建站歡迎大家使用!
本文主要介紹了使用Typescript和ES模塊發(fā)布Node模塊的方法,分享給大家,具體如下:
TypeScript已經(jīng)成為一種非常流行的JavaScript語(yǔ)言,這是有原因的。它的類(lèi)型系統(tǒng)和編譯器能夠在您的軟件運(yùn)行之前的編譯時(shí)捕獲各種bug,并且附加的代碼編輯器功能使它成為一個(gè)非常適合開(kāi)發(fā)人員的高效環(huán)境。
但是,當(dāng)你想用TypeScript編寫(xiě)一個(gè)庫(kù)或包,同時(shí)又想用JavaScript來(lái)發(fā)布,這樣你的最終用戶(hù)就不必手動(dòng)編譯你的代碼,會(huì)發(fā)生什么?我們?nèi)绾问褂矛F(xiàn)代的JavaScript功能(如ES模塊)來(lái)編寫(xiě),同時(shí)又能獲得TypeScript的所有好處?
本文旨在解決所有這些問(wèn)題,并為你提供一個(gè)設(shè)置,使你可以放心地編寫(xiě)和共享TypeScript庫(kù),并為包裝的使用者提供輕松的體驗(yàn)。
入門(mén)
我們要做的第一件事是建立一個(gè)新項(xiàng)目。在本教程中,我們將創(chuàng)建一個(gè)基本的數(shù)學(xué)程序包——不是一個(gè)服務(wù)于任何實(shí)際目的的程序包——因?yàn)樗鼘⒆屛覀冄菔舅形覀冃枰腡ypeScript,而不會(huì)偏離程序包的實(shí)際功能。
首先,創(chuàng)建一個(gè)空目錄并運(yùn)行 npm init -y
創(chuàng)建一個(gè)新項(xiàng)目。這將創(chuàng)建你的 package.json
并為你提供一個(gè)空項(xiàng)目以供處理:
$ mkdir maths-package $ cd maths-package $ npm init -y
現(xiàn)在,我們可以添加第一個(gè)也是最重要的依賴(lài)項(xiàng):TypeScript!
$ npm install --save-dev typescript
安裝TypeScript后,可以通過(guò)運(yùn)行 tsc --init
初始化TypeScript項(xiàng)目。 tsc
是“ TypeScript編譯器”的縮寫(xiě),是TypeScript的命令行工具。
為確保你運(yùn)行我們剛剛在本地安裝的TypeScript編譯器,應(yīng)在命令前加上 npx
。npx是個(gè)很棒的工具,它將在node_modules
文件夾中查找你提供的命令,因此,通過(guò)在命令前面加上前綴,可以確保我們使用的是本地版本,而不是你可能已安裝的TypeScript的任何其他全局版本。
$ npx tsc --init
這將創(chuàng)建一個(gè) tsconfig.json
文件,該文件負(fù)責(zé)配置我們的TypeScript項(xiàng)目。您會(huì)看到該文件具有數(shù)百個(gè)選項(xiàng),其中大多數(shù)選項(xiàng)已被注釋掉(TypeScript支持 tsconfig.json
文件中的注釋?zhuān)?。我已將文件縮減為僅啟用的設(shè)置,如下所示:
{ "compilerOptions": { "target": "es5", "module": "commonjs", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true } }
我們需要對(duì)此配置進(jìn)行一些更改,以使我們能夠使用ES模塊發(fā)布程序包,因此,讓我們現(xiàn)在來(lái)看一下這些選項(xiàng)。
配置tsconfig.json 選項(xiàng)
如果您正在尋找所有可能的 tsconfig
選項(xiàng)的完整列表,可以在TypeScript網(wǎng)站上找到此方便的參考。
讓我們從 target
開(kāi)始,這定義了你將在瀏覽器中提供代碼的JavaScript支持級(jí)別。如果您必須使用一組較舊的瀏覽器,這些瀏覽器可能不具有所有最新和最強(qiáng)大的功能,則可以將其設(shè)置為 ES2015
。如果您確實(shí)需要最大的瀏覽器覆蓋范圍,TypeScript甚至將支持 ES3
。
我們將在此處針對(duì)該模塊使用 ES2015
,但可以隨時(shí)進(jìn)行相應(yīng)更改。例如,如果我為自己建立一個(gè)快速的輔助項(xiàng)目,并且只關(guān)心尖端的瀏覽器,那么我很高興將其設(shè)置為 ES2020
。
選擇模塊系統(tǒng)
接下來(lái),我們必須決定將用于該項(xiàng)目的模塊系統(tǒng)。請(qǐng)注意,這不是我們要編寫(xiě)的模塊系統(tǒng),而是TypeScript的編譯器在輸出代碼時(shí)將使用的模塊系統(tǒng)。
發(fā)布模塊時(shí)我喜歡做的事情是發(fā)布兩個(gè)版本:
require
代碼),因此較早的構(gòu)建工具和Node.js環(huán)境可以輕松運(yùn)行該代碼稍后我們將介紹如何使用不同的選項(xiàng)捆綁兩次,但是現(xiàn)在,讓我們將TypeScript配置為輸出ES模塊。我們可以通過(guò)將 module
設(shè)置設(shè)置為 ES2020
來(lái)實(shí)現(xiàn)。
現(xiàn)在,你的 tsconfig.json
文件應(yīng)如下所示:
{ "compilerOptions": { "target": "ES2015", "module": "ES2020", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true } }
編寫(xiě)一些代碼
在討論捆綁代碼之前,我們需要寫(xiě)一些代碼!讓我們創(chuàng)建兩個(gè)小模塊,它們既導(dǎo)出函數(shù),又為導(dǎo)出所有代碼的模塊提供一個(gè)主 entry 文件。
我喜歡將所有TypeScript代碼放在 src
目錄中,因?yàn)檫@意味著我們可以直接將TypeScript編譯器指向它,因此,我將使用以下代碼創(chuàng)建 src/add.ts
:
export const add = (x: number, y:number):number => { return x + y; }
我也將創(chuàng)建 src/subtract.ts
:
export const subtract = (x: number, y:number):number => { return x - y; }
最后,src/index.ts
將導(dǎo)入我們所有的API方法并再次導(dǎo)出它們:
import { add } from './add.js' import { subtract } from './subtract.js' export { add, subtract }
這意味著,用戶(hù)可以通過(guò)導(dǎo)入只需要的東西來(lái)獲取我們的功能,也可以通過(guò)獲取所有的東西來(lái)獲取。
import { add } from 'maths-package'; import * as MathsPackage from 'maths-package';
請(qǐng)注意,在 src/index.ts
中,我的導(dǎo)入包含文件擴(kuò)展名。如果只想支持Node.js和構(gòu)建工具(例如webpack),則不需要這樣做,但是如果要支持支持ES模塊的瀏覽器,則需要文件擴(kuò)展名。
使用TypeScript進(jìn)行編譯
讓我們看看是否可以讓TypeScript編譯我們的代碼。我們需要先對(duì) tsconfig.json
文件進(jìn)行一些調(diào)整,然后才能執(zhí)行以下操作:
{ "compilerOptions": { "target": "ES2015", "module": "ES2020", "strict": true, "esModuleInterop": true, "forceConsistentCasingInFileNames": true, "outDir": "./lib", }, "include": [ "./src" ] }
我們進(jìn)行了兩項(xiàng)更改:
compilerOptions.outDir
——這告訴TypeScript將我們的代碼編譯到一個(gè)目錄中。在這種情況下,我已經(jīng)告訴它命名該目錄 lib
,但是您可以根據(jù)需要命名它。include
——告訴TypeScript我們希望在編譯過(guò)程中包含哪些文件。在我們的例子中,我們所有的代碼都位于src
目錄中,因此我將其傳入。這就是為什么我喜歡將所有TS源文件保存在一個(gè)文件夾中的原因,這使配置變得非常容易讓我們來(lái)試一試,看看會(huì)發(fā)生什么吧! 我發(fā)現(xiàn)在調(diào)整我的TypeScript配置時(shí),最適合我的方法是調(diào)整、編譯、檢查輸出,然后再調(diào)整。不要害怕嘗試這些設(shè)置,看看它們?nèi)绾斡绊懽罱K結(jié)果。
要編譯TypeScript,我們將運(yùn)行 tsc
并使用 -p
標(biāo)志(“project”的縮寫(xiě))告訴它 tsconfig.json
的位置:
npx tsc -p tsconfig.json
如果你有任何類(lèi)型錯(cuò)誤或配置問(wèn)題,將在此處顯示。如果沒(méi)有,您應(yīng)該什么也看不到——但是請(qǐng)注意,你有一個(gè)新的 lib
目錄,其中有文件!TypeScript編譯時(shí)不會(huì)將任何文件合并在一起,而是將每個(gè)模塊轉(zhuǎn)換成對(duì)應(yīng)的JavaScript。
讓我們看一下輸出的三個(gè)文件:
// lib/add.js export const add = (x, y) => { return x + y; }; // lib/subtract.js export const subtract = (x, y) => { return x - y; }; // lib/index.js import { add } from './add.js'; import { subtract } from './subtract.js'; export { add, subtract };
它們看起來(lái)和我們的輸入非常相似,但沒(méi)有我們添加的類(lèi)型注釋。這是可以預(yù)期的:我們?cè)贓S模塊中編寫(xiě)了我們的代碼,并告訴TypeScript也要以這種形式輸出。如果我們使用了比ES2015更新的任何JavaScript功能,TypeScript會(huì)將它們轉(zhuǎn)換為ES2015友好的語(yǔ)法,但是在我們的案例中,我們沒(méi)有使用它,因此TypeScript在很大程度上僅保留了所有內(nèi)容。
該模塊現(xiàn)在可以發(fā)布到npm上供其他用戶(hù)使用,但是我們有兩個(gè)問(wèn)題需要解決:
發(fā)布類(lèi)型定義
我們可以通過(guò)要求TypeScript在寫(xiě)代碼的同時(shí)發(fā)出一個(gè)聲明文件來(lái)解決類(lèi)型信息問(wèn)題。這個(gè)文件的結(jié)尾是 .d.ts
,它將包含關(guān)于我們代碼的類(lèi)型信息。將它看作源代碼,除了不包含類(lèi)型和實(shí)現(xiàn)之外,它只包含類(lèi)型。
讓我們?cè)?tsconfig.json
中添加 "declaration": true
(在 "compilerOptions"
部分中),然后再次運(yùn)行 npx tsc -p tsconfig.json
。
提示:我想在我的 package.json
文件中添加一個(gè)腳本來(lái)進(jìn)行編譯,因此無(wú)需輸入以下內(nèi)容:
"scripts": { "tsc": "tsc -p tsconfig.json" }
然后我可以運(yùn)行 npm run tsc
來(lái)編譯我的代碼。
現(xiàn)在,您將看到每個(gè)JavaScript文件(例如 add.js
)旁邊都有一個(gè)等效的 add.d.ts
文件,如下所示:
"scripts": { "tsc": "tsc -p tsconfig.json" }
因此,現(xiàn)在當(dāng)用戶(hù)使用我們的模塊時(shí),TypeScript編譯器將能夠選擇所有這些類(lèi)型。
發(fā)布到CommonJS
難題的最后一部分是還將TypeScript配置為輸出使用CommonJS的代碼版本。為此,我們可以制作兩個(gè) tsconfig.json
文件,一個(gè)針對(duì)ES模塊,另一個(gè)針對(duì)CommonJS。不過(guò),我們可以讓CommonJS配置擴(kuò)展我們的默認(rèn)設(shè)置并覆蓋 modules
設(shè)置,而不是復(fù)制所有配置。
讓我們創(chuàng)建 tsconfig-cjs.json
:
{ "extends": "./tsconfig.json", "compilerOptions": { "module": "CommonJS", "outDir": "./lib/cjs" }, }
重要的是第一行,這意味著此配置默認(rèn)情況下會(huì)繼承 tsconfig.json
的所有設(shè)置。這很重要,因?yàn)槟悴恍枰诙鄠€(gè)JSON文件之間同步設(shè)置。
然后覆蓋需要更改的設(shè)置。我相應(yīng)地更新模塊,然后將 outDir
設(shè)置更新到 lib/cjs
,這樣我們就可以輸出到lib
中的子文件夾。
此時(shí),我還更新了 package.json
中的 tsc
腳本:
"scripts": { "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json" }
現(xiàn)在,當(dāng)我們運(yùn)行 npm run tsc
時(shí),我們將編譯兩次,并且我們的lib目錄將如下所示:
這個(gè)有點(diǎn)亂,讓我們通過(guò)更新 tsconfig
中的 outDir
選項(xiàng)來(lái)將ESM輸出更新到 lib/esm
中
接下來(lái),我們將設(shè)置 module
屬性。這是應(yīng)該鏈接到我們軟件包的ES模塊版本的屬性。支持此功能的工具將能夠使用此版本的軟件包。因此,應(yīng)將其設(shè)置為 ./lib/esm/index.js
。
接下來(lái),我們將 files
entry 添加到 package.json
中。在這里,我們定義了發(fā)布模塊時(shí)應(yīng)包括的所有文件。我喜歡使用這種方法來(lái)明確定義要在最終模塊中推送到npm的文件。
這樣我們就可以減小模塊的大小。例如,我們不會(huì)發(fā)布 src
文件,而是發(fā)布 lib
目錄。如果你在 files
entry 中提供目錄,則默認(rèn)情況下會(huì)包含其所有文件和子目錄,因此你不必全部列出。
提示:如果要查看模塊中將包含哪些文件,請(qǐng)運(yùn)行 npx pkgfiles
以獲得列表。
現(xiàn)在,我們的 package.json
中包含以下三個(gè)附加字段:
"main": "./lib/cjs/index.js", "module": "./lib/esm/index.js", "files": [ "lib/" ],
還有最后一步。因?yàn)槲覀円l(fā)布 lib
目錄,所以需要確保在運(yùn)行 npm publish
時(shí) lib
目錄是最新的。npm文檔中有一節(jié)是關(guān)于如何做到這一點(diǎn)的——我們可以使用 prepublishOnly
腳本。當(dāng)我們運(yùn)行 npm publish
時(shí),該腳本將自動(dòng)為我們運(yùn)行:
"scripts": { "tsc": "tsc -p tsconfig.json && tsc -p tsconfig-cjs.json", "prepublish": "npm run tsc" },
注意,還有一個(gè)名為 prepublish
的腳本,這使選擇哪個(gè)稍微有些混亂。npm文檔提到了這一點(diǎn):不推薦使用prepublish
,如果只想在發(fā)布時(shí)運(yùn)行代碼,則應(yīng)使用prepublishOnly
。
這樣,運(yùn)行 npm publish
將運(yùn)行我們的TypeScript編譯器并在線發(fā)布模塊!我將該軟件包發(fā)布在 @ jackfranklin/maths-package-for-blog-post
下,雖然我不建議你使用它,但是你可以瀏覽文件并查看。我還將所有代碼都上傳到了CodeSandbox中,因此您可以根據(jù)需要下載或破解它。
看完這篇關(guān)于如何使用Typescript和ES模塊發(fā)布Node模塊的文章,如果覺(jué)得文章內(nèi)容寫(xiě)得不錯(cuò)的話,可以把它分享出去給更多人看到。