Vue-Cli 的 Webpack 配置清单

353 阅读11分钟

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 文件,最后是 jsonwasm 文件。
    • 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-modulesvue等 vue 文件内的 css 处理模式,还有normal-modules以及normal的正常 css 文件处理模式。Webpack 中的处理模块匹配规则是先匹配父规则、然后是 rules 最后是 rules 里面的 oneOf,这个过程中只要有一项匹配到了,就会按照第一个匹配成功的规则执行。

image.png 如果模块的 import 参数中带有 /module/ 部分,也就是引入了 module 文件夹下的css,那么就满足resourceQuery: /module/条件,进入 vue模块包 处理逻辑;import 参数中带有 /\?vue/ 部分,就进入 vue处理逻辑;匹配到 /\.module\.\w+$/,就进入normal模块包的处理逻辑;其余的都进入normal的处理逻辑。

image.png 深入到四种处理逻辑中发现,其实处理逻辑大同小异,我们就拿 vue模块包的处理方式来分析:引入的 loader 有 vue-style-loadercss-loaderpostcss-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 优先级:设置为-10
        • chunks 类型:设置为 initial,意味着这个缓存组主要是针对项目初始加载时需要的代码进行操作,也就是在页面首次加载时,会根据这个规则来提取来自 node_modules 的代码形成的单独的块。
      • common缓存组:生成的块名称为 chunk-common。这个缓存组的目标是提取在项目中被至少两个不同的模块(或文件)所使用的公共代码,并将其形成一个单独的块。这样可以避免这些公共代码在多个地方重复加载,进一步优化页面的加载性能
        • minChunks 条件:设置为2。规定了只有当一段代码被至少两个不同的模块(或文件)引用时,才会被这个缓存组提取出来。
        • priority 优先级:设置为-20。优先级比 defaultVendors 缓存组低,当一段代码既符合 defaultVendors 又符合 common,那么更有可能被划分到 defaultVendors 里。
        • chunks 类型:同样设置为 initial
        • reuseExistringChunk 条件:设置为 true,这意味着如果在构建过程中已经存在一个符合条件的现有块(比如之前已经通过其他规则提取出了一段公共代码形成了一个块),那么就会优先重用这个现有的块,而不是重新创建一个新的块来提取相同的公共代码,这有助于减少不必要的代码重复和构建时间

4. 输出:

  • output
    • hasFunction【生成文件哈希值的方法】:设置为xxhash64xxhash64 是一种相对快速且具有较好分布性的哈希算法。使用特定的哈希函数可以根据项目需求来准确地识别文件是否更新,从而便于浏览器进行缓存控制。例如,当一个 JavaScript 文件的内容被修改后,通过 xxhash64 计算出的新哈希值会使文件名发生变化,浏览器下次请求时就能够识别到这是一个新文件,进而重新下载,而不会因为缓存了旧版本而出现问题。
    • path【输出文件路径】:所有经过 Webpack 打包、处理后的文件最终都会被放置到这个指定的目录下。
    • filename【主输出文件的文件名格式】:这里采用了动态命名的方式,[name]是一个占位符,它会被替换为具体的模块名称或入口名称等相关信息。例如,如果有一个入口文件名为 main.js,那么经过 Webpack 构建后,对应的输出文件可能会被命名为 js/main.js
    • publicPath【指定在 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'
  },