1. 输入:
entry: {
app: [
'./src/main.ts'
]
},
context: 'D:\\Webpack-code\\webpack-test-vue-cli',
2. 模块处理:
resolve【配置模块路径解析规则】:alias:创建两个别名,一个常规项目都有的@,另一个是vue$,值得注意的是这里的 $ 是正则表达式的通配符,表示在引用 Vue 运行时文件可以通过别名访问,例如 import Vue from 'vue' 或者 form 'vue/xx/'extensions:排列好文件的解析顺序,其中[tsx,ts,mjs,js,jsx]顺序优先解析,其次是vue文件,最后是json与wasm文件。modules:使用绝对路径,告诉 Webpack 解析模块时应该在全局mode_modules,项目node_modules和@vue的node_modules
resolve: {
alias: {
'@': 'D:\\Webpack-code\\webpack-test-vue-cli\\src',
vue$: 'vue/dist/vue.runtime.esm-bundler.js'
},
extensions: [
'.tsx',
'.ts',
'.mjs',
'.js',
'.jsx',
'.vue',
'.json',
'.wasm'
],
modules: [
'node_modules',
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules',
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\@vue\\cli-service\\node_modules'
]
}
resolveLoader【用于解析 Webpack 的 loader 包】:modules:使用绝对路径,配置各个 loader 的位置。
resolveLoader: {
modules: [
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\@vue\\cli-plugin-typescript\\node_modules',
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\@vue\\cli-plugin-babel\\node_modules',
'node_modules',
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules',
'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\@vue\\cli-service\\node_modules'
]
},
module【配置模块加载规则】:noParse:忽略vue相关包的模块加载,防止 Webpack 解析这些包里与给定正则表达式相匹配的文件,进而提高构建性能。rules:设定创建模块时,匹配请求的规则数组,这些规则能够修改模块的创建方式。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。Vue-Cli 定义了多种格式的处理规则,其中主要有esm、vue、vue-style、postcss、scss、ts、tsx等复杂文件解析规则,还有images、media、fonts、css、js等基础文件的解析规则可供我们借鉴。我们将节选其中一部分,来分析一下。
其中简单的文件模块处理,只是test+type+generator,也就是文件名匹配+设置模块匹配类型+输出文件生成规则
/* config.module.rule('images') */
{
// 匹配多种图片模式
test: /\.(png|jpe?g|gif|webp|avif)(\?.*)?$/,
// 指定资源类型为 asset
type: 'asset',
// 规定好打包后生成的文件生成在 img 文件夹下
// 以图片名称+哈希值+图片格式为命名规则
generator: {
filename: 'img/[name].[hash:8][ext]'
}
},
复杂的文件模块也十分复杂,我们以 css 文件类型来举例:
通过oneOf配置不难看出,css 模块有四种模块处理规则,分别是vue-modules,vue等 vue 文件内的 css 处理模式,还有normal-modules以及normal的正常 css 文件处理模式。Webpack 中的处理模块匹配规则是先匹配父规则、然后是 rules 最后是 rules 里面的 oneOf,这个过程中只要有一项匹配到了,就会按照第一个匹配成功的规则执行。
如果模块的
import 参数中带有 /module/ 部分,也就是引入了 module 文件夹下的css,那么就满足resourceQuery: /module/条件,进入 vue模块包 处理逻辑;import 参数中带有 /\?vue/ 部分,就进入 vue处理逻辑;匹配到 /\.module\.\w+$/,就进入normal模块包的处理逻辑;其余的都进入normal的处理逻辑。
深入到四种处理逻辑中发现,其实处理逻辑大同小异,我们就拿
vue模块包的处理方式来分析:引入的 loader 有 vue-style-loader、css-loader、postcss-loader
/* config.module.rule('css').oneOf('vue-modules') */
{
// 匹配模式
resourceQuery: /module/,
// 应用于模块的 UseEntries 数组,可以看做是 loader 的配置列表
use: [
/* config.module.rule('css').oneOf('vue-modules').use('vue-style-loader') */
{
// loader 位置,使用 vue-style-loader 来解析文件,值得一提的是,vue-style-loader 是由 style-loader 精简而来
loader: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\vue-style-loader\\index.js',
// 不生成源映射和不启用特定的 shadowMode
options: {
sourceMap: false,
shadowMode: false
}
},
/* config.module.rule('css').oneOf('vue-modules').use('css-loader') */
{
loader: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\css-loader\\dist\\cjs.js',
options: {
// 不生成源映射
sourceMap: false,
// 配置允许在 css-loader 之前的 loader 数量
// 0 => no loaders (default);
// 1 => postcss-loader;
// 2 => postcss-loader, sass-loader
importLoaders: 2,
// vue 模块包和 normal 模块包有这项配置
// vue 和 normal 没有这项配置
// localIdentName 配置模块生成的本地标识符
// 如果没有启用 auto,则会将所有文件按 css 文件处理
modules: {
localIdentName: '[name]_[local]_[hash:base64:5]',
auto: () => true
}
}
},
/* config.module.rule('css').oneOf('vue-modules').use('postcss-loader') */
{
loader: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\postcss-loader\\dist\\cjs.js',
options: {
sourceMap: false,
// 允许设置 PostCSS 选项和插件
postcssOptions: {
plugins: [
function () { /* omitted long function */ }
]
}
}
}
]
},
plugins【插件列表】:-
new Plugin()【vue-lader 相关插件】 -
DefinePlugin【在编译时定义全局常量的插件】:定义了__VUE_OPTIONS_API__和__VUE_PROD_DEVTOOLS__两个常量__VUE_OPTIONS_API__:设置为true。代表启用选项式 API。__VUE_PROD_DEVTOOLS__:设置为false。代表在生产环境下不使用调试工具,从而减小生产包的大小并提高性能。
-
DefinePlugin【process.env 相关插件】:定义process.env环境变量NODE_ENV:设置为development,设置项目当前的开发环境BASE_URL:设置为/,指定项目的基础 URL 路径,在加载静态资源、处理路由等方面可能会用到这个基础路径。
-
CaseSensitivePathsPlugin:启用此插件,确保文件路径的匹配和引用过程中,路径是大小写敏感的。保证在跨平台开发时,项目能够正确地处理文件路径,避免因路径大小写不一致而导致的问题。 -
FriendlyErrorsWebpackPlugin:提供更友好的错误和警告信息展示,避免开发者面对一堆晦涩难懂的原始错误信息。additionalTransformers:声明自定义的转化函数additionalFormatters:声明自定义的格式化函数
-
HtmlWebpackPlugin:自动生成 HTML 文件,并将 Webpack 打包生成 JavaScript、CSS 等资源自动嵌入到这个 HTML 文件中。title:设置生成的 HTML 文件标题为weback-test-vue-cli,也就是项目名scriptLoading:设置为defer,这意味着 HTML 加载时,脚本会在文档解析完成后按照顺序执行,而不是阻塞文档的加载,有助于提高页面的加载速度和用于吐艳templateParameters:通过一个函数来设置 HTML 模板的参数,这个参数通常用于在 HTML 模板中动态插入一些数据,比如根据不同的环境变量插入不同的值等。template:指定用于生成 HTML 文件的模板文件路径。
-
CopyPlugin:将指定的文件或文件夹从一个位置复制到另一个位置。- 将
D:\Webpack-code\webpack-test-vue-cli\public目录下的文件(除了**/.DS_Store和D:/Webpack-code/webpack-test-vue-cli/public/index.html这两个被忽略的文件或文件夹)复制到D:\Webpack-code\webpack-test-vue-cli\dist目录下,并且设置了toType为dir(表示目标是一个目录),同时允许在目标位置不存在源文件时不报错(noErrorOnMissing),并且设置了复制信息为最小化(info: {minimized: true},具体含义可能与插件内部的优化或日志记录相关)。
- 将
-
ESLintWebpackPlugin:用于在 Webpack 构建过程中集成 ESLint 代码检查工具extensions:指定了需要进行 ESLint 检查的文件扩展名,包括常见的 JavaScript、JavaScriptX、Vue 单文件组件、TypeScript 和 TypeScriptX 等文件类型。cwd:设置当前工作目录为D:\Webpack-code\webpack-test-vue-cli,这通常用于确定相对路径的起点等。cache:设置为true,表示启用 ESLint 检查的缓存机制,这样在多次构建过程中,如果文件没有发生变化,就可以直接使用缓存的检查结果,提高构建效率。cacheLocation:指定了缓存文件的具体存储位置,这里是D:\Webpack-code\webpack-test-vue-cli\node_modules\.cache\eslint\3e6d7e32.json。context:设置为D:\Webpack-code\webpack-test-vue-cli,与cwd类似,用于确定相关操作的上下文环境。failOnWarning:设置为false,这意味着在出现 ESLint 警告时,不会导致 Webpack 构建失败,只会给出警告信息。failOnError:设置为true,表示在出现 ESLint 错误时,会导致 Webpack 构建失败,以确保代码质量。eslintPath:指定了 ESLint 工具的安装路径,这里是D:\Webpack-code\webpack-test-vue-cli\node_modules\eslint。formatter:设置为stylish,这是一种常见的 ESLint 结果格式化方式,会将检查结果以一种比较美观、易读的方式呈现出来。ForkTsCheckerWebpackPlugin用于在 Webpack 构建过程中对 TypeScript 代码进行异步检查。
-
typescript:在这个部分的配置中:extensions:针对vue文件类型进行了特殊设置,设置enabled为true,表示会对 Vue 单文件组件中的 TypeScript 代码进行检查,并且指定了用于编译 Vue 单文件组件的编译器路径为D:\Webpack-code\webpack-test-vue-cli\node_modules\vue\compiler-sfc\index.js。diagnosticOptions:设置了诊断选项,其中semantic设置为true,表示会进行语义检查(检查代码的逻辑含义等),而syntactic设置为false,表示不进行语法检查(因为通常在 TypeScript 本身的编译过程中已经进行了语法检查)。
-
plugins: [
/* config.plugin('vue-loader') */
new Plugin(),
/* config.plugin('feature-flags') */
new DefinePlugin(
{
__VUE_OPTIONS_API__: 'true',
__VUE_PROD_DEVTOOLS__: 'false'
}
),
/* config.plugin('define') */
new DefinePlugin(
{
'process.env': {
NODE_ENV: '"development"',
BASE_URL: '"/"'
}
}
),
/* config.plugin('case-sensitive-paths') */
new CaseSensitivePathsPlugin(),
/* config.plugin('friendly-errors') */
new FriendlyErrorsWebpackPlugin(
{
additionalTransformers: [
function () { /* omitted long function */ }
],
additionalFormatters: [
function () { /* omitted long function */ }
]
}
),
/* config.plugin('html') */
new HtmlWebpackPlugin(
{
title: 'webpack-test-vue-cli',
scriptLoading: 'defer',
templateParameters: function () { /* omitted long function */ },
template: 'D:\\Webpack-code\\webpack-test-vue-cli\\public\\index.html'
}
),
/* config.plugin('copy') */
new CopyPlugin(
{
patterns: [
{
from: 'D:\\Webpack-code\\webpack-test-vue-cli\\public',
to: 'D:\\Webpack-code\\webpack-test-vue-cli\\dist',
toType: 'dir',
noErrorOnMissing: true,
globOptions: {
ignore: [
'**/.DS_Store',
'D:/Webpack-code/webpack-test-vue-cli/public/index.html'
]
},
info: {
minimized: true
}
}
]
}
),
/* config.plugin('eslint') */
new ESLintWebpackPlugin(
{
extensions: [
'.js',
'.jsx',
'.vue',
'.ts',
'.tsx'
],
cwd: 'D:\\Webpack-code\\webpack-test-vue-cli',
cache: true,
cacheLocation: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\.cache\\eslint\\3e6d7e32.json',
context: 'D:\\Webpack-code\\webpack-test-vue-cli',
failOnWarning: false,
failOnError: true,
eslintPath: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\eslint',
formatter: 'stylish'
}
),
/* config.plugin('fork-ts-checker') */
new ForkTsCheckerWebpackPlugin(
{
typescript: {
extensions: {
vue: {
enabled: true,
compiler: 'D:\\Webpack-code\\webpack-test-vue-cli\\node_modules\\vue\\compiler-sfc\\index.js'
}
},
diagnosticOptions: {
semantic: true,
syntactic: false
}
}
}
)
],
3. 后处理
optimization【配置优化产物包体积】:realContentHash:在生成构建输出文件时,不会使用基于文件实际内容计算出来的哈希值来命名文件。SplitChunksPlugin:告诉构建工具如何将代码进行分割,以便更好地实现缓存利用、减少初始加载包的大小等优化目标。通过定义不同的缓存组(cacheGroups),可以按照特定的规则将代码划分到不同的“块”(chunks)中。defaultVendors缓存组:这个缓存中生成的块名称为 chunk-vendors。它的主要目的是将项目中来自 node_modules 目录的代码(通常是第三方库、框架等依赖项)提取出来,形成一个单独的块。这样做的好处是,当这些第三方以来的版本没有变化时,浏览器可以缓存这个chunk-vendors块,下次加载页面时就不需要重新下载这些已经缓存的以来代码,从而提高页面的加载速度。test 条件:只要文件路径中包含 node_modules,就会被认为是符合该缓存组提取条件的代码priority 优先级:设置为-10chunks 类型:设置为initial,意味着这个缓存组主要是针对项目初始加载时需要的代码进行操作,也就是在页面首次加载时,会根据这个规则来提取来自 node_modules 的代码形成的单独的块。
common缓存组:生成的块名称为 chunk-common。这个缓存组的目标是提取在项目中被至少两个不同的模块(或文件)所使用的公共代码,并将其形成一个单独的块。这样可以避免这些公共代码在多个地方重复加载,进一步优化页面的加载性能。minChunks 条件:设置为2。规定了只有当一段代码被至少两个不同的模块(或文件)引用时,才会被这个缓存组提取出来。priority 优先级:设置为-20。优先级比defaultVendors 缓存组低,当一段代码既符合defaultVendors又符合common,那么更有可能被划分到defaultVendors里。chunks 类型:同样设置为initialreuseExistringChunk 条件:设置为true,这意味着如果在构建过程中已经存在一个符合条件的现有块(比如之前已经通过其他规则提取出了一段公共代码形成了一个块),那么就会优先重用这个现有的块,而不是重新创建一个新的块来提取相同的公共代码,这有助于减少不必要的代码重复和构建时间。
4. 输出:
outputhasFunction【生成文件哈希值的方法】:设置为xxhash64。xxhash64是一种相对快速且具有较好分布性的哈希算法。使用特定的哈希函数可以根据项目需求来准确地识别文件是否更新,从而便于浏览器进行缓存控制。例如,当一个 JavaScript 文件的内容被修改后,通过xxhash64计算出的新哈希值会使文件名发生变化,浏览器下次请求时就能够识别到这是一个新文件,进而重新下载,而不会因为缓存了旧版本而出现问题。path【输出文件路径】:所有经过 Webpack 打包、处理后的文件最终都会被放置到这个指定的目录下。filename【主输出文件的文件名格式】:这里采用了动态命名的方式,[name]是一个占位符,它会被替换为具体的模块名称或入口名称等相关信息。例如,如果有一个入口文件名为main.js,那么经过 Webpack 构建后,对应的输出文件可能会被命名为js/main.jspublicPath【指定在 HTML 文件中引用静态资源时的基础路径】:确保项目在不同的部署环境下(如本地开发环境、生产环境等)能够正确地加载静态资源。当 HTML 文件中包含<script src="js/[name].js"></script>这样的引用时,浏览器会从项目的根目录开始寻找js/[name].js这个文件,也就是在publicPath所指定的基础路径下寻找相应的静态资源。chunkFilename【指定代码块文件的文件名格式】:与filename类似,chunkFilename用于指定代码块(chunks)文件的文件名格式。这里采用了动态命名的方式,[name]会被替换为具体的代码块名称等相关信息,并且输出的代码块文件也会被放置在js子目录下(如js/[name].js),以保持文件结构的一致性和清晰性。
output: {
hashFunction: 'xxhash64',
path: 'D:\\Webpack-code\\webpack-test-vue-cli\\dist',
filename: 'js/[name].js',
publicPath: '/',
chunkFilename: 'js/[name].js'
},