不借助腳手架手動搭建react專案(webpack5 + Antd4 + React18)

你比從前快樂KX發表於2023-02-24

前言

工作中發現很多同事在接到一個新專案時,總是基於現有專案複製一份配置檔案,然後寫自己的元件及業務程式碼,以至於專案中存在一些冗餘的依賴及配置資訊。並且由於已有專案的依賴包及外掛比較老,新專案也一直沒有得到更新。即使是自己搭建,為了省時省力,大多會選擇透過React提供的腳手架create-react-app建立專案,一行命令全部搞定。從來沒有研究過各個模組是如何配置的,下面我跟搭建一起從0開始手動搭建一個React專案(PS:不要在乎我的專案路徑之類的,因為我這邊用的虛擬機器並且只有一個磁碟)

基礎環境準備

在開始搭建專案之前先安裝好nodejs,安裝包去node官網下載即可,下載安裝都比較簡單,這裡不做贅述。安裝好以後開啟CMD命令列視窗,輸入node -v及npm -v,如果正確顯示版本資訊,則表示安裝成功。

專案初始化

node裝好以後,在磁碟合適位置新建一個資料夾,比如my_react_app,進入該資料夾以後在目錄行輸入cmd回車使用命令列視窗開啟

在開啟的視窗目錄下輸入命令:npm init,回車初始化基礎專案框架

初始化完成後會在資料夾下面生成一個package.json檔案,裡面就是在建立專案時指定的基礎資訊,如下:

安裝webpack

webpack是一個靜態模組打包工具,可以將我們的程式碼打包成瀏覽器可以認識的檔案。webpack一般配合webpack-cli一起使用,webpack命令的使用依賴webpack-cli

webpack安裝分為全域性安裝跟區域性安裝:
全域性安裝,打包時用的全域性的webpack,那麼多人開發時可能由於電腦上版本不一樣導致打包的版本不一致。
區域性安裝,每個專案版本固定,統一依賴,避免打包時版本不一致問題

npm install webpack webpack-cli -g  #全域性安裝
npm install webpack webpack-cli -D  #區域性安裝

命令列視窗進入專案目錄,執行區域性安裝命令,如下:

安裝完成以後,專案根目錄下會生成package-lock.json檔案以及node_modules資料夾。其中node_modules裡面是各種依賴包,說一下package-lock.json跟package.json的區別:
package.json記錄專案中所需的所有模組資訊,但不會記錄這些模組所依賴的子模組資訊及版本。在npm5以前沒有package-lock.json檔案,所以要儲存依賴資訊需要在後面加上 --save 引數,而在npm5以後則不需要了,所以package-lock.json檔案儲存的是專案所需模組及其子依賴包的版本資訊,相對較全,並且可以鎖定版本,防止自動升級。所以當我們刪了依賴包想要快速恢復時,可以直接執行npm install,此時node會從package.json中讀取模組名稱並且從package-lock.json中讀取版本資訊。如果我們想更新某個模組版本,需要執行npm install packagename或者npm install packagename@X.X.X,前者不指定版本號則更新為當前最新版本,後者按照指定版本號更新,package-lock.json檔案也會自動更新記錄當前版本資訊。

建立目錄結構,驗證打包

在專案根目錄建一個src資料夾,一個public資料夾。src下面用於存放我們的專案程式碼,在裡面建一個index.js檔案,作為入口檔案,public下面建一個index.html,這是我們專案執行的主介面。再在專案根目錄下建一個webpack.config.js檔案,寫我們的webpack配置程式碼,這就是當前基本的專案結構,如下:

OK,接下來我們寫一點基礎程式碼驗證一下基礎搭建是否成功:

在webpack.config.js中寫入基礎配置,如下:

安裝一個html-webpack-plugin外掛,該外掛是可以讓webpack按照指定模板生成主介面,安裝好以後再在配置檔案中新增進去,如下:

然後在index.js裡隨便寫點程式碼驗證打包,如下:

寫好以後在終端輸入打包命令:npm run build,可以看到根目錄下生成了一個dist資料夾,裡面有一個index.html檔案及一個main.js檔案,這就是按照配置檔案生成的兩個檔案,檢視main.js可以看到我們剛剛在index.js裡寫入的程式碼編譯結果,如下:

JSX語法、ES5+語法及高階API支援

我們在react專案中寫的JSX語法程式碼以及ES6等各種ES5+的程式碼,瀏覽器是無法解析的,所以我們需要將這些轉換為瀏覽器認識的東西,這個事情是由babel外掛去完成。
執行以下命令安裝外掛:

npm install babel-loader @babel/core @babel/preset-env @babel/preset-react --save-dev

其中babel-loader是webpack的loader,用來預處理檔案,告訴webpack當遇到js檔案時交給babel處理,至於怎麼處理跟webpack無關,取決於babel的配置。babel/core是babel的核心庫,提供轉換的API,babel/preset開頭的代表預設,@babel/preset-env用來將ES5+的高階語法轉化為es5,babel/preset-react用來解析jsx語法,具體的可以看babel官網

下載完以後,package.json配置檔案中可以看到對應模組資訊,package-lock.json也會同步更新進去。然後需要配置babel,可以透過新增babel配置檔案的形式也可以直接在webpack配置檔案中作為某條規則的optionsi寫入。我這裡使用官網推薦的方式,在專案根目錄下新建一個叫babel.config.json的配置檔案(需要 v7.8.0 或更高版本,如果是舊版本的babel,則命名為babel.config.js),並寫入如下配置資訊:

然後在webpack.config.js檔案中新增babel-loader外掛的配置資訊,如下:

這個時候我們就可以在index.js中寫點ES6語法的程式碼,驗證下打包後是否成功轉換為ES5程式碼,如下:

執行打包命令後,可以看到箭頭函式已經成功轉換為ES5語法程式碼,如下:

接下來我們寫點使用新API的程式碼,比如熟悉的promise,再次打包檢視結果如下:

可以看到有報錯,因為@babel/preset-env 只能轉換ES6等高階語法,並不能轉換例如promise、async等新的API,然後我們在上面配置了polyfill的按需引入以及corejs引數,但是還沒有安裝外掛,執行以下命令安裝 core-js regenerator-runtime(7.4版本以後棄用@babel/polyfill,推薦分開安裝),如下:

然後再次打包看打包成功的結果,如下:

接下來安裝 @babel/plugin-transform-runtime、@babel/runtime、@babel/runtime-corejs3,用於自動去除語法轉換後的內斂函式以最佳化體積及避免全域性變數汙染,如下:

為了可以更明顯的看到打包體積變小的效果,新建3個檔案,在index.js下引入,執行打包命令,如下:

再把@babel/plugin-transform-runtime配置加上,檢視打包體積,如下:

可以看到體積大小從294變成了168,明顯減少。體積減少的原理就是,babel在轉換的時候有時候需要用一些輔助函式,如果多個檔案都有相同的語法,那每個檔案都會注入相同的一份輔助函式,而針對這種情況 @babel/plugin-transform-runtime 會自動將需要引入的 helpers 函式替換為從 @babel/runtime 中的引用,這樣就只使用了一份,從而減少體積。

Less支援,Antd按需引入

由於antd元件是基於less方式開發的,所以我們也得配置一下支援less前處理器編寫的程式碼轉換為css,當前支援的方式主要有三種:
第一種:react-app-rewired + customize-cra,antd@3X版本提出的方案
第二種:antd@4.19.4文件提出的craco方案
第三種:安裝loader外掛

我們這裡使用第三種,安裝需要的幾種loader,執行以下命令:

npm install less less-loader css-loader style-loader -D

npm install babel-plugin-import

其中css-loader是將css檔案編譯為webpack能識別的模組,less-loader負責將css檔案編譯為css檔案,style-loader會動態生成style標籤並將css內容放進去進行渲染,less是常用的前處理器,而babel-plugin-import是用於配置antd按需引入。

順便提一句,我們在安裝依賴或者外掛的時候可能會出現下面的錯誤(沒有報錯或者不使用該方式請忽略),比如我在使用craco方式的時候,如下:

可以看到報錯了,錯誤提示為無法解析依賴樹,簡單來說就是依賴衝突了,這是因為在npm@7.X中,預設安裝peerDependencies,這在很多情況下都會導致版本衝突,從而中斷安裝過程。可以透過降npm版本到7以前(比如npm@6)解決,也可以透過npm7引入的--legacy-peer-deps解決,該引數的目的是告訴npm忽略專案中引入的各個modules之間的相同modules但不同版本的問題並繼續安裝,保證各個引入的依賴之間對自身所使用的不同版本modules共存(原文連結)。我這裡選擇在後面加上該引數重新安裝,如下:

接下來安裝antd,由於antd@5X版本變動較大(比如:去除了less),還沒仔細研究~,我這裡暫時用4X版本了,各位可以根據具體專案選擇,執行如下命令:

npm install antd@4 --legacy-peer-deps

安裝完以後在babel.config.json配置檔案中配置antd按需引入,如下:

接下來在src目錄下新建一個index.less檔案,寫點less語法程式碼,並在index.js中引入,驗證打包,如下:

執行打包命令,可以看到已經成功解析,如下:

另外我們在開發過程中經常會需要相容各種瀏覽器,如果手動新增字首會很麻煩,所以我們可以透過配置postcss-loader來自動新增字首,執行安裝:

npm install --save-dev postcss-loader postcss postcss-preset-env

安裝完以後在webpack.config.js中加入外掛配置,如下:

然後我們可以在package.json檔案中新增 browserslist 來控制樣式的相容性做到什麼程度,比如:

整合React

要使用react,首先需要安裝react跟react-dom,如下:

npm install react react-dom

安裝好以後,我們就可以寫react獨特的JSX語法程式碼了,還記得在上面的時候,我已經安裝過@babel/preset-react,這個就是實現將jsx轉換為ES5程式碼的外掛,下面我們在index.js裡面寫點程式碼,然後在index.html中新增一個div承載需要渲染的元件。如下:

執行打包命令看下效果,如下:

熱載入

到這一步基本能用了,但是我們會發現每次改點什麼都得手動重新編譯,而且每次重新編譯之前都要手動刪除dist資料夾及裡面的內容,很不方便。我們引入webpack-dev-server及clean-webpack-plugin,其中webpack-dev-server相當於提供一個本地的web服務,同時具有live reloading(實時重新載入)功能,而clean-webpack-plugin外掛則是每次打包前自動刪除上次打包的東西,而之前安裝的html-webpack-plugin外掛可以將我們編譯好的js及css檔案動態新增到html中。執行安裝命令,如下:

npm install -D clean-webpack-plugin webpack-dev-server

安裝完以後,在webpack配置檔案中新增配置,如下:

然後再package.json中新增啟動命令,如下:

"start": "webpack-dev-server --open-app-name chrome" --後面引數代表啟動後自動開啟谷歌瀏覽器

配置好以後,輸入npm start啟動,如下:

可以看到專案成功執行,之前寫的那點字型為紅色的樣式也渲染到了html中,不過控制檯有兩個警告報錯,雖然不影響正常執行,但作為前端開發人員,看到控制檯有紅色就難受~~,一起看一下:
第一個報錯大概意思是ReactDOM.render在react18中已經不支援了,讓我們用createRoot替代,第二個錯誤是說LocaleProvider已棄用,請將“locale”與“ConfigProvider”一起使用。我們來試一下,修改index.js檔案如下:

我們配置了熱載入,修改完點儲存後,webpack會自動重新編譯且瀏覽器自動更新修改的地方,如下:

另外。為了方便開發過程中定位問題,可以配置在開發環境時,生成原始碼,如下:

具體專案開發時,可以把webpack配置檔案進行拆分,分別給生產環境及開發環境進行不同的配置,具體的可以參考其他資料,這裡不做說明,重新啟動看下效果:

OK,到這裡就結束了,另外還有很多可以最佳化的配置,比如css提前,圖片資源解析等等,可以參考官網進行相應配置。說一下我在搭建過程中遇到認為比較需要注意的一點就是,要注意版本差異。很多地方會由於webpack版本差異存在寫法不同,也可能依賴包之間會存在衝突。然後npm下載依賴的時候應不應該帶引數,建議去npm官網package下看推薦,願我們都在踩坑的過程中越戰越勇,一起進步!

相關文章