React 組件庫都是怎麼打包的?

大家都用過組件庫,react 流行的組件庫有阿里的 ant-design、字節的 semi-design、arco-design 等。

那這些組件庫都是怎麼打包的呢?

我們自己寫個組件庫的話,怎麼寫打包邏輯呢?

這篇文章我們就來探究下。

新建一個項目:

mkdir component-lib-test

cd component-lib-test

npm init -y

分別安裝 ant-design、arco-design、semi-design

pnpm install antd

pnpm install @douyinfe/semi-ui

pnpm install @arco-design/web-react

npm、yarn 會把所有依賴鋪平,看着比較亂。而 pnpm 不會,node_modules 下很清晰:

首先看下 antd,分爲了 lib、es、dist 3 個目錄:

分別看下這三個目錄的組件代碼:

lib 下的組件是 commonjs 的:

es 下的組件是 es module 的:

dist 下的組件是 umd 的:

然後在 package.json 裏分別聲明瞭 commonjs、esm、umd 還有類型的入口:

這樣,當你用 require 引入的就是 lib 下的組件,用 import 引入的就是 es 下的組件。

而直接 script 標籤引入的就是 unpkg 下的組件。

再來看看 semi-design 的:

也是一樣:

只不過多了個 css 目錄。

antd 沒有這個目錄是因爲它已經換成 css in js 的方案了,不需要單獨引入 css 文件。

然後是 arco-design 的:

也是一樣:

同樣是 lib、es、dist 3 個目錄,同樣是分別聲明瞭 esm、commonjs、umd 的入口。

也就是說,組件庫都是這樣的,分別打包出 3 份代碼(esm、commonjs、umd),然後在 package.json 裏聲明不同模塊規範的入口。

那問題來了,如果我有一個 esm 的模塊,怎麼分別構建出 esm、commonjs、umd 的 3 份代碼呢?

這個問題很容易回答。

umd 的代碼用 webpack 打包就行。

esm 和 commonjs 的不用打包,只需要用 tsc 或者 babel 編譯下就好了。

我們分別看下這三個組件庫都是怎麼做的:

先是 arco-design 的:

它的打包邏輯在 arco-cli 的 arco-scripts 下:

看下這個 index.ts

分別有 build 3 種代碼加上 build css 的方法。

我們分別看下:

esm 和 cjs 的編譯它封裝了一個 compileTS 的方法,然後傳入不同的 type。

compileTS 裏可以用 tsc 或者 babel 編譯:

tsc 編譯就是讀取項目下的 tsconfig.json,然後 compile:

babel 編譯是基於內置配置,修改了下產物 modules 規範,然後編譯:

babelConfig 裏配置了 typescript 和 jsx 的編譯:

再就是 umd:

和我們分析的一樣,確實是用 webpack 來打包:

webpack 配置裏可以看到,確實是爲了 unpkg 準備的,用了 ts-loader 和 babel-loader:

而 css 部分則是用了 less 編譯:

gulp 是用來組織編譯任務的,可以讓任務串行、並行的執行。

這裏的 gulp.series 就是串行執行任務,而 gulp.parallel 則是並行。

所以說,那 3 種代碼加上 css 文件是怎麼打包的就很清晰了:

其中用到 gulp 只是用來組織編譯任務的,可用可不用。

再來看下 semi-design 的:

它就沒有單獨分一個 xx-scripts 包了,直接在 semi-ui 的 scripts 目錄下。

它也是用到了 gulp 來組織任務。

看下這個 compileLib 的 gulp task:

這裏的 compileTSXForESM 和 ForCJS 很明顯就是編譯組件到 esm 和 cjs 兩種代碼的。

先用了 tsc 編譯再用了 babel 編譯:

然後是 umd,也是用了 webpack:

用了 babel-loader 和 ts-loader:

最後是 scss 的編譯:

semi-design 把所有組件的 scss 都放在了 semi-foundation 這個目錄下來維護:

所以編譯的時候就是這樣的:

就是把 semi-foundation 這個目錄下的所有 scss 編譯後合併成了一個文件

而 arco-design 的樣式是在組件目錄下維護的:

這個倒是沒啥大的區別,只是編譯的時候改下源碼目錄就好了。

這就是 semi-design 的 esm、cjs、umd、scss 是如何編譯打包的。

和 arco-design 的 scripts 區別大麼?

不大,只不過沒有單獨做一個 xxx-scripts 的包,編譯出 esm 和 cjs 代碼用的是 tsc + babel,而且用的是 scss 不是 less 而已。

再來看看 ant-design 的:

它也是單獨分了一個包來維護編譯打包的 scripts,叫做 @ant-design/tools。

它也有個 gulpfile 定義了很多 task

比如 compile 的 task 是編譯出 es 和 cjs 代碼的:

是不是很熟悉的感覺?

大家都是這麼搞的。

它也是先用了 tsc 再用 babel 編譯,最後輸出到 es 或者 lib 目錄:

打包 umd 代碼的時候也是用了 webpack:

只不過它這個 webpack 配置文件是讀取的組件庫項目目錄下的,而不像 arco-design 那樣是內置的。

這就是這三個組件庫的編譯打包的邏輯。

區別大麼?

不大,甚至可以說幾乎一模一樣。

總結

我們分析了 ant-design、semi-design、arco-design 組件庫的產物和編譯打包邏輯。

它們都有 lib、es、dist 目錄,分別放着 commonjs、es module、umd 規範的組件代碼。

並且在 package.json 裏用 main、module、unpkg 來聲明瞭 3 種規範的入口。

從產物上來看,三個組件庫都是差不多的。

然後我們分析了下編譯打包的邏輯。

ant-design 和 acro-design 都是單獨抽了一個放 scripts 的包,而 semi-design 沒有。

它們編譯 esm 和 cjs 代碼都用了 babel 和 tsc 來編譯,只不過 arco-design 是用 tsc 或者 babel 二選一,而 ant-design 和 semi-design 是先用 tsc 編譯再用 babel 編譯。

打包出 umd 的代碼,三個組件庫都是用的 webpack,只不過有的是把 webpack 配置內置了,有的是放在組件庫項目目錄下。

而樣式部分,ant-design 是用 css-in-js 的運行時方案了,不需要編譯,而 arco-design 用的 less,樣式放組件目錄下維護,semi-design 用的 scss,單獨一個目錄來放所有組件樣式。

並且編譯任務都是用的 gulp 來組織的,它可以串行、並行的執行一些任務。

雖然有一些細小的差別,但從整體上來看,這三大組件庫的編譯打包邏輯可以說是一模一樣的。

寫這樣的 scripts 麻煩麼?

並不麻煩,umd 部分的 webpack 打包大家都會,而 esm 和 cjs 用 babel 或者 tsc 編譯也不難,至於 scss、less 部分,那個就更簡單了。

所以編譯打包並不是組件庫的難點。

如果你要寫一個組件庫,也可以這樣來寫 scripts。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/hACjf-QUTC6TRRUs6g3znQ