vue-cli webpack 配置信息分析(module)

Posted by XuBaoshi on June 20, 2020

vue-cli webpack 配置信息分析(module)

使用 vue-cli3 生成 webpack 配置(default)

/img/vue-cli/1.png

项目生成和完毕后,切换到项目目录,执行命令 npx vue-cli-service inspect, 生成的 webpack 信息如下:

webpack 配置信息

module

noParse

vue-cli 配置:

noParse: /^(vue|vue-router|vuex|vuex-router-sync)$/,

noParse 配置项可以让 webpack 忽略对部分从没有采用模块化的文件的递归解析与处理,可以提高构建性能, 没有采用模块化标准的库如(jquery)让 webpack 解析既耗时又没有意义。

noParse 支持正则表达式和函数

// 使用正则表达式
noParse: /jquery|vue/

// 使用函数,从 Webpack 3.0.0 开始支持
noParse: (content) => {
  // content 代表一个模块的文件路径
  // 返回 true or false
  return /jquery|vue/.test(content)
}

注意:被忽略掉的文件里不应该包含 import 、 require 、 define 等模块化语句,不然会导致构建出的代码中包含无法在浏览器环境下执行的模块化语句。

rules

rules 主要是用来配置模块的读取和解析规则,通常是用来配置 Loader。其类型是一个数组,数组中的每一项都是描述了如何去处理部分文件。

每一个 rule 大致都是使用以下方式:

  1. 条件匹配 通过 testincludeexclude 三个配置项来命中匹配的文件
  2. 应用规则 对选中后的文件通过 use 配置项来使用 Loader, 可以使用一个 loader 或者从后往前应用一组 Loader, 同时也可以分别给 loader 传入参数
  3. 重置顺序 默认 use 内的 loader 是从后往前执行的, 但可以通过在 enforce 选项在最前或最后执行 loader

可以使用字符串或对象配置 loader。

字符串:

{
  // 命中 JavaScript 文件
  test: /\.js$/,
  // 用 babel-loader 转换 JavaScript 文件
  // ?cacheDirectory 表示传给 babel-loader 的参数,用于缓存 babel 编译结果加快重新编译速度
  use: ['babel-loader?cacheDirectory'],
  // 只命中src目录里的js文件,加快 Webpack 搜索速度
  include: path.resolve(__dirname, 'src')
}

对象:

{
  test: /\.js$/,
  use: [{
    loader:'babel-loader',
    options: {
      cacheDirectory: true
    }
  }],
  // 只命中src目录里的js文件,加快 Webpack 搜索速度
  include: path.resolve(__dirname, 'src')
}

vue

vue-cli 配置:

{
  test: /\.vue$/,
  use: [
    /* config.module.rule('vue').use('cache-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\cache-loader\\dist\\cjs.js',
      options: {
        cacheDirectory: 'H:\\code\\own\\test\\node_modules\\.cache\\vue-loader',
        cacheIdentifier: '5d7b93da'
      }
    },
    /* config.module.rule('vue').use('vue-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\vue-loader\\lib\\index.js',
      options: {
        compilerOptions: {
          whitespace: 'condense'
        },
        cacheDirectory: 'H:\\code\\own\\test\\node_modules\\.cache\\vue-loader',
        cacheIdentifier: '5d7b93da'
      }
    }
  ]
}

上述配置中匹配到了后缀名为 vue 的文件,针对此类文件先后执行了 vue-loader 及 cache-loader。

提取 .vue 文件中的 script、style、html 模板代码,再分别交给对应的 loader 处理。loader 配置了 vue-loader 位置 。

其中 compilerOptions 的配置来源于 vue-template-compiler 插件,其中 compilerOptions.whitespace = condense 表示会尽可能减少标签之间的空白区域,这样会减少代码大小,提升编译性能。

cacheDirectory、cacheIdentifier 这两个选项需要同时配置并配合 cache-loader 使用开启文件系统的编译缓存。cacheDirectory 表示编译缓存存放位置,cacheIdentifier 表示缓存的标识符。

注:使用 vue-loader 不光要在 module.rules 配置引用 vue-loader, 同时需要引入 vue-loader/lib/plugin,同时在 plugins 内引入

const VueLoaderPlugin = require('vue-loader/lib/plugin')

// ...
{
  plugins: [new VueLoaderPlugin()]
}

demo

images

vue-cli 配置:

{
  test: /\.(png|jpe?g|gif|webp)(\?.*)?$/,
  use: [
    /* config.module.rule('images').use('url-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\url-loader\\dist\\cjs.js',
      options: {
        limit: 4096,
        fallback: {
          loader: 'H:\\code\\own\\test\\node_modules\\file-loader\\dist\\cjs.js',
          options: {
            name: 'img/[name].[hash:8].[ext]'
          }
        }
      }
    }
  ]
},

匹配 png、jpg、jpeg、webp 文件, 如果文件大小没有超过 4096b,使用 url-loader 将图片转换成 base64 的 URI。如果超过了 4096b 则使用 file-loader 放置在 dist/img 中,名称为 图片名称 + 8 位哈希值 + 文件后缀名。

svg

vue-cli 配置:

{
  test: /\.(svg)(\?.*)?$/,
  use: [
    /* config.module.rule('svg').use('file-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\file-loader\\dist\\cjs.js',
      options: {
        name: 'img/[name].[hash:8].[ext]'
      }
    }
  ]
}

svg 文件使用 file-loader 使用方式同上面的 images。

media

vue-cli 配置:

{
  test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
  use: [
    /* config.module.rule('media').use('url-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\url-loader\\dist\\cjs.js',
      options: {
        limit: 4096,
        fallback: {
          loader: 'H:\\code\\own\\test\\node_modules\\file-loader\\dist\\cjs.js',
          options: {
            name: 'media/[name].[hash:8].[ext]'
          }
        }
      }
    }
  ]
}

匹配 mp4、webm、ogg、mp3、wav、flac、aac 文件, 如果文件大小没有超过 4096b,使用 url-loader 将图片转换成 base64 的 URI。如果超过了 4096b 则使用 file-loader 放置在 dist/media 中,名称为 图片名称 + 8 位哈希值 + 文件后缀名。

fonts

vue-cli 配置:

{
  test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
  use: [
    /* config.module.rule('fonts').use('url-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\url-loader\\dist\\cjs.js',
      options: {
        limit: 4096,
        fallback: {
          loader: 'H:\\code\\own\\test\\node_modules\\file-loader\\dist\\cjs.js',
          options: {
            name: 'fonts/[name].[hash:8].[ext]'
          }
        }
      }
    }
  ]
}

处理方式同 images,打包后放置在 dist/fonts

pug

vue-cli 配置:

{
  test: /\.pug$/,
  oneOf: [
    /* config.module.rule('pug').oneOf('pug-vue') */
    // 这条规则应用到 Vue 组件内的 `<template lang="pug">`
    {
      resourceQuery: /vue/,
      use: [
        /* config.module.rule('pug').oneOf('pug-vue').use('pug-plain-loader') */
        {
          loader: 'pug-plain-loader'
        }
      ]
    },
    /* config.module.rule('pug').oneOf('pug-template') */
    // 这条规则应用到 JavaScript 内的 pug 导入
    {
      use: [
        /* config.module.rule('pug').oneOf('pug-template').use('raw') */
        {
          loader: 'raw-loader'
        },
        /* config.module.rule('pug').oneOf('pug-template').use('pug-plain-loader') */
        {
          loader: 'pug-plain-loader'
        }
      ]
    }
  ]
}

pug-plain-loader 主要作用是将 pug 模板引擎书写的文件转换成 html。其中主要分为两种情况。

  1. .vue 文件 其中 template 为 <template lang="pug"></template>
  2. .pug 文件 在 js 通过 import 或 require 导入

oneOf:表示对该资源只应用第一个匹配的规则,一般结合 resourceQuery。

上述配置中 oneOf 为一个数组数据内第一条通过 resourceQuery=/^\?vue/ 匹配 .vue 文件中 <template lang="pug"></template> 部分的代码, 如果无法匹配则为 .pug 文件。

注:raw-loader 此 loader 主要是将导入的文件转换成字符串, raw-loader 会破坏 Vue 组件的输出。

demo

test/include/exclude/resource/issuer 的用法和区别

  1. test:一般是提供一个正则表达式或正则表达式的数组,绝对路径符合这个正则的则意味着满足这个条件
  2. include:是一个字符串或者字符串数组,指定目录中的文件需要走这个规则
  3. exclude:同样是一个字符串或者字符串数组,指定目录中的文件不需要走这个规则
  4. resource:就是对 text/include/exclude 的一个对象包装,和他们单独写没有区别
  5. issuer:和 resource 有异曲同工的作用,不过区别在于它是将这个 rule 应用于哪个文件以及这个文件所导入的所有依赖文件
  6. resourceQuery:和 resource 用法一样,不过针对的是匹配结果’?’后面的路径参数,可以调用 resource 中的 text 等
module.exports = {
  modules: {
    rules: [
      {
        test: /\.js?$/,
        include: [path.resolve(__dirname, 'app')],
        exclude: [path.resolve(__dirname, 'app/demo')],
        resource: {
          test: /\.js?$/,
          include: path.resolve(__dirname, 'app'),
          exclude: path.resolve(__dirname, 'app/demo'),
        },
        issuer: {
          test: /\.js?$/,
          include: path.resolve(__dirname, 'app'),
          exclude: path.resolve(__dirname, 'app/demo'),
        },
      },
    ],
  },
}

demo

css

vue-cli 配置:

{
  test: /\.css$/,
  oneOf: [
    /* config.module.rule('css').oneOf('vue-modules') */
    {
      resourceQuery: /module/,
      use: [
        /* config.module.rule('css').oneOf('vue-modules').use('vue-style-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\vue-style-loader\\index.js',
          options: {
            sourceMap: false,
            shadowMode: false
          }
        },
        /* config.module.rule('css').oneOf('vue-modules').use('css-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\css-loader\\dist\\cjs.js',
          options: {
            sourceMap: false,
            importLoaders: 2,
            modules: {
              localIdentName: '[name]_[local]_[hash:base64:5]'
            }
          }
        },
        /* config.module.rule('css').oneOf('vue-modules').use('postcss-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\postcss-loader\\src\\index.js',
          options: {
            sourceMap: false,
            plugins: [
              function () { /* omitted long function */ }
            ]
          }
        }
      ]
    },
    /* config.module.rule('css').oneOf('vue') */
    {
      resourceQuery: /\?vue/,
      use: [
        /* config.module.rule('css').oneOf('vue').use('vue-style-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\vue-style-loader\\index.js',
          options: {
            sourceMap: false,
            shadowMode: false
          }
        },
        /* config.module.rule('css').oneOf('vue').use('css-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\css-loader\\dist\\cjs.js',
          options: {
            sourceMap: false,
            importLoaders: 2
          }
        },
        /* config.module.rule('css').oneOf('vue').use('postcss-loader') */
        {
          loader: 'H:\\code\\own\\test\\node_modules\\postcss-loader\\src\\index.js',
          options: {
            sourceMap: false,
            plugins: [
              function () { /* omitted long function */ }
            ]
          }
        }
      ]
    },
    /* config.module.rule('css').oneOf('normal-modules') */
    {
      test: /\.module\.\w+$/,
      use: [
        /* config.module.rule('css').oneOf('normal-modules').use('vue-style-loader') */
        // 配置同 resourceQuery: /module/

        /* config.module.rule('css').oneOf('normal-modules').use('css-loader') */
        // 配置同 resourceQuery: /module/

        /* config.module.rule('css').oneOf('normal-modules').use('postcss-loader') */
        // 配置同 resourceQuery: /module/
      ]
    },
    /* config.module.rule('css').oneOf('normal') */
    {
      use: [
        /* config.module.rule('css').oneOf('normal').use('vue-style-loader') */
        // 配置同 resourceQuery: /\?vue/

        /* config.module.rule('css').oneOf('normal').use('css-loader') */
        // 配置同 resourceQuery: /\?vue/

        /* config.module.rule('css').oneOf('normal').use('postcss-loader') */
        // 配置同 resourceQuery: /\?vue/
      ]
    }
  ]
}

demo

上述配置中 oneOf 配置以下四种类型,分别执行 loaders

1.resourceQuery: /module/

匹配 vue template 组件 <style module></style>

2.resourceQuery: /\?vue/

匹配 vue template 组件 <style></style><style scoped></style>

3.test: /.module.\w+$/

匹配 *.module.css 文件

处理如 vue 中 import moduleCss from './css/test.module.css' 类似代码

/img/vue-cli/css2.png

4.其他

上述条件都不满足时执行的 loaders 文件

匹配到文件后先后执行 postcss-loadercss-loadervue-style-loader

postcss-loader postcss 主要有两个功能:1.将 css 解析成 javascript 可以操作的 ast 语法树,2.使用 postcss 附带的插件处理 ast 并得到结果。

/img/vue-cli/css1.png

css-loader css-loader 主要用来处理 css 文件中的 url 路径,把 css 文件变成一个模块。

vue-style-loader 这个项目其实是 fork 的 style-loader 其配置与 style-loader 一致,主要作用是将生成的 css 代码通过 style 标签的方式插入到 html 代码中,其中 sourceMap: false 表示不生成样式文件对应的 sourceMap 文件。

使用 vue-style-loader 的目的是将解析后的 css 插入到 html 中,如果希望解析的 css 代码通过 link 标签引入, 则需要 mini-css-extract-plugin

var MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  // 其它选项...
  module: {
    rules: [
      // ... 忽略其它规则
      {
        test: /\.css$/,
        use: [
          process.env.NODE_ENV !== 'production'
            ? 'vue-style-loader'
            : MiniCssExtractPlugin.loader,
          'css-loader',
        ],
      },
    ],
  },
  plugins: [
    // ... 忽略 vue-loader 插件
    new MiniCssExtractPlugin({
      filename: 'style.css',
    }),
  ],
}

postcss

vue-cli 配置:

 {
    test: /\.p(ost)?css$/,
    oneOf: [
      // 配置同 css 故省略
      // ...
    ]
 }

scss

scss 是 sass3 引入的新语法其语法完全兼容 scss,并继承了 sass 的功能,文件后缀名不同。书写方式中 sass 严格遵守缩进式语法, 不带 {};

vue-cli 配置:

 {
    test: /\.scss$/,
    oneOf: [
      {
        resourceQuery: /module/,
        use: [
          // 配置同 css 故省略
          // ...
          {
            loader: 'sass-loader',
            options: {
              sourceMap: false
            }
          }
        ]
      }
    ]
 }

sass

vue-cli 配置:

 {
    test: /\.scss$/,
    oneOf: [
      {
        resourceQuery: /module/,
        use: [
          // 配置同 css 故省略
          // 此配置为最后
          // ...
          {
            loader: 'sass-loader',
            options: {
              sourceMap: false
            }
          }
        ]
      }
      // 配置同 css 故省略
      // ...
    ]
 }

less

vue-cli 配置:

 {
    test: /\.less$/,
    oneOf: [
      {
        resourceQuery: /module/,
        use: [
          // 配置同 css 故省略
          // 此配置为最后
          // ...
          {
            loader: 'less-loader',
            options: {
              sourceMap: false
            }
          }
        ]
      }
      // 配置同 css 故省略
      // ...
    ]
 }

stylus

vue-cli 配置:

 {
    test: /\.styl(us)?$/,
    oneOf: [
      {
        resourceQuery: /module/,
        use: [
          // 配置同 css 故省略
          // 此配置为最后
          // ...
           loader: 'stylus-loader',
           options: {
            sourceMap: false,
            preferPathResolver: 'webpack'
           }
        ]
      }
      // 配置同 css 故省略
      // ...
    ]
 }

/img/vue-cli/css3.png

js

vue-cli 配置:

{
  test: /\.m?jsx?$/,
  exclude: [
    function () { /* omitted long function */ }
  ],
  use: [
    /* config.module.rule('js').use('cache-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\cache-loader\\dist\\cjs.js',
      options: {
        cacheDirectory: 'H:\\code\\own\\test\\node_modules\\.cache\\babel-loader',
        cacheIdentifier: '436b10c2'
      }
    },
    /* config.module.rule('js').use('babel-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\babel-loader\\lib\\index.js'
    }
  ]
}

上述配置中支持项目中引用后缀名为 .mjs(es module)、.jsx.js 文件, 使用 babel-loader 解析目标文件,并将解析的结果使用 cache-loader 缓存。

MJS 相关

cacheDirectory、cacheIdentifier 这两个选项需要同时配置并配合 cache-loader 使用开启文件系统的编译缓存。cacheDirectory 表示编译缓存存放位置,cacheIdentifier 表示缓存的标识符。

eslint

vue-cli 配置:

{
  enforce: 'pre',
  test: /\.(vue|(j|t)sx?)$/,
  exclude: [
    /node_modules/,
    'H:\\code\\own\\test\\node_modules\\@vue\\cli-service\\lib'
  ],
  use: [
    /* config.module.rule('eslint').use('eslint-loader') */
    {
      loader: 'H:\\code\\own\\test\\node_modules\\eslint-loader\\index.js',
      options: {
        extensions: [
          '.js',
          '.jsx',
          '.vue'
        ],
        cache: true,
        cacheIdentifier: '84300228',
        emitWarning: false,
        emitError: false,
        eslintPath: 'H:\\code\\own\\test\\node_modules\\eslint',
        formatter: undefined
      }
    }
  ]
}
  1. enforce: 'pre' 编译前检查
  2. test: /\.(vue|(j|t)sx?)$/ 匹配 vue 、js、jsx、ts、tsx
  3. exclude 排除 node_modules 中文件
  4. 使用 eslint-loader 还需要配合 eslint.js
  5. eslint-loader options.cache:true 将校验的结果缓存到 ./node_modules/.cache/eslint-loader
  6. eslint-loader options.cacheIdentifier:84300228 表示缓存的标识符
  7. eslint-loader options.emitWarning:false eslint 校验失败不发出警告
  8. eslint-loader options.emitError:false eslint 校验失败不报错
  9. eslint-loader options.eslintPath:'H:\\code\\own\\test\\node_modules\\eslint' eslint eslint-loader 所依赖 eslint 位置 eslint-loader

未完待续…..