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