[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"knowledge-\u002Fapi\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fadvanced":3,"knowledge-related-\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fadvanced":2779,"knowledge-series-\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fadvanced":2803},{"source":4,"connector":5,"page":6},"sqlite","better-sqlite3",{"path":7,"title":8,"description":9,"category":10,"tags":11,"date":14,"pinned":15,"draft":15,"body":16,"seo":2775,"stem":2776,"id":2777,"extension":2778},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fadvanced","进阶配置","Webpack 进阶场景：多页面（MPA）、devServer、环境变量注入、Source Map、Module Federation 等。基础见 工作原理。","技能小册",[12,13],"webpack","advanced","2021-10-18",false,{"type":17,"value":18,"toc":2766},"minimark",[19,22,32,37,40,54,518,525,536,765,781,785,789,792,962,966,972,989,994,1025,1028,1126,1130,1172,1503,1507,1518,1571,1581,1690,1694,1701,1707,1748,1755,1759,1765,1937,1942,1962,2033,2037,2040,2050,2066,2071,2188,2193,2330,2338,2343,2496,2529,2533,2538,2549,2554,2583,2588,2623,2627,2704,2707,2710,2713,2762],[20,21,8],"h1",{"id":8},[23,24,25,26,31],"p",{},"Webpack 进阶场景：多页面（MPA）、devServer、环境变量注入、Source Map、Module Federation 等。基础见 ",[27,28,30],"a",{"href":29},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fworkflow","工作原理","。",[33,34,36],"h3",{"id":35},"一多页面应用配置","一、多页面应用配置",[23,38,39],{},"多页面应用（MPA）需要生成多个 HTML 文件，每个文件对应不同的入口。",[41,42,44,45,49,50,53],"h4",{"id":43},"_1-动态生成多个-entry-与-htmlwebpackplugin-实例","1. 动态生成多个 ",[46,47,48],"code",{},"entry"," 与 ",[46,51,52],{},"HtmlWebpackPlugin"," 实例",[55,56,61],"pre",{"className":57,"code":58,"language":59,"meta":60,"style":60},"language-js shiki shiki-themes material-theme-lighter github-light github-dark","\u002F\u002F webpack.config.js\nconst path = require(\"path\");\nconst HtmlWebpackPlugin = require(\"html-webpack-plugin\");\nconst glob = require(\"glob\");\n\n\u002F\u002F 约定页面目录：src\u002Fpages\u002F下每个子文件夹代表一个页面\nconst pages = glob.sync(\".\u002Fsrc\u002Fpages\u002F*\u002Findex.js\");\nconst entry = {};\nconst htmlPlugins = [];\n\npages.forEach((pagePath) => {\n  const pageName = path.basename(path.dirname(pagePath));\n  entry[pageName] = pagePath;\n  htmlPlugins.push(\n    new HtmlWebpackPlugin({\n      template: `.\u002Fsrc\u002Fpages\u002F${pageName}\u002Findex.html`,\n      filename: `${pageName}.html`,\n      chunks: [pageName, \"vendor\"], \u002F\u002F 指定引入的chunk\n    }),\n  );\n});\n\nmodule.exports = {\n  entry,\n  plugins: [...htmlPlugins],\n};\n","js","",[46,62,63,72,112,137,162,169,175,206,219,234,239,266,303,326,340,353,385,407,439,449,457,466,471,487,494,512],{"__ignoreMap":60},[64,65,68],"span",{"class":66,"line":67},"line",1,[64,69,71],{"class":70},"sutJx","\u002F\u002F webpack.config.js\n",[64,73,75,79,83,87,91,95,99,103,105,108],{"class":66,"line":74},2,[64,76,78],{"class":77},"sbsja","const",[64,80,82],{"class":81},"s_hVV"," path",[64,84,86],{"class":85},"smGrS"," =",[64,88,90],{"class":89},"sGLFI"," require",[64,92,94],{"class":93},"su5hD","(",[64,96,98],{"class":97},"sjJ54","\"",[64,100,102],{"class":101},"s_sjI","path",[64,104,98],{"class":97},[64,106,107],{"class":93},")",[64,109,111],{"class":110},"sP7_E",";\n",[64,113,115,117,120,122,124,126,128,131,133,135],{"class":66,"line":114},3,[64,116,78],{"class":77},[64,118,119],{"class":81}," HtmlWebpackPlugin",[64,121,86],{"class":85},[64,123,90],{"class":89},[64,125,94],{"class":93},[64,127,98],{"class":97},[64,129,130],{"class":101},"html-webpack-plugin",[64,132,98],{"class":97},[64,134,107],{"class":93},[64,136,111],{"class":110},[64,138,140,142,145,147,149,151,153,156,158,160],{"class":66,"line":139},4,[64,141,78],{"class":77},[64,143,144],{"class":81}," glob",[64,146,86],{"class":85},[64,148,90],{"class":89},[64,150,94],{"class":93},[64,152,98],{"class":97},[64,154,155],{"class":101},"glob",[64,157,98],{"class":97},[64,159,107],{"class":93},[64,161,111],{"class":110},[64,163,165],{"class":66,"line":164},5,[64,166,168],{"emptyLinePlaceholder":167},true,"\n",[64,170,172],{"class":66,"line":171},6,[64,173,174],{"class":70},"\u002F\u002F 约定页面目录：src\u002Fpages\u002F下每个子文件夹代表一个页面\n",[64,176,178,180,183,185,187,190,193,195,197,200,202,204],{"class":66,"line":177},7,[64,179,78],{"class":77},[64,181,182],{"class":81}," pages",[64,184,86],{"class":85},[64,186,144],{"class":93},[64,188,189],{"class":110},".",[64,191,192],{"class":89},"sync",[64,194,94],{"class":93},[64,196,98],{"class":97},[64,198,199],{"class":101},".\u002Fsrc\u002Fpages\u002F*\u002Findex.js",[64,201,98],{"class":97},[64,203,107],{"class":93},[64,205,111],{"class":110},[64,207,209,211,214,216],{"class":66,"line":208},8,[64,210,78],{"class":77},[64,212,213],{"class":81}," entry",[64,215,86],{"class":85},[64,217,218],{"class":110}," {};\n",[64,220,222,224,227,229,232],{"class":66,"line":221},9,[64,223,78],{"class":77},[64,225,226],{"class":81}," htmlPlugins",[64,228,86],{"class":85},[64,230,231],{"class":93}," []",[64,233,111],{"class":110},[64,235,237],{"class":66,"line":236},10,[64,238,168],{"emptyLinePlaceholder":167},[64,240,242,245,247,250,252,254,258,260,263],{"class":66,"line":241},11,[64,243,244],{"class":93},"pages",[64,246,189],{"class":110},[64,248,249],{"class":89},"forEach",[64,251,94],{"class":93},[64,253,94],{"class":110},[64,255,257],{"class":256},"s99_P","pagePath",[64,259,107],{"class":110},[64,261,262],{"class":77}," =>",[64,264,265],{"class":110}," {\n",[64,267,269,272,275,277,279,281,284,287,289,291,294,296,298,301],{"class":66,"line":268},12,[64,270,271],{"class":77},"  const",[64,273,274],{"class":81}," pageName",[64,276,86],{"class":85},[64,278,82],{"class":93},[64,280,189],{"class":110},[64,282,283],{"class":89},"basename",[64,285,94],{"class":286},"skxfh",[64,288,102],{"class":93},[64,290,189],{"class":110},[64,292,293],{"class":89},"dirname",[64,295,94],{"class":286},[64,297,257],{"class":93},[64,299,300],{"class":286},"))",[64,302,111],{"class":110},[64,304,306,309,312,315,318,321,324],{"class":66,"line":305},13,[64,307,308],{"class":93},"  entry",[64,310,311],{"class":286},"[",[64,313,314],{"class":93},"pageName",[64,316,317],{"class":286},"] ",[64,319,320],{"class":85},"=",[64,322,323],{"class":93}," pagePath",[64,325,111],{"class":110},[64,327,329,332,334,337],{"class":66,"line":328},14,[64,330,331],{"class":93},"  htmlPlugins",[64,333,189],{"class":110},[64,335,336],{"class":89},"push",[64,338,339],{"class":286},"(\n",[64,341,343,346,348,350],{"class":66,"line":342},15,[64,344,345],{"class":85},"    new",[64,347,119],{"class":89},[64,349,94],{"class":286},[64,351,352],{"class":110},"{\n",[64,354,356,359,362,365,368,371,373,376,379,382],{"class":66,"line":355},16,[64,357,358],{"class":286},"      template",[64,360,361],{"class":110},":",[64,363,364],{"class":97}," `",[64,366,367],{"class":101},".\u002Fsrc\u002Fpages\u002F",[64,369,370],{"class":97},"${",[64,372,314],{"class":93},[64,374,375],{"class":97},"}",[64,377,378],{"class":101},"\u002Findex.html",[64,380,381],{"class":97},"`",[64,383,384],{"class":110},",\n",[64,386,388,391,393,396,398,400,403,405],{"class":66,"line":387},17,[64,389,390],{"class":286},"      filename",[64,392,361],{"class":110},[64,394,395],{"class":97}," `${",[64,397,314],{"class":93},[64,399,375],{"class":97},[64,401,402],{"class":101},".html",[64,404,381],{"class":97},[64,406,384],{"class":110},[64,408,410,413,415,418,420,423,426,429,431,434,436],{"class":66,"line":409},18,[64,411,412],{"class":286},"      chunks",[64,414,361],{"class":110},[64,416,417],{"class":286}," [",[64,419,314],{"class":93},[64,421,422],{"class":110},",",[64,424,425],{"class":97}," \"",[64,427,428],{"class":101},"vendor",[64,430,98],{"class":97},[64,432,433],{"class":286},"]",[64,435,422],{"class":110},[64,437,438],{"class":70}," \u002F\u002F 指定引入的chunk\n",[64,440,442,445,447],{"class":66,"line":441},19,[64,443,444],{"class":110},"    }",[64,446,107],{"class":286},[64,448,384],{"class":110},[64,450,452,455],{"class":66,"line":451},20,[64,453,454],{"class":286},"  )",[64,456,111],{"class":110},[64,458,460,462,464],{"class":66,"line":459},21,[64,461,375],{"class":110},[64,463,107],{"class":93},[64,465,111],{"class":110},[64,467,469],{"class":66,"line":468},22,[64,470,168],{"emptyLinePlaceholder":167},[64,472,474,478,480,483,485],{"class":66,"line":473},23,[64,475,477],{"class":476},"s39Yj","module",[64,479,189],{"class":110},[64,481,482],{"class":476},"exports",[64,484,86],{"class":85},[64,486,265],{"class":110},[64,488,490,492],{"class":66,"line":489},24,[64,491,308],{"class":93},[64,493,384],{"class":110},[64,495,497,500,502,504,507,510],{"class":66,"line":496},25,[64,498,499],{"class":286},"  plugins",[64,501,361],{"class":110},[64,503,417],{"class":93},[64,505,506],{"class":85},"...",[64,508,509],{"class":93},"htmlPlugins]",[64,511,384],{"class":110},[64,513,515],{"class":66,"line":514},26,[64,516,517],{"class":110},"};\n",[41,519,521,522,524],{"id":520},"_2-多页面共享-vendor-chunk","2. 多页面共享 ",[46,523,428],{}," chunk",[23,526,527,528,531,532,535],{},"为了避免每个页面重复打包公共库，使用 ",[46,529,530],{},"splitChunks"," 提取 ",[46,533,534],{},"node_modules"," 中的依赖：",[55,537,539],{"className":57,"code":538,"language":59,"meta":60,"style":60},"optimization: {\n  splitChunks: {\n    cacheGroups: {\n      vendor: {\n        test: \u002F[\\\\\u002F]node_modules[\\\\\u002F]\u002F,\n        name: 'vendor',\n        chunks: 'all',        \u002F\u002F 对所有入口生效\n        priority: 10,\n      },\n      common: {\n        minChunks: 2,          \u002F\u002F 至少被2个入口引用\n        name: 'common',\n        chunks: 'all',\n        priority: 5,\n        reuseExistingChunk: true,\n      },\n    },\n  },\n},\n",[46,540,541,551,560,569,578,615,632,651,664,669,678,693,708,722,733,746,750,755,760],{"__ignoreMap":60},[64,542,543,547,549],{"class":66,"line":67},[64,544,546],{"class":545},"sbgvK","optimization",[64,548,361],{"class":110},[64,550,265],{"class":110},[64,552,553,556,558],{"class":66,"line":74},[64,554,555],{"class":545},"  splitChunks",[64,557,361],{"class":110},[64,559,265],{"class":110},[64,561,562,565,567],{"class":66,"line":114},[64,563,564],{"class":545},"    cacheGroups",[64,566,361],{"class":110},[64,568,265],{"class":110},[64,570,571,574,576],{"class":66,"line":139},[64,572,573],{"class":545},"      vendor",[64,575,361],{"class":110},[64,577,265],{"class":110},[64,579,580,583,585,588,590,594,598,600,603,605,607,609,611,613],{"class":66,"line":164},[64,581,582],{"class":545},"        test",[64,584,361],{"class":110},[64,586,587],{"class":97}," \u002F",[64,589,311],{"class":476},[64,591,593],{"class":592},"sjYin","\\\\",[64,595,597],{"class":596},"stzsN","\u002F",[64,599,433],{"class":476},[64,601,534],{"class":602},"sQRbd",[64,604,311],{"class":476},[64,606,593],{"class":592},[64,608,597],{"class":596},[64,610,433],{"class":476},[64,612,597],{"class":97},[64,614,384],{"class":110},[64,616,617,620,622,625,627,630],{"class":66,"line":171},[64,618,619],{"class":545},"        name",[64,621,361],{"class":110},[64,623,624],{"class":97}," '",[64,626,428],{"class":101},[64,628,629],{"class":97},"'",[64,631,384],{"class":110},[64,633,634,637,639,641,644,646,648],{"class":66,"line":177},[64,635,636],{"class":545},"        chunks",[64,638,361],{"class":110},[64,640,624],{"class":97},[64,642,643],{"class":101},"all",[64,645,629],{"class":97},[64,647,422],{"class":110},[64,649,650],{"class":70},"        \u002F\u002F 对所有入口生效\n",[64,652,653,656,658,662],{"class":66,"line":208},[64,654,655],{"class":545},"        priority",[64,657,361],{"class":110},[64,659,661],{"class":660},"srdBf"," 10",[64,663,384],{"class":110},[64,665,666],{"class":66,"line":221},[64,667,668],{"class":110},"      },\n",[64,670,671,674,676],{"class":66,"line":236},[64,672,673],{"class":545},"      common",[64,675,361],{"class":110},[64,677,265],{"class":110},[64,679,680,683,685,688,690],{"class":66,"line":241},[64,681,682],{"class":545},"        minChunks",[64,684,361],{"class":110},[64,686,687],{"class":660}," 2",[64,689,422],{"class":110},[64,691,692],{"class":70},"          \u002F\u002F 至少被2个入口引用\n",[64,694,695,697,699,701,704,706],{"class":66,"line":268},[64,696,619],{"class":545},[64,698,361],{"class":110},[64,700,624],{"class":97},[64,702,703],{"class":101},"common",[64,705,629],{"class":97},[64,707,384],{"class":110},[64,709,710,712,714,716,718,720],{"class":66,"line":305},[64,711,636],{"class":545},[64,713,361],{"class":110},[64,715,624],{"class":97},[64,717,643],{"class":101},[64,719,629],{"class":97},[64,721,384],{"class":110},[64,723,724,726,728,731],{"class":66,"line":328},[64,725,655],{"class":545},[64,727,361],{"class":110},[64,729,730],{"class":660}," 5",[64,732,384],{"class":110},[64,734,735,738,740,744],{"class":66,"line":342},[64,736,737],{"class":545},"        reuseExistingChunk",[64,739,361],{"class":110},[64,741,743],{"class":742},"syTEX"," true",[64,745,384],{"class":110},[64,747,748],{"class":66,"line":355},[64,749,668],{"class":110},[64,751,752],{"class":66,"line":387},[64,753,754],{"class":110},"    },\n",[64,756,757],{"class":66,"line":409},[64,758,759],{"class":110},"  },\n",[64,761,762],{"class":66,"line":441},[64,763,764],{"class":110},"},\n",[23,766,767,768,770,771,774,775,777,778,780],{},"然后在 ",[46,769,52],{}," 的 ",[46,772,773],{},"chunks"," 数组中包含 ",[46,776,428],{}," 和 ",[46,779,703],{},"，确保生成的 HTML 会引用这些共享 chunk。",[33,782,784],{"id":783},"二开发服务器webpack-dev-server进阶","二、开发服务器（webpack-dev-server）进阶",[41,786,788],{"id":787},"_1-代理配置proxy解决跨域路径重写","1. 代理配置（proxy）解决跨域、路径重写",[23,790,791],{},"开发环境下，前端请求后端 API 时若遇到跨域问题，可以通过 devServer 代理将请求转发到真实服务器。",[55,793,795],{"className":57,"code":794,"language":59,"meta":60,"style":60},"devServer: {\n  proxy: {\n    '\u002Fapi': {\n      target: 'http:\u002F\u002Flocalhost:3000',\n      pathRewrite: { '^\u002Fapi': '' },   \u002F\u002F 将 \u002Fapi\u002Fusers 重写为 \u002Fusers\n      changeOrigin: true,              \u002F\u002F 修改请求头中的 origin\n      secure: false,                   \u002F\u002F 接受 HTTPS 自签名证书\n    },\n    '\u002Fsocket': {\n      target: 'ws:\u002F\u002Flocalhost:8080',\n      ws: true,                        \u002F\u002F 支持 WebSocket 代理\n    },\n  },\n},\n",[46,796,797,806,815,830,846,875,889,904,908,921,936,950,954,958],{"__ignoreMap":60},[64,798,799,802,804],{"class":66,"line":67},[64,800,801],{"class":545},"devServer",[64,803,361],{"class":110},[64,805,265],{"class":110},[64,807,808,811,813],{"class":66,"line":74},[64,809,810],{"class":545},"  proxy",[64,812,361],{"class":110},[64,814,265],{"class":110},[64,816,817,820,823,825,828],{"class":66,"line":114},[64,818,819],{"class":97},"    '",[64,821,822],{"class":101},"\u002Fapi",[64,824,629],{"class":97},[64,826,827],{"class":286},": ",[64,829,352],{"class":110},[64,831,832,835,837,839,842,844],{"class":66,"line":139},[64,833,834],{"class":286},"      target",[64,836,361],{"class":110},[64,838,624],{"class":97},[64,840,841],{"class":101},"http:\u002F\u002Flocalhost:3000",[64,843,629],{"class":97},[64,845,384],{"class":110},[64,847,848,851,853,856,858,862,864,866,869,872],{"class":66,"line":164},[64,849,850],{"class":286},"      pathRewrite",[64,852,361],{"class":110},[64,854,855],{"class":110}," {",[64,857,624],{"class":97},[64,859,861],{"class":860},"sZUrc","^\u002Fapi",[64,863,629],{"class":97},[64,865,361],{"class":110},[64,867,868],{"class":97}," ''",[64,870,871],{"class":110}," },",[64,873,874],{"class":70},"   \u002F\u002F 将 \u002Fapi\u002Fusers 重写为 \u002Fusers\n",[64,876,877,880,882,884,886],{"class":66,"line":171},[64,878,879],{"class":286},"      changeOrigin",[64,881,361],{"class":110},[64,883,743],{"class":742},[64,885,422],{"class":110},[64,887,888],{"class":70},"              \u002F\u002F 修改请求头中的 origin\n",[64,890,891,894,896,899,901],{"class":66,"line":177},[64,892,893],{"class":286},"      secure",[64,895,361],{"class":110},[64,897,898],{"class":742}," false",[64,900,422],{"class":110},[64,902,903],{"class":70},"                   \u002F\u002F 接受 HTTPS 自签名证书\n",[64,905,906],{"class":66,"line":208},[64,907,754],{"class":110},[64,909,910,912,915,917,919],{"class":66,"line":221},[64,911,819],{"class":97},[64,913,914],{"class":101},"\u002Fsocket",[64,916,629],{"class":97},[64,918,827],{"class":286},[64,920,352],{"class":110},[64,922,923,925,927,929,932,934],{"class":66,"line":236},[64,924,834],{"class":286},[64,926,361],{"class":110},[64,928,624],{"class":97},[64,930,931],{"class":101},"ws:\u002F\u002Flocalhost:8080",[64,933,629],{"class":97},[64,935,384],{"class":110},[64,937,938,941,943,945,947],{"class":66,"line":241},[64,939,940],{"class":286},"      ws",[64,942,361],{"class":110},[64,944,743],{"class":742},[64,946,422],{"class":110},[64,948,949],{"class":70},"                        \u002F\u002F 支持 WebSocket 代理\n",[64,951,952],{"class":66,"line":268},[64,953,754],{"class":110},[64,955,956],{"class":66,"line":305},[64,957,759],{"class":110},[64,959,960],{"class":66,"line":328},[64,961,764],{"class":110},[41,963,965],{"id":964},"_2-热更新hmr原理","2. 热更新（HMR）原理",[23,967,968,971],{},[969,970,30],"strong",{},"：",[973,974,975,983,986],"ul",{},[976,977,978,979,982],"li",{},"构建时，",[46,980,981],{},"HotModuleReplacementPlugin"," 给模块添加 HMR runtime 代码。",[976,984,985],{},"运行时，通过 WebSocket 与 devServer 通信，接收模块更新通知。",[976,987,988],{},"当某个模块更新时，客户端只请求变更的 chunk，并执行模块的热替换逻辑（需要模块自身支持，如 Vue、React 的 HMR API）。",[23,990,991,971],{},[969,992,993],{},"启用 HMR",[55,995,997],{"className":57,"code":996,"language":59,"meta":60,"style":60},"devServer: {\n  hot: true,   \u002F\u002F 启用 HMR，自动注入 HotModuleReplacementPlugin\n},\n",[46,998,999,1007,1021],{"__ignoreMap":60},[64,1000,1001,1003,1005],{"class":66,"line":67},[64,1002,801],{"class":545},[64,1004,361],{"class":110},[64,1006,265],{"class":110},[64,1008,1009,1012,1014,1016,1018],{"class":66,"line":74},[64,1010,1011],{"class":545},"  hot",[64,1013,361],{"class":110},[64,1015,743],{"class":742},[64,1017,422],{"class":110},[64,1019,1020],{"class":70},"   \u002F\u002F 启用 HMR，自动注入 HotModuleReplacementPlugin\n",[64,1022,1023],{"class":66,"line":114},[64,1024,764],{"class":110},[23,1026,1027],{},"自定义模块热接受：",[55,1029,1031],{"className":57,"code":1030,"language":59,"meta":60,"style":60},"if (module.hot) {\n  module.hot.accept(\".\u002Fmodule.js\", () => {\n    console.log(\"module 更新了\");\n    \u002F\u002F 重新执行副作用\n  });\n}\n",[46,1032,1033,1051,1084,1107,1112,1121],{"__ignoreMap":60},[64,1034,1035,1039,1042,1044,1046,1049],{"class":66,"line":67},[64,1036,1038],{"class":1037},"sVHd0","if",[64,1040,1041],{"class":93}," (",[64,1043,477],{"class":476},[64,1045,189],{"class":110},[64,1047,1048],{"class":93},"hot) ",[64,1050,352],{"class":110},[64,1052,1053,1056,1058,1061,1063,1066,1068,1070,1073,1075,1077,1080,1082],{"class":66,"line":74},[64,1054,1055],{"class":476},"  module",[64,1057,189],{"class":110},[64,1059,1060],{"class":93},"hot",[64,1062,189],{"class":110},[64,1064,1065],{"class":89},"accept",[64,1067,94],{"class":286},[64,1069,98],{"class":97},[64,1071,1072],{"class":101},".\u002Fmodule.js",[64,1074,98],{"class":97},[64,1076,422],{"class":110},[64,1078,1079],{"class":110}," ()",[64,1081,262],{"class":77},[64,1083,265],{"class":110},[64,1085,1086,1089,1091,1094,1096,1098,1101,1103,1105],{"class":66,"line":114},[64,1087,1088],{"class":93},"    console",[64,1090,189],{"class":110},[64,1092,1093],{"class":89},"log",[64,1095,94],{"class":286},[64,1097,98],{"class":97},[64,1099,1100],{"class":101},"module 更新了",[64,1102,98],{"class":97},[64,1104,107],{"class":286},[64,1106,111],{"class":110},[64,1108,1109],{"class":66,"line":139},[64,1110,1111],{"class":70},"    \u002F\u002F 重新执行副作用\n",[64,1113,1114,1117,1119],{"class":66,"line":164},[64,1115,1116],{"class":110},"  }",[64,1118,107],{"class":286},[64,1120,111],{"class":110},[64,1122,1123],{"class":66,"line":171},[64,1124,1125],{"class":110},"}\n",[41,1127,1129],{"id":1128},"_3-自定义-dev-server-中间件","3. 自定义 dev-server 中间件",[23,1131,1132,1135,1136,1139,1140,1135,1143,1146,1147,1150,1151,1154,1155,1158,1159,1161,1162,1165,1166,1168,1169,31],{},[46,1133,1134],{},"onBeforeSetupMiddleware"," \u002F ",[46,1137,1138],{},"onAfterSetupMiddleware","（v3 之前为 ",[46,1141,1142],{},"before",[46,1144,1145],{},"after","）已在 ",[969,1148,1149],{},"webpack-dev-server v5 中移除","，统一改用 ",[46,1152,1153],{},"setupMiddlewares","：用 ",[46,1156,1157],{},"middlewares.unshift(...)"," 在内部中间件之前注入（对应旧的 ",[46,1160,1134],{},"），用 ",[46,1163,1164],{},"middlewares.push(...)"," 在之后注入（对应旧的 ",[46,1167,1138],{},"），并需要 ",[46,1170,1171],{},"return middlewares",[55,1173,1175],{"className":57,"code":1174,"language":59,"meta":60,"style":60},"devServer: {\n  setupMiddlewares: (middlewares, devServer) => {\n    if (!devServer) {\n      throw new Error(\"webpack-dev-server is not defined\");\n    }\n    \u002F\u002F 对应旧的 onBeforeSetupMiddleware：在内部中间件之前执行\n    middlewares.unshift((req, res, next) => {\n      if (req.path === \"\u002Fcustom\") {\n        return res.json({ custom: \"response\" });\n      }\n      next();\n    });\n    \u002F\u002F 对应旧的 onAfterSetupMiddleware：在内部中间件之后执行\n    middlewares.push((req, res, next) => {\n      console.log(`[${new Date()}] ${req.method} ${req.url}`);\n      next();\n    });\n    return middlewares;\n  },\n},\n",[46,1176,1177,1185,1208,1225,1249,1254,1259,1292,1319,1355,1360,1370,1378,1383,1411,1469,1477,1485,1495,1499],{"__ignoreMap":60},[64,1178,1179,1181,1183],{"class":66,"line":67},[64,1180,801],{"class":545},[64,1182,361],{"class":110},[64,1184,265],{"class":110},[64,1186,1187,1190,1192,1194,1197,1199,1202,1204,1206],{"class":66,"line":74},[64,1188,1189],{"class":545},"  setupMiddlewares",[64,1191,361],{"class":110},[64,1193,1041],{"class":110},[64,1195,1196],{"class":256},"middlewares",[64,1198,422],{"class":110},[64,1200,1201],{"class":256}," devServer",[64,1203,107],{"class":110},[64,1205,262],{"class":77},[64,1207,265],{"class":110},[64,1209,1210,1213,1215,1218,1220,1223],{"class":66,"line":114},[64,1211,1212],{"class":1037},"    if",[64,1214,1041],{"class":286},[64,1216,1217],{"class":85},"!",[64,1219,801],{"class":93},[64,1221,1222],{"class":286},") ",[64,1224,352],{"class":110},[64,1226,1227,1230,1233,1236,1238,1240,1243,1245,1247],{"class":66,"line":139},[64,1228,1229],{"class":1037},"      throw",[64,1231,1232],{"class":85}," new",[64,1234,1235],{"class":89}," Error",[64,1237,94],{"class":286},[64,1239,98],{"class":97},[64,1241,1242],{"class":101},"webpack-dev-server is not defined",[64,1244,98],{"class":97},[64,1246,107],{"class":286},[64,1248,111],{"class":110},[64,1250,1251],{"class":66,"line":164},[64,1252,1253],{"class":110},"    }\n",[64,1255,1256],{"class":66,"line":171},[64,1257,1258],{"class":70},"    \u002F\u002F 对应旧的 onBeforeSetupMiddleware：在内部中间件之前执行\n",[64,1260,1261,1264,1266,1269,1271,1273,1276,1278,1281,1283,1286,1288,1290],{"class":66,"line":177},[64,1262,1263],{"class":93},"    middlewares",[64,1265,189],{"class":110},[64,1267,1268],{"class":89},"unshift",[64,1270,94],{"class":286},[64,1272,94],{"class":110},[64,1274,1275],{"class":256},"req",[64,1277,422],{"class":110},[64,1279,1280],{"class":256}," res",[64,1282,422],{"class":110},[64,1284,1285],{"class":256}," next",[64,1287,107],{"class":110},[64,1289,262],{"class":77},[64,1291,265],{"class":110},[64,1293,1294,1297,1299,1301,1303,1305,1308,1310,1313,1315,1317],{"class":66,"line":208},[64,1295,1296],{"class":1037},"      if",[64,1298,1041],{"class":286},[64,1300,1275],{"class":93},[64,1302,189],{"class":110},[64,1304,102],{"class":93},[64,1306,1307],{"class":85}," ===",[64,1309,425],{"class":97},[64,1311,1312],{"class":101},"\u002Fcustom",[64,1314,98],{"class":97},[64,1316,1222],{"class":286},[64,1318,352],{"class":110},[64,1320,1321,1324,1326,1328,1331,1333,1336,1339,1341,1343,1346,1348,1351,1353],{"class":66,"line":221},[64,1322,1323],{"class":1037},"        return",[64,1325,1280],{"class":93},[64,1327,189],{"class":110},[64,1329,1330],{"class":89},"json",[64,1332,94],{"class":286},[64,1334,1335],{"class":110},"{",[64,1337,1338],{"class":286}," custom",[64,1340,361],{"class":110},[64,1342,425],{"class":97},[64,1344,1345],{"class":101},"response",[64,1347,98],{"class":97},[64,1349,1350],{"class":110}," }",[64,1352,107],{"class":286},[64,1354,111],{"class":110},[64,1356,1357],{"class":66,"line":236},[64,1358,1359],{"class":110},"      }\n",[64,1361,1362,1365,1368],{"class":66,"line":241},[64,1363,1364],{"class":89},"      next",[64,1366,1367],{"class":286},"()",[64,1369,111],{"class":110},[64,1371,1372,1374,1376],{"class":66,"line":268},[64,1373,444],{"class":110},[64,1375,107],{"class":286},[64,1377,111],{"class":110},[64,1379,1380],{"class":66,"line":305},[64,1381,1382],{"class":70},"    \u002F\u002F 对应旧的 onAfterSetupMiddleware：在内部中间件之后执行\n",[64,1384,1385,1387,1389,1391,1393,1395,1397,1399,1401,1403,1405,1407,1409],{"class":66,"line":328},[64,1386,1263],{"class":93},[64,1388,189],{"class":110},[64,1390,336],{"class":89},[64,1392,94],{"class":286},[64,1394,94],{"class":110},[64,1396,1275],{"class":256},[64,1398,422],{"class":110},[64,1400,1280],{"class":256},[64,1402,422],{"class":110},[64,1404,1285],{"class":256},[64,1406,107],{"class":110},[64,1408,262],{"class":77},[64,1410,265],{"class":110},[64,1412,1413,1416,1418,1420,1422,1424,1426,1428,1431,1434,1437,1439,1441,1443,1445,1447,1450,1452,1455,1457,1459,1462,1465,1467],{"class":66,"line":342},[64,1414,1415],{"class":93},"      console",[64,1417,189],{"class":110},[64,1419,1093],{"class":89},[64,1421,94],{"class":286},[64,1423,381],{"class":97},[64,1425,311],{"class":101},[64,1427,370],{"class":97},[64,1429,1430],{"class":85},"new",[64,1432,1433],{"class":89}," Date",[64,1435,1367],{"class":1436},"sfo-9",[64,1438,375],{"class":97},[64,1440,317],{"class":101},[64,1442,370],{"class":97},[64,1444,1275],{"class":93},[64,1446,189],{"class":97},[64,1448,1449],{"class":93},"method",[64,1451,375],{"class":97},[64,1453,1454],{"class":97}," ${",[64,1456,1275],{"class":93},[64,1458,189],{"class":97},[64,1460,1461],{"class":93},"url",[64,1463,1464],{"class":97},"}`",[64,1466,107],{"class":286},[64,1468,111],{"class":110},[64,1470,1471,1473,1475],{"class":66,"line":355},[64,1472,1364],{"class":89},[64,1474,1367],{"class":286},[64,1476,111],{"class":110},[64,1478,1479,1481,1483],{"class":66,"line":387},[64,1480,444],{"class":110},[64,1482,107],{"class":286},[64,1484,111],{"class":110},[64,1486,1487,1490,1493],{"class":66,"line":409},[64,1488,1489],{"class":1037},"    return",[64,1491,1492],{"class":93}," middlewares",[64,1494,111],{"class":110},[64,1496,1497],{"class":66,"line":441},[64,1498,759],{"class":110},[64,1500,1501],{"class":66,"line":451},[64,1502,764],{"class":110},[33,1504,1506],{"id":1505},"三环境变量注入","三、环境变量注入",[41,1508,1510,1511,1514,1515],{"id":1509},"_1-defineplugin-vs-environmentplugin","1. ",[46,1512,1513],{},"DefinePlugin"," vs ",[46,1516,1517],{},"EnvironmentPlugin",[1519,1520,1521,1537],"table",{},[1522,1523,1524],"thead",{},[1525,1526,1527,1531,1534],"tr",{},[1528,1529,1530],"th",{},"插件",[1528,1532,1533],{},"作用",[1528,1535,1536],{},"用法",[1538,1539,1540,1555],"tbody",{},[1525,1541,1542,1547,1550],{},[1543,1544,1545],"td",{},[46,1546,1513],{},[1543,1548,1549],{},"定义全局常量，可用于任意字符串替换",[1543,1551,1552],{},[46,1553,1554],{},"new webpack.DefinePlugin({ 'process.env.NODE_ENV': JSON.stringify('production') })",[1525,1556,1557,1561,1566],{},[1543,1558,1559],{},[46,1560,1517],{},[1543,1562,1563,1564],{},"读取系统环境变量并注入到代码中，底层仍使用 ",[46,1565,1513],{},[1543,1567,1568],{},[46,1569,1570],{},"new webpack.EnvironmentPlugin(['NODE_ENV', 'API_BASE'])",[23,1572,1573,1576,1577,1580],{},[969,1574,1575],{},"注意","：传入的值会被 JavaScript 表达式求值，因此必须使用 ",[46,1578,1579],{},"JSON.stringify"," 包裹字符串，否则会被当成变量名。",[55,1582,1584],{"className":57,"code":1583,"language":59,"meta":60,"style":60},"new webpack.DefinePlugin({\n  __VERSION__: JSON.stringify(\"1.0.0\"),\n  __API_BASE__: process.env.API_BASE ? JSON.stringify(process.env.API_BASE) : '\"\u002Fapi\"',\n});\n",[46,1585,1586,1601,1629,1682],{"__ignoreMap":60},[64,1587,1588,1590,1593,1595,1597,1599],{"class":66,"line":67},[64,1589,1430],{"class":85},[64,1591,1592],{"class":93}," webpack",[64,1594,189],{"class":110},[64,1596,1513],{"class":89},[64,1598,94],{"class":93},[64,1600,352],{"class":110},[64,1602,1603,1606,1608,1611,1613,1616,1618,1620,1623,1625,1627],{"class":66,"line":74},[64,1604,1605],{"class":286},"  __VERSION__",[64,1607,361],{"class":110},[64,1609,1610],{"class":81}," JSON",[64,1612,189],{"class":110},[64,1614,1615],{"class":89},"stringify",[64,1617,94],{"class":93},[64,1619,98],{"class":97},[64,1621,1622],{"class":101},"1.0.0",[64,1624,98],{"class":97},[64,1626,107],{"class":93},[64,1628,384],{"class":110},[64,1630,1631,1634,1636,1639,1641,1644,1646,1649,1652,1654,1656,1658,1661,1663,1665,1667,1669,1671,1673,1675,1678,1680],{"class":66,"line":114},[64,1632,1633],{"class":286},"  __API_BASE__",[64,1635,361],{"class":110},[64,1637,1638],{"class":93}," process",[64,1640,189],{"class":110},[64,1642,1643],{"class":93},"env",[64,1645,189],{"class":110},[64,1647,1648],{"class":81},"API_BASE",[64,1650,1651],{"class":85}," ?",[64,1653,1610],{"class":81},[64,1655,189],{"class":110},[64,1657,1615],{"class":89},[64,1659,1660],{"class":93},"(process",[64,1662,189],{"class":110},[64,1664,1643],{"class":93},[64,1666,189],{"class":110},[64,1668,1648],{"class":81},[64,1670,1222],{"class":93},[64,1672,361],{"class":85},[64,1674,624],{"class":97},[64,1676,1677],{"class":101},"\"\u002Fapi\"",[64,1679,629],{"class":97},[64,1681,384],{"class":110},[64,1683,1684,1686,1688],{"class":66,"line":139},[64,1685,375],{"class":110},[64,1687,107],{"class":93},[64,1689,111],{"class":110},[41,1691,1693],{"id":1692},"_2-类型安全的全局变量注入配合-typescript","2. 类型安全的全局变量注入（配合 TypeScript）",[23,1695,1696,1697,1700],{},"如果在代码中使用了全局变量（如 ",[46,1698,1699],{},"__API_BASE__","），TypeScript 会报错，需要声明其类型。",[23,1702,1703,1704,971],{},"创建 ",[46,1705,1706],{},"global.d.ts",[55,1708,1712],{"className":1709,"code":1710,"language":1711,"meta":60,"style":60},"language-ts shiki shiki-themes material-theme-lighter github-light github-dark","declare const __API_BASE__: string;\ndeclare const __VERSION__: string;\n","ts",[46,1713,1714,1733],{"__ignoreMap":60},[64,1715,1716,1719,1722,1725,1727,1731],{"class":66,"line":67},[64,1717,1718],{"class":77},"declare",[64,1720,1721],{"class":77}," const",[64,1723,1724],{"class":81}," __API_BASE__",[64,1726,361],{"class":85},[64,1728,1730],{"class":1729},"sZMiF"," string",[64,1732,111],{"class":110},[64,1734,1735,1737,1739,1742,1744,1746],{"class":66,"line":74},[64,1736,1718],{"class":77},[64,1738,1721],{"class":77},[64,1740,1741],{"class":81}," __VERSION__",[64,1743,361],{"class":85},[64,1745,1730],{"class":1729},[64,1747,111],{"class":110},[23,1749,1750,1751,1754],{},"然后在任意 ",[46,1752,1753],{},".ts"," 文件中便可直接使用，类型安全。",[33,1756,1758],{"id":1757},"四source-map-策略","四、source map 策略",[23,1760,1761,1764],{},[46,1762,1763],{},"devtool"," 选项决定了 source map 的生成方式，需要在构建速度、重建速度、调试体验之间权衡。",[1519,1766,1767,1789],{},[1522,1768,1769],{},[1525,1770,1771,1774,1777,1780,1783,1786],{},[1528,1772,1773],{},"devtool 类型",[1528,1775,1776],{},"构建速度",[1528,1778,1779],{},"重建速度",[1528,1781,1782],{},"生产环境",[1528,1784,1785],{},"调试体验",[1528,1787,1788],{},"说明",[1538,1790,1791,1815,1835,1855,1880,1899,1918],{},[1525,1792,1793,1798,1801,1803,1806,1809],{},[1543,1794,1795],{},[46,1796,1797],{},"eval",[1543,1799,1800],{},"最快",[1543,1802,1800],{},[1543,1804,1805],{},"❌",[1543,1807,1808],{},"一般",[1543,1810,1811,1812,1814],{},"每个模块用 ",[46,1813,1797],{}," 执行，报错只显示行号，无源码映射",[1525,1816,1817,1822,1825,1827,1829,1832],{},[1543,1818,1819],{},[46,1820,1821],{},"eval-cheap-source-map",[1543,1823,1824],{},"快",[1543,1826,1824],{},[1543,1828,1805],{},[1543,1830,1831],{},"较好",[1543,1833,1834],{},"每行映射，不包含列信息，丢失原始源文件（loader 转换后）",[1525,1836,1837,1842,1845,1847,1849,1852],{},[1543,1838,1839],{},[46,1840,1841],{},"eval-cheap-module-source-map",[1543,1843,1844],{},"中等",[1543,1846,1844],{},[1543,1848,1805],{},[1543,1850,1851],{},"好",[1543,1853,1854],{},"包含列信息，保留 loader 处理后的源码映射，开发推荐",[1525,1856,1857,1862,1865,1867,1870,1873],{},[1543,1858,1859],{},[46,1860,1861],{},"source-map",[1543,1863,1864],{},"慢",[1543,1866,1864],{},[1543,1868,1869],{},"✅",[1543,1871,1872],{},"最佳",[1543,1874,1875,1876,1879],{},"生成完整独立 ",[46,1877,1878],{},".map"," 文件，减速构建，生产可用但不推荐（暴露源码）",[1525,1881,1882,1887,1889,1891,1893,1896],{},[1543,1883,1884],{},[46,1885,1886],{},"hidden-source-map",[1543,1888,1864],{},[1543,1890,1864],{},[1543,1892,1869],{},[1543,1894,1895],{},"不直接",[1543,1897,1898],{},"生成 source map 但不关联，用于错误上报平台",[1525,1900,1901,1906,1908,1910,1912,1915],{},[1543,1902,1903],{},[46,1904,1905],{},"nosources-source-map",[1543,1907,1864],{},[1543,1909,1864],{},[1543,1911,1869],{},[1543,1913,1914],{},"有限",[1543,1916,1917],{},"只显示堆栈位置，不包含源代码内容，适合生产环境",[1525,1919,1920,1925,1928,1930,1932,1934],{},[1543,1921,1922],{},[46,1923,1924],{},"cheap-module-source-map",[1543,1926,1927],{},"较慢",[1543,1929,1927],{},[1543,1931,1869],{},[1543,1933,1831],{},[1543,1935,1936],{},"不包含列，但保留原始源码，适合生产调试",[23,1938,1939,971],{},[969,1940,1941],{},"推荐组合",[973,1943,1944,1952],{},[976,1945,1946,971,1949,1951],{},[969,1947,1948],{},"开发环境",[46,1950,1841],{},"（快速重建，调试友好）",[976,1953,1954,971,1956,1958,1959,1961],{},[969,1955,1782],{},[46,1957,1886],{}," 或 ",[46,1960,1905],{},"（不暴露源码，但可提供错误追踪）",[55,1963,1965],{"className":57,"code":1964,"language":59,"meta":60,"style":60},"module.exports = {\n  devtool:\n    process.env.NODE_ENV === \"production\" ? \"hidden-source-map\" : \"eval-cheap-module-source-map\",\n};\n",[46,1966,1967,1979,1987,2029],{"__ignoreMap":60},[64,1968,1969,1971,1973,1975,1977],{"class":66,"line":67},[64,1970,477],{"class":476},[64,1972,189],{"class":110},[64,1974,482],{"class":476},[64,1976,86],{"class":85},[64,1978,265],{"class":110},[64,1980,1981,1984],{"class":66,"line":74},[64,1982,1983],{"class":286},"  devtool",[64,1985,1986],{"class":110},":\n",[64,1988,1989,1992,1994,1996,1998,2001,2003,2005,2008,2010,2012,2014,2016,2018,2021,2023,2025,2027],{"class":66,"line":114},[64,1990,1991],{"class":93},"    process",[64,1993,189],{"class":110},[64,1995,1643],{"class":93},[64,1997,189],{"class":110},[64,1999,2000],{"class":81},"NODE_ENV",[64,2002,1307],{"class":85},[64,2004,425],{"class":97},[64,2006,2007],{"class":101},"production",[64,2009,98],{"class":97},[64,2011,1651],{"class":85},[64,2013,425],{"class":97},[64,2015,1886],{"class":101},[64,2017,98],{"class":97},[64,2019,2020],{"class":85}," :",[64,2022,425],{"class":97},[64,2024,1841],{"class":101},[64,2026,98],{"class":97},[64,2028,384],{"class":110},[64,2030,2031],{"class":66,"line":139},[64,2032,517],{"class":110},[33,2034,2036],{"id":2035},"五模块联邦module-federation","五、模块联邦（Module Federation）",[23,2038,2039],{},"模块联邦是 Webpack 5 的核心特性，用于实现微前端架构，允许多个独立构建的应用在运行时共享模块。",[41,2041,1510,2043,49,2046,2049],{"id":2042},"_1-exposes-与-remotes-配置",[46,2044,2045],{},"exposes",[46,2047,2048],{},"remotes"," 配置",[973,2051,2052,2059],{},[976,2053,2054,2058],{},[969,2055,2056],{},[46,2057,2045],{},"：当前应用暴露给其他应用的模块。",[976,2060,2061,2065],{},[969,2062,2063],{},[46,2064,2048],{},"：当前应用引用的远程应用入口。",[23,2067,2068,971],{},[969,2069,2070],{},"host 应用（shell）",[55,2072,2074],{"className":57,"code":2073,"language":59,"meta":60,"style":60},"new ModuleFederationPlugin({\n  name: \"shell\", \u002F\u002F 应用名称，必须唯一\n  remotes: {\n    app1: \"app1@http:\u002F\u002Flocalhost:3001\u002FremoteEntry.js\",\n    app2: \"app2@http:\u002F\u002Flocalhost:3002\u002FremoteEntry.js\",\n  },\n  shared: [\"react\", \"react-dom\"],\n});\n",[46,2075,2076,2087,2106,2115,2131,2147,2151,2180],{"__ignoreMap":60},[64,2077,2078,2080,2083,2085],{"class":66,"line":67},[64,2079,1430],{"class":85},[64,2081,2082],{"class":89}," ModuleFederationPlugin",[64,2084,94],{"class":93},[64,2086,352],{"class":110},[64,2088,2089,2092,2094,2096,2099,2101,2103],{"class":66,"line":74},[64,2090,2091],{"class":286},"  name",[64,2093,361],{"class":110},[64,2095,425],{"class":97},[64,2097,2098],{"class":101},"shell",[64,2100,98],{"class":97},[64,2102,422],{"class":110},[64,2104,2105],{"class":70}," \u002F\u002F 应用名称，必须唯一\n",[64,2107,2108,2111,2113],{"class":66,"line":114},[64,2109,2110],{"class":286},"  remotes",[64,2112,361],{"class":110},[64,2114,265],{"class":110},[64,2116,2117,2120,2122,2124,2127,2129],{"class":66,"line":139},[64,2118,2119],{"class":286},"    app1",[64,2121,361],{"class":110},[64,2123,425],{"class":97},[64,2125,2126],{"class":101},"app1@http:\u002F\u002Flocalhost:3001\u002FremoteEntry.js",[64,2128,98],{"class":97},[64,2130,384],{"class":110},[64,2132,2133,2136,2138,2140,2143,2145],{"class":66,"line":164},[64,2134,2135],{"class":286},"    app2",[64,2137,361],{"class":110},[64,2139,425],{"class":97},[64,2141,2142],{"class":101},"app2@http:\u002F\u002Flocalhost:3002\u002FremoteEntry.js",[64,2144,98],{"class":97},[64,2146,384],{"class":110},[64,2148,2149],{"class":66,"line":171},[64,2150,759],{"class":110},[64,2152,2153,2156,2158,2160,2162,2165,2167,2169,2171,2174,2176,2178],{"class":66,"line":177},[64,2154,2155],{"class":286},"  shared",[64,2157,361],{"class":110},[64,2159,417],{"class":93},[64,2161,98],{"class":97},[64,2163,2164],{"class":101},"react",[64,2166,98],{"class":97},[64,2168,422],{"class":110},[64,2170,425],{"class":97},[64,2172,2173],{"class":101},"react-dom",[64,2175,98],{"class":97},[64,2177,433],{"class":93},[64,2179,384],{"class":110},[64,2181,2182,2184,2186],{"class":66,"line":208},[64,2183,375],{"class":110},[64,2185,107],{"class":93},[64,2187,111],{"class":110},[23,2189,2190,971],{},[969,2191,2192],{},"remote 应用",[55,2194,2196],{"className":57,"code":2195,"language":59,"meta":60,"style":60},"new ModuleFederationPlugin({\n  name: \"app1\",\n  filename: \"remoteEntry.js\", \u002F\u002F 远程入口文件\n  exposes: {\n    \".\u002FButton\": \".\u002Fsrc\u002FButton\",\n    \".\u002Futils\": \".\u002Fsrc\u002Futils\",\n  },\n  shared: [\"react\", \"react-dom\"],\n});\n",[46,2197,2198,2208,2223,2242,2251,2272,2292,2296,2322],{"__ignoreMap":60},[64,2199,2200,2202,2204,2206],{"class":66,"line":67},[64,2201,1430],{"class":85},[64,2203,2082],{"class":89},[64,2205,94],{"class":93},[64,2207,352],{"class":110},[64,2209,2210,2212,2214,2216,2219,2221],{"class":66,"line":74},[64,2211,2091],{"class":286},[64,2213,361],{"class":110},[64,2215,425],{"class":97},[64,2217,2218],{"class":101},"app1",[64,2220,98],{"class":97},[64,2222,384],{"class":110},[64,2224,2225,2228,2230,2232,2235,2237,2239],{"class":66,"line":114},[64,2226,2227],{"class":286},"  filename",[64,2229,361],{"class":110},[64,2231,425],{"class":97},[64,2233,2234],{"class":101},"remoteEntry.js",[64,2236,98],{"class":97},[64,2238,422],{"class":110},[64,2240,2241],{"class":70}," \u002F\u002F 远程入口文件\n",[64,2243,2244,2247,2249],{"class":66,"line":139},[64,2245,2246],{"class":286},"  exposes",[64,2248,361],{"class":110},[64,2250,265],{"class":110},[64,2252,2253,2256,2259,2261,2263,2265,2268,2270],{"class":66,"line":164},[64,2254,2255],{"class":97},"    \"",[64,2257,2258],{"class":860},".\u002FButton",[64,2260,98],{"class":97},[64,2262,361],{"class":110},[64,2264,425],{"class":97},[64,2266,2267],{"class":101},".\u002Fsrc\u002FButton",[64,2269,98],{"class":97},[64,2271,384],{"class":110},[64,2273,2274,2276,2279,2281,2283,2285,2288,2290],{"class":66,"line":171},[64,2275,2255],{"class":97},[64,2277,2278],{"class":860},".\u002Futils",[64,2280,98],{"class":97},[64,2282,361],{"class":110},[64,2284,425],{"class":97},[64,2286,2287],{"class":101},".\u002Fsrc\u002Futils",[64,2289,98],{"class":97},[64,2291,384],{"class":110},[64,2293,2294],{"class":66,"line":177},[64,2295,759],{"class":110},[64,2297,2298,2300,2302,2304,2306,2308,2310,2312,2314,2316,2318,2320],{"class":66,"line":208},[64,2299,2155],{"class":286},[64,2301,361],{"class":110},[64,2303,417],{"class":93},[64,2305,98],{"class":97},[64,2307,2164],{"class":101},[64,2309,98],{"class":97},[64,2311,422],{"class":110},[64,2313,425],{"class":97},[64,2315,2173],{"class":101},[64,2317,98],{"class":97},[64,2319,433],{"class":93},[64,2321,384],{"class":110},[64,2323,2324,2326,2328],{"class":66,"line":221},[64,2325,375],{"class":110},[64,2327,107],{"class":93},[64,2329,111],{"class":110},[41,2331,2333,2334,2337],{"id":2332},"_2-共享依赖shared策略","2. 共享依赖（",[46,2335,2336],{},"shared","）策略",[23,2339,2340,2342],{},[46,2341,2336],{}," 配置用于避免重复加载公共库，同时解决版本冲突。",[55,2344,2346],{"className":57,"code":2345,"language":59,"meta":60,"style":60},"shared: {\n  react: {\n    singleton: true,      \u002F\u002F 全局唯一实例\n    eager: false,         \u002F\u002F 默认 false，不提前加载（懒加载）\n    requiredVersion: '^18.0.0',\n    version: '18.2.0',\n  },\n  'react-dom': {\n    singleton: true,\n    eager: false,\n  },\n  lodash: {\n    singleton: false,     \u002F\u002F 允许多版本共存\n  },\n}\n",[46,2347,2348,2356,2365,2379,2393,2409,2425,2429,2442,2452,2462,2466,2475,2488,2492],{"__ignoreMap":60},[64,2349,2350,2352,2354],{"class":66,"line":67},[64,2351,2336],{"class":545},[64,2353,361],{"class":110},[64,2355,265],{"class":110},[64,2357,2358,2361,2363],{"class":66,"line":74},[64,2359,2360],{"class":545},"  react",[64,2362,361],{"class":110},[64,2364,265],{"class":110},[64,2366,2367,2370,2372,2374,2376],{"class":66,"line":114},[64,2368,2369],{"class":545},"    singleton",[64,2371,361],{"class":110},[64,2373,743],{"class":742},[64,2375,422],{"class":110},[64,2377,2378],{"class":70},"      \u002F\u002F 全局唯一实例\n",[64,2380,2381,2384,2386,2388,2390],{"class":66,"line":139},[64,2382,2383],{"class":545},"    eager",[64,2385,361],{"class":110},[64,2387,898],{"class":742},[64,2389,422],{"class":110},[64,2391,2392],{"class":70},"         \u002F\u002F 默认 false，不提前加载（懒加载）\n",[64,2394,2395,2398,2400,2402,2405,2407],{"class":66,"line":164},[64,2396,2397],{"class":545},"    requiredVersion",[64,2399,361],{"class":110},[64,2401,624],{"class":97},[64,2403,2404],{"class":101},"^18.0.0",[64,2406,629],{"class":97},[64,2408,384],{"class":110},[64,2410,2411,2414,2416,2418,2421,2423],{"class":66,"line":171},[64,2412,2413],{"class":545},"    version",[64,2415,361],{"class":110},[64,2417,624],{"class":97},[64,2419,2420],{"class":101},"18.2.0",[64,2422,629],{"class":97},[64,2424,384],{"class":110},[64,2426,2427],{"class":66,"line":177},[64,2428,759],{"class":110},[64,2430,2431,2434,2436,2438,2440],{"class":66,"line":208},[64,2432,2433],{"class":97},"  '",[64,2435,2173],{"class":101},[64,2437,629],{"class":97},[64,2439,827],{"class":286},[64,2441,352],{"class":110},[64,2443,2444,2446,2448,2450],{"class":66,"line":221},[64,2445,2369],{"class":286},[64,2447,361],{"class":110},[64,2449,743],{"class":742},[64,2451,384],{"class":110},[64,2453,2454,2456,2458,2460],{"class":66,"line":236},[64,2455,2383],{"class":286},[64,2457,361],{"class":110},[64,2459,898],{"class":742},[64,2461,384],{"class":110},[64,2463,2464],{"class":66,"line":241},[64,2465,759],{"class":110},[64,2467,2468,2471,2473],{"class":66,"line":268},[64,2469,2470],{"class":545},"  lodash",[64,2472,361],{"class":110},[64,2474,265],{"class":110},[64,2476,2477,2479,2481,2483,2485],{"class":66,"line":305},[64,2478,2369],{"class":545},[64,2480,361],{"class":110},[64,2482,898],{"class":742},[64,2484,422],{"class":110},[64,2486,2487],{"class":70},"     \u002F\u002F 允许多版本共存\n",[64,2489,2490],{"class":66,"line":328},[64,2491,759],{"class":110},[64,2493,2494],{"class":66,"line":342},[64,2495,1125],{"class":110},[973,2497,2498,2506,2518],{},[976,2499,2500,2505],{},[969,2501,2502],{},[46,2503,2504],{},"singleton","：是否只使用一个共享实例。如果多个应用版本不兼容，Webpack 会在控制台发出警告，并选择最高兼容版本。",[976,2507,2508,2513,2514,2517],{},[969,2509,2510],{},[46,2511,2512],{},"eager","：是否在初始 chunk 中直接加载该共享模块，而不是异步加载。如果设置为 ",[46,2515,2516],{},"true","，可能会增加首屏体积，但避免异步加载的额外请求。",[976,2519,2520,2528],{},[969,2521,2522,1135,2525],{},[46,2523,2524],{},"version",[46,2526,2527],{},"requiredVersion","：用于版本冲突时的匹配。",[41,2530,2532],{"id":2531},"_3-构建时与运行时的模块加载机制","3. 构建时与运行时的模块加载机制",[23,2534,2535,971],{},[969,2536,2537],{},"构建时",[973,2539,2540,2546],{},[976,2541,2542,2543,2545],{},"每个联邦构建会生成一个 ",[46,2544,2234],{}," 文件（或者其他名称），该文件包含了暴露模块的映射以及加载逻辑。",[976,2547,2548],{},"构建时并不解析远程模块的具体代码，只记录其暴露路径和共享依赖关系。",[23,2550,2551,971],{},[969,2552,2553],{},"运行时",[973,2555,2556,2570,2580],{},[976,2557,2558,2559,2562,2563,2565,2566,770,2568,31],{},"当 host 应用执行 ",[46,2560,2561],{},"import('app1\u002FButton')"," 时，Webpack 运行时会根据 ",[46,2564,2048],{}," 配置动态加载 ",[46,2567,2218],{},[46,2569,2234],{},[976,2571,2572,2575,2576,2579],{},[46,2573,2574],{},"remoteEntry"," 中定义了一个 ",[46,2577,2578],{},"__webpack_require__.l"," 动态加载方法，可以按需获取暴露模块的 chunk。",[976,2581,2582],{},"共享依赖的检测：加载远程模块前，运行时会检查本地是否已有匹配的共享模块版本，如果有则直接提供，避免重复加载。",[23,2584,2585,971],{},[969,2586,2587],{},"图示流程",[2589,2590,2591,2601,2607,2616],"ol",{},[976,2592,2593,2594,2597,2598],{},"Host 引用 ",[46,2595,2596],{},"app1\u002FButton"," → 调用 ",[46,2599,2600],{},"__webpack_require__.f.remotes",[976,2602,2603,2604],{},"检查缓存中是否已有该模块，没有则动态加载 ",[46,2605,2606],{},"app1\u002FremoteEntry.js",[976,2608,2609,2610,2612,2613,2615],{},"执行 ",[46,2611,2574],{}," 代码，注册 ",[46,2614,2218],{}," 的模块工厂",[976,2617,2618,2619,2622],{},"从远程模块工厂中获取 ",[46,2620,2621],{},"Button"," 模块，同时处理共享依赖（优先使用 host 提供的版本）",[2624,2625,2626],"h2",{"id":2626},"总结",[1519,2628,2629,2639],{},[1522,2630,2631],{},[1525,2632,2633,2636],{},[1528,2634,2635],{},"配置主题",[1528,2637,2638],{},"核心要点",[1538,2640,2641,2651,2660,2670,2688],{},[1525,2642,2643,2648],{},[1543,2644,2645],{},[969,2646,2647],{},"MPA",[1543,2649,2650],{},"动态生成 entry + HtmlWebpackPlugin，splitChunks 提取共享 vendor",[1525,2652,2653,2657],{},[1543,2654,2655],{},[969,2656,801],{},[1543,2658,2659],{},"proxy 代理跨域，HMR 原理（插件+WebSocket），自定义中间件扩展",[1525,2661,2662,2667],{},[1543,2663,2664],{},[969,2665,2666],{},"环境变量",[1543,2668,2669],{},"DefinePlugin vs EnvironmentPlugin；配合 TypeScript declare 保证类型安全",[1525,2671,2672,2677],{},[1543,2673,2674],{},[969,2675,2676],{},"source map",[1543,2678,2679,2680,2682,2683,1958,2685,2687],{},"开发用 ",[46,2681,1841],{},"，生产用 ",[46,2684,1886],{},[46,2686,1905],{},"，权衡构建速度和调试信息",[1525,2689,2690,2695],{},[1543,2691,2692],{},[969,2693,2694],{},"模块联邦",[1543,2696,2697,2700,2701,2703],{},[46,2698,2699],{},"exposes\u002Fremotes"," 暴露\u002F引用模块，",[46,2702,2336],{}," 控制依赖共享策略（singleton、eager），实现微前端运行时模块共享",[23,2705,2706],{},"掌握这些高级配置能让 Webpack 适应更复杂的业务场景，并显著提升开发体验和生产性能。",[2624,2708,2709],{"id":2709},"参考文献",[23,2711,2712],{},"以下链接在编写时均可正常访问：",[1519,2714,2715,2724],{},[1522,2716,2717],{},[1525,2718,2719,2722],{},[1528,2720,2721],{},"资料",[1528,2723,1788],{},[1538,2725,2726,2739,2751],{},[1525,2727,2728,2736],{},[1543,2729,2730],{},[27,2731,2735],{"href":2732,"rel":2733},"https:\u002F\u002Fwebpack.js.org\u002Fconcepts\u002Fmodule-federation\u002F",[2734],"nofollow","Module Federation",[1543,2737,2738],{},"官方",[1525,2740,2741,2748],{},[1543,2742,2743],{},[27,2744,2747],{"href":2745,"rel":2746},"https:\u002F\u002Fwebpack.js.org\u002Fconfiguration\u002Fdev-server\u002F",[2734],"DevServer",[1543,2749,2750],{},"配置",[1525,2752,2753,2760],{},[1543,2754,2755],{},[27,2756,2759],{"href":2757,"rel":2758},"https:\u002F\u002Fwebpack.js.org\u002Fconfiguration\u002Fdevtool\u002F",[2734],"Source Map",[1543,2761,1763],{},[2763,2764,2765],"style",{},"html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjYin, html code.shiki .sjYin{--shiki-light:#90A4AE;--shiki-light-font-weight:inherit;--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQRbd, html code.shiki .sQRbd{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#DBEDFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZUrc, html code.shiki .sZUrc{--shiki-light:#E53935;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":60,"searchDepth":74,"depth":74,"links":2767},[2768,2769,2770,2771,2772,2773,2774],{"id":35,"depth":114,"text":36},{"id":783,"depth":114,"text":784},{"id":1505,"depth":114,"text":1506},{"id":1757,"depth":114,"text":1758},{"id":2035,"depth":114,"text":2036},{"id":2626,"depth":74,"text":2626},{"id":2709,"depth":74,"text":2709},{"title":8,"description":9},"knowledge\u002Fbooklet\u002Fproject\u002FWebpack\u002Fadvanced","knowledge\u002Fknowledge\u002Fbooklet\u002Fproject\u002FWebpack\u002Fadvanced.md","md",{"items":2780},[2781,2783,2787,2791,2795,2799],{"path":29,"title":30,"description":2782,"category":10,"score":171},"Webpack 是静态模块打包器：从入口递归解析依赖，经 Loader 转换后输出一个或多个 bundle。与 Vite 对比见 Webpack 与 Vite。",{"path":2784,"title":2785,"description":2786,"category":10,"score":171},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fchunks","产物优化","Webpack 通过代码分割、Tree Shaking、压缩与资源模块等减少体积、改善缓存。原理背景见 工作原理。",{"path":2788,"title":2789,"description":2790,"category":10,"score":171},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fcompany","企业级实践","大型前端项目在 Webpack 场景下的多环境拆分、Docker 构建缓存、自定义 CLI 与微前端（Module Federation \u002F single-spa）等实践。入门见 工程化概览。",{"path":2792,"title":2793,"description":2794,"category":10,"score":171},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Floader","Loader","Loader 在 Webpack 构建链中把源文件转为可打包的 JS 模块（如 TS、SCSS、Vue SFC）。见 工作原理。",{"path":2796,"title":2797,"description":2798,"category":10,"score":171},"\u002Fknowledge\u002Fbooklet\u002Fproject\u002Fwebpack\u002Fplugin","Plugin","Webpack 插件在构建生命周期钩子上扩展能力（修改资源、生成 HTML、上传 CDN 等）。背景见 工作原理；与 Loader 分工：Loader 转换单文件，插件面向整体构建。",{"path":2800,"title":2801,"description":2802,"category":10,"score":139},"\u002Fknowledge\u002Fbooklet\u002Fweb\u002Fjs\u002Fadvanced","函数进阶","this 绑定、高阶函数、纯函数、记忆化、递归与 IIFE 等是日常开发与面试中的核心主题。前置：语言基础、作用域与闭包。",{"series":2804,"title":12,"items":2805,"index":2817,"prev":2818,"next":2819},"booklet\u002Fproject\u002Fwebpack",[2806,2807,2809,2811,2813,2815],{"path":7,"title":8,"date":14,"pinned":15},{"path":2784,"title":2785,"date":2808,"pinned":15},"2025-01-28",{"path":2788,"title":2789,"date":2810,"pinned":15},"2024-07-30",{"path":2792,"title":2793,"date":2812,"pinned":15},"2022-11-14",{"path":2796,"title":2797,"date":2814,"pinned":15},"2020-08-27",{"path":29,"title":30,"date":2816,"pinned":15},"2025-12-19",0,null,{"path":2784,"title":2785,"date":2808,"pinned":15}]