webpack
webpack 开箱即用,可以无需使用任何配置文件。然而,webpack 会假定项目的入口起点为 src/index.js,然后会在 dist/main.js 输出结果,并且在生产环境开启压缩和优化.
Webpack 有大量的配置项,利用 webpack-cli 的 init 命令,它可以根据你的项目需求快速生成 webpack 配置文件,它会在创建配置文件之前询问你几个问题。
bash
$ npx webpack init
[webpack-cli] For using this command you need to install: '@webpack-cli/generators' package.
[webpack-cli] Would you like to install '@webpack-cli/generators' package? (That will run 'npm install -D @webpack-cli/generators') (Y/n)
devDependencies:
+ @webpack-cli/generators 2.5.0
? Which of the following JS solutions do you want to use? ES6
? Do you want to use webpack-dev-server? Yes
? Do you want to simplify the creation of HTML files for your bundle? Yes
? Do you want to add PWA support? No
? Which of the following CSS solutions do you want to use? CSS only
? Will you be using PostCSS in your project? Yes
? Do you want to extract CSS for every file? Only for Production
? Do you like to install prettier to format generated configuration? Yes
? Pick a package manager: pnpm
[webpack-cli] ℹ INFO Initialising project...
devDependencies:
+ @babel/core 7.19.3
+ @babel/preset-env 7.19.4
+ autoprefixer 10.4.12
+ babel-loader 8.2.5
+ css-loader 6.7.1
+ html-webpack-plugin 5.5.0
+ mini-css-extract-plugin 2.6.1
+ postcss 8.4.17
+ postcss-loader 7.0.1
+ prettier 2.7.1
+ style-loader 3.3.1
+ webpack-dev-server 4.11.1
[webpack-cli] Project has been initialised with webpack!
webpack 配置选项
通常你的项目还需要继续扩展此能力,为此你可以在项目根目录下创建一个 webpack.config.js 文件,然后 webpack 会自动使用它
js
// 用于删除/清理构建文件夹
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
// 把打包后的文件插入HTML文件中,可以传递变量给HTML
const HtmlWebpackPlugin = require("html-webpack-plugin");
// 将 CSS 提取到单独的文件中。需要 webpack 5 才能工作
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
// 用于优化 \ 最小化 CSS 资源
const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin");
// 拆分CSS文件,兼容IE9,最多支持4000个选择器)
const CSSSplitWebpackPlugin = require("css-split-webpack-plugin").default;
//引入CSS cssnano配置压缩选项
const cssnano = require("cssnano");
// 混合外部webpack配置文件:merge(baseConfig,currentConfig)
const merge = require("webpack-merge");
// 导出webpack配置文件
module.exports = {
// 项目入口文件:String|Array|Object
entry: {
app: "/bin.js",
},
//构建目录和文件
output: {
filename: "[name].[hash:7].js",
path: "/dist",
clean: true, // 在生成文件之前清空 output 目录
},
// 打包模式 development|production(内部做更多优化,生产部署代码)
mode: "development",
// sourceMap配置选项:跟踪原始文件目录|行号|列号,方便调试
devtool: "cheap-module-eval-source-map",
//缓存生成的 webpack 模块和 chunk,来改善构建速度
cache: {
type: "filesystem",
allowCollectingMemory: true,
},
//告知 webpack 为目标(target)指定一个环境
target: "browserslist",
//防止将某些 import 的包打包到 bundle 中,而是在运行时(runtime)再去从外部(jQuery全局变量中)获取这些扩展依赖(external dependencies)。
externals: {
//导入jquery在运行时再到全局变量jQuery中获取依赖
jquery: "jQuery",
// ./math 是模块,只需要模块下的 subtract 变量子集
subtract: ["./math", "subtract"],
},
//更精确地控制 bundle 信息该怎么显示
stats: "errors-only",
// 在 loader 上下文 中暴露自定义值。
loader: {
answer: 42,
},
//配置如何展示性能提示 object | string
performance: {
//只给出 .js 文件的性能提示
assetFilter: function (assetFilename) {
return assetFilename.endsWith(".js");
},
//打开/关闭提示 'error' | 'warning'| boolean
hints: false,
//根据单个资源体积(单位: bytes),控制 webpack 何时生成性能提示。
maxAssetSize: 100000,
},
// 开发服务器配置,交给webpack-dev-server
devServer: {
//public/ 目录当中的所有内容提供一个本地服务(serve):
static: {
directory: path.join(__dirname, "public"),
},
contentBase: "/dist", //构建文件的目录
publicPath: "",
host: "localhost",
port: 3000,
open: true, // 自动打开浏览器
compress: true, // 启用gzip压缩
hot: true, // 启动热更新
inline: true, // 启用内联模式
// 为所有响应添加 headers:
headers: {
"X-Custom-Foo": "bar",
},
historyApiFallback: true,
proxy: {
"/api": {
target: "http://localhost:3000",
pathRewrite: { "^/api": "" },
changeOrigin: true,
},
},
},
// 模块化配置选项
module: {
// 配置如何解析MIME文件类型
rules: [
{
test: /\.js[x]?$/, // jsx、js处理
exclude: /node_modules/,
use: ["babel-loader"],
},
{
test: /\.(le|c)ss$/, // scss、css处理
use: [MiniCssExtractPlugin.loader, "css-loader", "less-loader"],
},
{
test: /\.(png|jpg|jpeg|gif|svg)/, // 图片处理
use: [
{
loader: "url-loader",
options: {
name: "[name]_[hash].[ext]",
outputPath: "images/",
limit: 204800, // 小于200kb采用base64转码
},
},
],
},
{
test: /\.(eot|woff2?|ttf)/, // 字体处理
use: [
{
loader: "url-loader",
options: {
name: "[name]-[hash:5].min.[ext]",
limit: 5000, // 5kb限制
outputPath: "fonts/",
},
},
],
},
],
},
// 配置如何模块解析策略
resolve: {
extensions: [".jsx", ".js"],
alias: {
Utilities: path.resolve(__dirname, "src/utilities/"),
Templates: path.resolve(__dirname, "src/templates/"),
},
fallback: {
assert: require.resolve("assert"),
buffer: require.resolve("buffer"),
console: require.resolve("console-browserify"),
},
mainFields: ["browser", "module", "main"],
mainFiles: ["index"],
modules: [path.resolve(__dirname, "src"), "node_modules"],
exportsFields: ["exports", "myCompanyExports"],
importsFields: ["browser", "module", "main"],
},
// 配置优化选项
optimization: {
chunkIds: "named",
moduleIds: "named",
//设置为 true 或 'multiple',会为每个入口添加一个只含有 runtime 的额外 chunk
runtimeChunk: {
name: entrypoint => `runtime~${entrypoint.name}`,
},
removeEmptyChunks: true,
// process.env.NODE_ENV 设置为一个给定字符串.默认值取决于 mode
nodeEnv: "production",
mergeDuplicateChunks: false,
//使用 TerserPlugin插件压缩 bundle。
minimize: true,
//提供一个或多个定制过的 TerserPlugin 实例,覆盖默认压缩工具(minimizer)。
minimizer: [
new TerserPlugin({
parallel: true,
terserOptions: {
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions
},
}),
],
// 配置代码分割策略
splitChunks: {
// 提取公共代码
chunks: "all", // async(动态加载模块),initital(入口模块),all(全部模块入口和动态的)
minSize: 3000, // 抽取出来的文件压缩前最小大小
maxSize: 0, // 抽取出来的文件压缩前的最大大小
minChunks: 1, // 被引用次数,默认为1
maxAsyncRequests: 5, // 最大的按需(异步)加载次数,默认为 5;
maxInitialRequests: 3, // 最大的初始化加载次数,默认为 3;
automaticNameDelimiter: "~", // 抽取出来的文件的自动生成名字的分割符,默认为 ~;
name: "vendor/vendor", // 抽取出的文件名,默认为true,表示自动生成文件名
// 配置具体分割组
cacheGroups: {
// 缓存组
common: {
// 将node_modules模块被不同的chunk引入超过1次的抽取为common
test: /[\\/]node_modules[\\/]/,
name: "common",
chunks: "initial",
priority: 2,
minChunks: 2,
},
default: {
reuseExistingChunk: true, // 避免被重复打包分割
filename: "common.js", // 其他公共函数打包成common.js
priority: -20,
},
},
},
},
// 引入的各种插件配置
plugins: [
new webpack.DefinePlugin({
// 创建可在编译时配置的全局常量
}),
new CleanWebpackPlugin(),
new MiniCssExtractPlugin({
filename: "css/[name].[hash:7].css",
chunkFilename: "[id].css",
}),
new OptimizeCSSAssetsPlugin({
assetNameRegExp: /\.css$/g,
cssProcessor: cssnano, // //引入cssnano配置压缩选项
cssProcessorPluginOptions: {
preset: [
"default",
{
discardComments: {
// 移除注释
removeAll: true,
},
normalizeUnicode: false,
},
],
},
canPrint: true,
}),
new CSSSplitWebpackPlugin({
size: 4000, // 超过4kb进行拆分
filename: "[name]-[part].[ext]",
}),
new HtmlWebpackPlugin({
filename: "index.html", // 模板文件名
template: "/index.html", // 模板文件源
minify: {
collapseWhitespace: true, // 压缩空格
minifyCSS: true, // 压缩css
minifyJS: true, // 压缩js
removeComments: true, // 移除注释
caseSensitive: true, // 去除大小写
removeScriptTypeAttributes: true, // 移除script的type属性
removeStyleLinkTypeAttributes: true, // 移除link的type属性
},
}),
],
};
webpack-chunksMap-plugin
插件介绍:对 webpack 配置,使页面路由打包出来的文件,根据路由 path 进行命名。页面文件打包构建后,会生成 js、css、map 文件,可以过滤掉 map 文件,生成路由 path 和 js、css 文件路径的映射关系。最后组装成 script 脚本,注入 HTML 文件里。页面加载后,就可以根据页面的路由 path,找出对应要加载的 js、css 资源。如果知道用户将要去什么页面,就可以预加载对应页面资源了。如果用户要去的是第三方页面,会进行 DNS 预解析流程。
js
// 引入node crypto加解密模块,用于完整性校验
const crypto = require("crypto");
const Buffer = require("buffer");
const path = require("path");
/** 插件默认配置对象
* @pluginName 自定义插件名称
* @indectName 输出的映射资源注入到window的变量名称,或者映射文件名字
* @chunksMapFileFun 自定义输出的映射资源文件的名称
* contentHash 用于完整性校验,内容变动都会改变,更好利用缓存
* @chunkNameFilter chunk输出过滤器:哪些chunk需要生成映射
* chunkName 参数为chunk.name
* @return boolean true时输出
* @chunkKeyFun 自定义输出的映射文件中的json对象里的key
* chunkName
* @return chunkName
* @injectToHtmlFilter 要注入到的HTML文件的,HTML文件资源过滤器
* assetName 打包生成的资源字符串,默认index.html文件
* @return boolean true时注入
* @assetsFilter assets资源输出过滤器
* assetUrl
* @return boolean true时输出
* @assetToChunkRegs 把指定资源,映射到指定chunk中,可配置多个筛选项
* chunkNameFilter 指定chunkname过滤器
* assetNameFilter 指定asset资源过滤器
*
* 把包含link-layout的资源放入,包含link的chunkname对应的映射中
* [{
chunkNameFilter:(chunkName)=>/link/.test(chunkName),
assetNameFilter:(assetUrl)=>/link-layout/.test(assetUrl)}]
*/
const defaultOptions = {
pluginName: "webpack-chunksMap-plugin",
injectName: "_chunks_map",
chunksMapFileFun: contentHash => `${this.injectName}.${contentHash}.js`,
// chunkNameFilter: chunkName => !!chunkName,
chunkNameFilter: chunkName => chunkName.includes("_"),
chunkKeyFun: chunkName => chunkName,
injectToHtmlFilter: assetName => /^index\.html?$/i.test(assetName),
assetsFilter: assetUrl => {
// 默认过滤掉chunk对应的映射中的map文件
return !["map"].includes(path.extname(assetUrl));
},
// [{ chunkNameFilter:(chunkName)=>true,assetNameFilter:(assetUrl)=>true}]
assetToChunkRegs: [],
};
/**
* 插件定义,使用类时,必须要有apply方法
*/
class ChunksMapPlugin {
constructor(options = {}) {
// 覆盖默认options
this.options = Object.assign({}, defaultOptions, options);
}
// webpack调用apply方法,传入webpack compiler编译器实例
apply(compiler) {
const _this = this;
/** 即将输入生成的文件时,触发emit钩子,tapAsync类型
* @pluginName 插件名称
* @callpack 回调函数
* complication 保存着本次编译的资源、各种方法
* next 处理完自身逻辑,需要调用next(),webpack继续执行
*/
compiler.hooks.emit.tapAsync(_this.options.pluginName, function (compilation, next) {
// 要输出的资源资源对象,类似:
// { _home_page:['_home_page.232.js','_home_page.232.css'] }
const chunksMap = {};
// 根据assetNameFilter函数筛选出特定资源,放入该规则的assets属性
const assetToChunkRegs = _this.options.assetToChunkRegs.map(regObject => {
// 根据assets Key即资源名称,过滤出需要映射的chunk依赖的资源
const assets = Object.keys(compilation.assets).filter(item => regObject.assetNameFilter(item));
return Object.assign({}, regObject, { assets });
});
/** compilation
* @chunks 存放所有打包生成的chunk代码块
* files 由chunk生成的文件,一般拆分成js,css,map文件,修改该数组对打包结果没有影响
* modulesIterable 依赖模块迭代器
* @assets 存放即将要输入的资源对象
*/
compilation.chunks.forEach(function (chunk) {
// 当前chunk对应的资源数组,包含由chunk生成assets,以及chunk的依赖模块生成的assets
let files = [];
// 筛选出路由对应的chunk,生成的文件
if (_this.options.chunkNameFilter(chunk.name)) {
// 如果当前chunk需要生成映射
// 保存由chunk生成assets文件
files = chunk.files || [];
for (const regObject of assetToChunkRegs) {
// 找出满足chunkNameFilter规则的资源也加入映射
if (regObject.chunkNameFilter(chunk.name)) {
files = files.concat(regObject.assets);
}
}
/**
* module.buildInfo 该模块打包信息
* buildInfo.assets 打包后生成的assets对象
*/
for (const module of chunk.modulesIterable) {
if (module.buildInfo && module.buildInfo.assets) {
// 把该chunk的依赖模块资源也加入映射中
files = files.concat(Object.keys(module.buildInfo.assets));
}
}
// chunk名称作为映射对象的每个key
const chunkKey = _this.options.chunkKeyFun(chunk.name);
// 保存chunk和chunk筛选后的对应资源的映射
chunksMap[chunkKey] = files.filter(function (url) {
return _this.options.assetsFilter(url);
});
}
});
// 把生成的资源映射对象转成JSON字符串
const chunksMapString = JSON.stringify(chunksMap);
// 根据资源映射字符串生成内容摘要
const contentHash = _this.generateHashByContent(chunksMapString);
// 拼接JavaScript脚本字符串,把资源映射,放入全局变量中
const configJs = "\n(typeof window=='undefined'?global:window)." + _this.options.injectName + "=" + chunksMapString + ";\n";
// 获取资源映射的内容要输出的文件名字
const outputFileName = _this.options.chunksMapFileFun(contentHash);
// 手动添加要输出映射文件,在assets中用对象表示
compilation.assets[outputFileName] = {
name: outputFileName,
source: () => configJs,
size: () => {
return Buffer.byteLength(configJs, "utf8");
},
};
_this.addToHtmlFun(compilation, _this.options.injectToHtmlFilter, outputFileName);
// 这是一个异步事件,要记得调用 callback 通知 Webpack 本次事件监听处理结束。
// 如果忘记了调用 callback,Webpack 将一直卡在这里而不会往后执行。
next();
});
}
/**
* 根据内容生成 contenthash,如果内容没变生成的contenthash是一样的
* 修复内容改变,继续使用旧缓存的问题,此时需要获取最新的映射资源
*/
generateHashByContent(content) {
if (typeof content !== "string" && !Buffer.isBuffer(content)) {
throw new TypeError("Expected a Buffer or string");
}
/** 使用md5(sha1,sha256默认,sha512)算法生成一个hash实例
* hash.update('要加密的字符串'),返回hash实例
* hash.digest('hex) 生成内容摘要。默认是2进制,转成16进制
* 调用String.slice()截取前十位
*/
return crypto.createHash("md5").update(content).digest("hex").slice(0, 10);
}
// 直接注入脚本代码到HTML文件
addToHtmlFun(compilation, injectToHtmlFilter, outputFileName) {
// 从将要输出的资源中,找出index.html文件
const indexHtmls = Object.keys(compilation.assets).filter(assetName => {
return injectToHtmlFilter(assetName);
});
indexHtmls.forEach(htmlName => {
const htmlSource = compilation.assets[htmlName];
// 获取index.html文件的内容
const htmlContent = htmlSource.source();
if (htmlContent) {
// 把生成的映射文件,添加进index.html文件的 body元素最后
htmlContent = htmlContent.replace(/\<\/body\>/i, m => `<script src=${outputFileName} async></script>${m}`);
}
// 把修改后的html,手动添加到要输入文件中
delete compilation.assets[htmlName];
compilation.assets[htmlName] = {
name: htmlName,
source: () => htmlContent,
size: () => {
return Buffer.byteLength(htmlContent, "utf8");
},
};
});
}
}
module.exports = ChunksMapPlugin;
插件配套的工具
js
const path = require("path");
// 每个子路由对象route,大概这个样子
const route = {
name: "AddNewCard",
path: "addNewCard",
meta: { layout: "contentBar" },
component: () => import("@/pages/newCard"),
//用于生成webpack分包配置选项的test属性
chunkPath: "/src/pages/newCard",
};
// 每个webpack分包配置对象chunk,大概这个样子
const chunk = {
name: "_home_addnewcard",
priority: 25,
test: "[\\\\/]src[\\\\/]pages[\\\\/]newCard",
enforce: true,
chunks: "all",
};
/** 配置生成chunk的规则
* @basePriority 生成路由chunk的基础优先级,路由chunk依次++
* @pathSeparator 把页面路由path的分隔符‘/’替换为其他标识符,
* 用于根据路由path,生成chunk.name,例如: /cashier/home -> _cashier_home
* @chunkPathKey 每个路由配置route中的一个字段,用于生成chunk的匹配规则
* @chunkConfigFun 自定义webpack的分包配置规则,会把匹配到的文件打包进
* 同一个chunk里。name和basePriority不可自定义
*/
const chunkOptions = {
basePriority: 20,
pathSeparator: "_",
chunkPathKey: "chunkPath",
chunkConfigFun: (route, chunkPathKey) => ({
test: new RegExp(route[chunkPathKey].replace(/[/]/g, "[\\\\/]")),
// test:'[\\\\/]src[\\\\/]pages[\\\\/]newCard',
enforce: true,
chunks: "all",
}),
};
/** 收银台默认默认配置对象
* @indectName 把映射数据注入window中全局变量名称,用户获取映射资源
* @isHashMode 路由是否hash模式,用于判断如何从url中拿到路由path
* @extraInitUrls 额外需要进行dns预解析或资源预加载的url数组(非编译时)
* @isCurrentDomainFun 用于判断url是否和项目当前域名相同的函数。
* 相同域名进行资源预加载,不同域名进行DNS预解析流程
* @chunkOptions 配置生成chunk的规则
* @routes 需要生成映射资源的页面路由数组
*/
const defaultOptions = {
injectName: "_chunks_map",
isHashMode: true,
extraInitUrls: [],
isCurrentDomainFun: url => true,
chunkOptions: chunkOptions,
routes: [], // 页面路由数组
};
/** 定义ChunksMapUtils Webpack插件,可以定义为函数或者类
*
*/
class ChunksMapUtils {
/**
* 该插件的构造函数,获取自定义的配置对象,可以覆盖默认配置
*/
constructor(options = {}) {
// 合并后的总的配置对象
this.options = Object.assign({}, defaultOptions, options);
// 用于保存获取到的映射文件的数据
this.chunksMap = (window && window[this.options.injectName]) || {};
// 保存已添加到dom的url数组,防止重复资源预加载或者DNS预解析
this.linkUrls = [];
// 获取由路由生成的webpack分包配置splitChunks:cacheGroup配置选项
// 用于后续根据path映射chunkname,再映射到生成的资源
this.routesChunks = ChunksMapUtils.generateRouterChunks(this.options.chunkOptions);
// 获取HTML head元素,用于后续资源预加载和DNS预解析
this.head = window && window.document.head;
// 需要额外往HTML中注入资源url数组(要放在最后)
this.injectLinksToDom(this.options.extraInitUrls);
}
// 当前插件为单例模式
static getInstance(options) {
if (!this.instance) {
this.instance = new ChunksMapUtils(options);
}
return this.instance;
}
/** 创建link标签,有后缀资源预加载,否则dns预解析
* @href 需要进行资源加载或者DNS预解析的URL
* @return <HTMLLinkElement> | undefined
*/
getLinkElement(href) {
// 已添加过的url,不在重复添加
if (this.linkUrls.includes(href)) return;
const ext = path.extname(href);
const link = document.createElement("link");
const map = new Map([
["js", "script"],
["css", "style"],
["other", "image"],
]);
if (this.options.isCurrentDomainFun(href)) {
if (ext) {
// 是当前域名且存在文件后缀,则根据资源类型,映射不同as属性
link.rel = "prefetch";
link.as = map.get(ext) || map.get("other");
}
} else {
// 不是当前域名进行DNS预解析
link.rel = "dns-prefetch";
}
link.href = href;
// 加入缓存防止重复添加进DOM
this.linkUrls.push(href);
return link;
}
/** 获取url上的pathname,例如:/cashier/result
* hash:'https://cashier-n.payermax.com/index.html#/cashier/result'
* history:'https://cashier-n.payermax.com/cashier/result'
* @href 获取pathname来源的url
* @return 返回url 的 pathname。例如:/cashier/result
*/
getPathFromUrl(url) {
if (!url) return "";
// 把url解析成URL对象
const urlObj = new URL(url);
// hash模式直接返回pathname
if (!this.options.isHashMode) return urlObj.pathname;
const hash = urlObj.hash;
if (!hash) return "";
const index = hash.indexOf("?");
// 截取hash中‘#’之后‘?’之前的部分
if (index > 0) return hash.slice(1, index);
return hash.slice(1);
}
/** 根据pathname获取打包后对应的chunkname,它能映射资源名称
* @pathname
* @return chunkname
*/
getChunkNameByPath(pathname) {
if (!pathname || typeof pathname !== "string") return "";
const pathnameLower = pathname.toLowerCase();
// 从webpack 分包配置选项中,找出当前路由的chunk配置
const chunk = this.routesChunks[pathnameLower];
return chunk?.name || "";
}
/** 根据URL数组,生成对应的link标签,注入到HTML文档中
* @urlList url组成的数组
* @return undefined
*/
injectLinksToDom(urlList) {
// 验证安全边界
if (!urlList || !Array.isArray(urlList) || urlList.length === 0 || !window) return;
try {
// 构建documentFragment文档片段对象
const fragment = document.createDocumentFragment();
urlList.forEach(url => {
if (!this.options.isCurrentDomainFun(url)) {
// 第三方域名,进行dns预解析,
let linkElement = this.getLinkElement(url);
return fragment.appendChild(linkElement);
}
// 当前域名,进行资源预加载。首选获取url对应的path
const path = this.getPathFromUrl(url);
// 获取path对应的,打包后的资源映射的key
const chunkName = this.getChunkNameByPath(path);
// 获取path对应的,打包后的资源数组,并遍历
(this.chunksMap[chunkName] || []).forEach(item => {
//如果没有被添加过dom,返回构建好的link元素
let linkElement = this.getLinkElement(item);
// 统一添加到documentFragment片段,减少回流重绘次数
linkElement && fragment.appendChild(linkElement);
});
});
// 将documentFragment片段添加进DOM里
// HTML将自动进行资源预加载或者DNS预解析
this.head && this.head.append(fragment);
} catch (error) {
console.warn("dns预解析和资源预加载失败:", error);
}
}
/** 构建要传给webpack的分包:splitChunks配置选项
* @options chunkOptions对象
* @return splitChunks分包配置对象
*/
static generateRouterChunks(options = this.options.chunkOptions) {
const chunks = {};
// 构建每个chunk分组,并保存到chunks的routeFullPath属性
const setChunks = (route, parentPath) => {
// 子路由不存在chunkPathKey,默认是chunkPath,则不会映射打包后的资源
if (!route?.[options.chunkPathKey]) return;
// 拼接当前route的完整pathname
const routeFullPath = `${parentPath}/${route.path}`.toLowerCase();
// 替换完整pathname的分隔符,用于打包后生成的chunk名,会转成文件名
// 拼接前 /home/page,拼接后 _home_page。因为‘/’是个敏感字符
const chunkname = routeFullPath.replace(/[\\/]/g, options.pathSeparator);
// chunks根据routeFullPath分组,优先级priority递增
chunks[routeFullPath] = Object.assign({}, options.chunkConfigFun(route, options.chunkPathKey), { name: chunkname, priority: ++options.basePriority });
};
const parseRoutes = (routes, parentPath) => {
for (let route of routes) {
// 构建当前route对应的webpack分组配置chunk选项
setChunks(route, parentPath);
if (route.children?.length) {
// 根据父级path和当前path,拼接完整的path
const routeFullPath = parentPath ? `${parentPath}/${route.path}` : route.path;
//递归子路由,配置每个路由的分组配置选项
parseRoutes(route.children, routeFullPath);
}
}
};
// 传入要构建页面和资源映射关系的routes数组
parseRoutes(this.routes);
return chunks;
}
}
module.exports = ChunksMapUtils;