一、模塊化體系:ESM vs CJS 深入
1.語法與靜態性
(1)ESM:靜態語法,可被打包器做 Tree-shaking
export function play() {}
export default ...
import { play } from './mod.js'
(2)CJS:運行時 require() , 分析能力弱,不利于 Tree-shaking
2.Node 解析規則
(1)package.json 有 "type": "module"?→ .js 當 ESM,CJS 用 .cjs
(2)沒有"type":"moudle"?→ .js 當 CJS,ESM 用 .mjs
(3)ESM模式下導入需帶后綴:import './mod.js'
3.互操作常見坑
(1)從 CJS 默認導出到 ESM: import pkg from 'cjs-pkg' 可能需要 default ,視包而定
(2)ESM的頂層 await import() 可用于懶加載(瀏覽器需<script type="module">)
4.瀏覽器直載 ESM:<script type="module" src="/src/index.js"> 可工作,但生產一般仍用打包器做壓縮/緩存/分隔
二、npm 與依賴管理
1.依賴分層
(1) dependencies : 運行時要用(瀏覽器/Node 產物實際需要)
(2) devDependencies:僅開發/構建/測試(如 webpack 、typescript )
2.版本策略:^1.2.3:允許次/補丁更新;~1.2.3:只允許補丁更新。線上穩定期可固定版本
3.腳本編排: build/dev/test/lint/format 統一入口,CI 直接調用,冪等可重試
4.鎖文件:固定依賴樹,保證本地與 CI 一致性;發布或回歸時避免“幽靈問題”
三、打包器的價值與核心概念
1.為什么要打包
(1)體積與性能:壓縮、去未用代碼、緩存友好命名
(2)兼容性與生態:TS/SCSS/圖片/字體/JSON 等統一處理與優化
(3)架構能力:代碼分割(按需)、預渲染、資源內聯/懶載
2.Tree-shaking:需要 ESM 靜態導入;包需標記 sideEffects:false(或為有副作用文件列白名單)
3.代碼分割
(1)靜態多入口:
entry: { app: '...', admin: '...' }
(2)動態導入:
import('./scenes/scene1_birth')
?→ 自動生成獨立 chunk
4.緩存策略
(1)文件名包含 [contenthash] ,瀏覽器長期緩存;HTML/入口負責指向最新名
(2)依賴庫與業務分離提升復用緩存命中率
四、Webpack 入門到實踐要點
1.最小依賴
webpack
webpack-cli
typescript
ts-loader
2.基本配置點
entry/output
filename: [name].[contenthash].js
clean: true
resolve.extensions: ['.ts', '.js']
module.rules
ts-loader
.ts
optimization.splitChunks: { chunks: 'all' }
3.動態導入分包
ts
// 假設放在 src/index.ts document.querySelector('#playScene1')?.addEventListener('click', async () => {const mod = await import('./scenes/scene1_birth'); // 產出獨立 chunkmod.play(); });
4.副作用控制:
package.json
"sideEffects": false
5.資源處理:簡單可使用 asset/resource (內置)或拷貝插件,把 assets/ 拷到 build/assets/
6.HTML輸出:html-webpack-plugin 自動注入最新 hash 腳本,避免手改文件名
五、Vite / esbuild 對比
1.Vite (推薦開發體驗)
(1)Dev:原生 ESM + 極速 HMR
(2)Build:走 Rollup,分包/CSS/資源生態成熟
(3)TS支持好、配置輕
2. esbuild (極快):打包速度快;復雜生態與 HTML 注入等需額外方案
3.Webpack(覆蓋面最全):復雜/歷史項目強;配置相對繁瑣,Dev 體驗不如 Vite