webpack 設定已經備妥,現在剩下啟動打包的指令、以及你可能需要一點小工具幫你把放在 ./public 中的靜態資源(比如 favicon)複製一份到輸出資料夾中。今天就來介紹個人慣用的腳本內容。
打包
Makefile 腳本
.PHONY: buildbuild: node -r esbuild-runner/register ./script/build.tsscript/build.ts 內容
個人基本上會將打包邏輯放在 ./script/build.ts 中,而這個檔案會處理以下三件事情:
- 啟動
webpack進行打包 - 將
./public中的靜態資源複製一份到打包完的目的資料夾 - (在專案內容比較簡單的時候)手動產生一份
sitemap.xml並灌到打包完的資料夾中
先看看第一步「透過 webpack 進行打包」的功能:
import webpackProductionConfig from "@/config/webpack.config.production";import webpack from "webpack";
function webpackBuild(): Promise<void> { return new Promise((resolve, reject) => { const compiler = webpack(webpackProductionConfig); compiler.run((error, stats) => { if (error) { console.error(error); return reject(error); } if (stats) { // 將毫秒轉回秒 console.info("build time: ", (stats.endTime - stats.startTime) / 1000); // 印出打包過程 log console.info("build log: ", stats.toString()); } if (stats && stats.hasErrors()) { return reject(stats.compilation.errors); } compiler.close((closeError) => { if (closeError) { console.error(closeError); return reject(closeError); } else { return resolve(); } }); }); });}將 webpackProductionConfig 傳入 webpack() 建立打包用的 compiler 並啟動之。在有錯誤發生時,執行 reject() 把錯誤拋出。中間讀取 stats.endTime / stats.startTime 來顯示打包時長。其他打包過程中出現的資訊則透過 stats.toString() 直接輸出到終端。
如果萬事順利,則進入 compiler.close() callback 中的的 return resolve(); 分之,結束 webpack 打包流程。
接著來將 ./public 中除了 index.html 以外的內容都複製一份到打包完畢的目的地資料夾:
import env from "@/config/data/env";import type { EnvForBuildApp } from "@/config/data/types";import { resolvePath } from "@/tool/resolvePath";import fs from "fs-extra";
const ENV_FOR_BUILD_APP = env["process.env"] as EnvForBuildApp;const BUILD_DESTINATION_PATH = resolvePath( JSON.parse(ENV_FOR_BUILD_APP.BUILD_DESTINATION),);
function copyPublicAsset() { fs.copySync(resolvePath("public"), BUILD_DESTINATION_PATH, { filter: (file) => file !== resolvePath("public/index.html"), }); console.info("copy public folder done.");}透過 filter 跳過 index.html 是因為這份檔案已經透過 webpack 的 html-webpack-plugin 處理了,我們不需要在這裡再複製一份。關於 fx-extra 中的 copySync 詳細說明可參考官方文件。
而在 React app 結構較簡單的情況下,個人會在處理打包時順便將 sitemap 一起做掉。
function generateSiteMap() { const content = `<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://example.com/</loc><lastmod>${new Date().toISOString()}</lastmod></url>
</urlset>`; fs.writeFileSync(`${BUILD_DESTINATION_PATH}/sitemap_index.xml`, content); console.log("sitemap.xml generated done.");}上方 xml 內容參考自 Google Search Center: Build and submit a sitemap
sitemap 的功能基本上就是在告訴搜尋引擎「這個網站包含哪些內容」。需要更近一步的介紹可看看 Google 的 Learn about sitemaps 一文。
最後,把這三個功能包起來執行即可:
async function buildProduction() { console.info("build start."); await webpackBuild(); copyPublicAsset(); generateSiteMap(); console.info("build end.");}
buildProduction();預覽
個人習慣在打包完畢後,檢查包完的內容是否真的符合預期(比如檔案間互相引用的路徑在透過 webpack 處理後是否還正確、樣式是否能正常顯示)。以下是 Makefile 腳本:
# 預覽打包後的結果.PHONY: previewpreview: npx http-server $(BUILD_DESTINATION) -p $(APP_PORT)(BUILD_DESTINATION 與 APP_PORT 都是引用自 .evn 的內容,忘記這邊是怎麼來的可以回頭參考第 6 天內容)
在終端輸入 make preview 即可在本機試玩打包後的成果。
恭喜,你的專案已經交給 devOps 來準備上線了 (*゚ー゚)b