细说webpack 5. webpack的常见配置(下)

3,856 阅读5分钟
大家好!我是萝卜,上篇介绍了与 entry 和 output 相关的配置,这一篇将介绍 Webpack 其他重要配置。

resolve

Webpack 进行构建的时候会从入口文件(entry)开始遍历寻找各个模块的依赖,resolve 配置是帮助 Webpack 查找依赖模块的,通过 resolve 的配置,可以帮助 Webpack 快速查找依赖,也可以替换对应的依赖(比如开发环境用 dev 版本的 lib 等)。下面来介绍下常用的 resolve 配置。
1.resolve.extensions
resolve.extensions是帮助 Webpack 解析扩展名的配置,默认值:['.wasm', '.mjs', '.js', '.json'],所以我们引入 js 和 json 文件,可以不写它们的扩展名,通常我们可以加上 .css、.less等,但是要确保同一个目录下面没有重名的 css 或者 js 文件,如果存在的话,还是把路径写全吧。
module.exports = {
  resolve: {
      extensions: [‘.js’, ‘.json’, ‘.css’]
  }
}
2. resolve.alias
resolve.alias 是最常用的配置,通过设置 alias 可以帮助 webpack 更快查找模块依赖,而且也能使我们编写代码更加方便。例如,我们在实际开发中经常会把源码都放到src文件夹,目录结构如下:
src 
├── lib 
│ └── utils.js 
└── pages 
└── demo 
└── index.js
在 src/pages/demo/index.js 中如果要引用 src/lib/utils.js 那么可以通过:import utils from '../../lib/utils' ,如果目录更深一些,会越来越难看,这时可以通过设置 alias 来缩短这种写法,例如:
module.exports = {
  resolve: {
      src: path.resolve(__dirname, ‘src’),
     ‘@lib’: path.resolve(__dirname, ‘src/lib’)
  }
}
经过设置了 alias,我们可以在任意文件中,不用理会目录结构,直接使用 require('@lib/utils')或者 require('src/lib/utils') 来帮助 Webpack 定位模块。
Tips:alias 的名字可以使用 @ ! ~等这些特殊字符,实际使用中 alias 都使用一种,或者不同类型使用一种,这样可以跟正常的模块引入区分开,增加辨识度。
3. resolve.mainFields
有一些我们用到的模块会针对不同宿主环境提供几份代码,例如提供 ES5 和 ES6 的两份代码,或者提供浏览器环境和 nodejs 环境两份代码,这时候在package.json文件里会做如下配置:
{
    ‘jsnext:main’: ‘es/index.js’, //采用ES6语法的代码入口文件
    ‘main’: ‘lib/index.js’, //采用ES5语法的代码入口文件,node
    ‘brower’: ‘lib/web.js’ //这个是专门给浏览器用的版本
}
在 Webpack 中,会根据 resolve.mainFields 的设置去决定先使用哪个版本的模块代码。mainFields 默认配置如下:
module.exports = {
    resolve: {
     mainFields: [‘brower’, ‘main’, ‘jsnext:main]
    }
}
webpack 会按照数组里的顺序在 package.json 文件里寻找,只会使用找到的第1个文件。假如我们想优先采用ES6的那份代码,则可以这样配置:
module.exports = {
    resolve: {
        mainFields: [‘jsnext:main, ‘main’, ‘brower’]
    }
}
下面是不常用的或者比较简单的配置:
  • resolve.mainFiles:解析目录时候的默认文件名,默认是 index ,即查找目录下面的index+resolve.extensions文件;
  • resolve.modules:查找模块依赖时,默认是node_modules;
  • resolve.symlinks:是否解析符合链接(软连接,symlink);
  • resolve.plugins:添加解析插件,数组格式;
  • resolve.cachePredicate:是否缓存,支持 boolean 和 function,function 传入一个带有 path 和 require 的对象,必须返回 boolean 值。

module

在 webpack 解析模块的同时,不同的模块需要使用不同类型的模块处理器来处理,这部分的设置就在 module 配置中。module 有两个配置:module.noParse 和 module.rules。
1. module.noParse 

module.noParse 配置项可以让 Webpack 忽略对部分没采用模块化的文件的递归解析和处理,这样做的好处是能提高构建性能,接收的类型为正则表达式,或者正则表达式数组或者接收模块路径参数的一个函数:

module.exports = {
    module: {
        // 使用正则表达式
        noParse: /jquery|loadsh/
        // 使用函数
        noParse: (content) => {
            // content 代表一个模块的文件路径
            // 返回 true 或者 false
            return /jquery|loadsh/.test(content);
        }
    }
}
Tips:这里一定要确定被排除出去的模块代码中不能包含 import、require、define等内容,以保证 webpack 的打包包含了所有的模块,不然会导致打包出来的 js 因为缺少模块而报错。
2.parser 控制模块化语法
因为webpack是以模块化的JavaScript文件为入口,所以内置了对模块化JavaScript的解析功能。支持AMD、Commonjs、SystemJs、ES6。parse属性可以更细粒度的配置哪些模块语法要解析,哪些不解析。简单来说,如果设置parser.commonjs=false,那么代码里面使用commonjs的require语法引入模块,对应的模块就不会被解析到依赖中,也不会被处理,支持的选项包括:
Module: {
    Rules: [{
        Test: /\.js$/,
        Use: [‘babel-loader’],
        Parser: {
            amd: false, // 禁用 AMD
            commonjs: false, // 禁用 CommonJS
            system: false, // 禁用 SystemJS
            harmony: false, // 禁用 ES6 import/export
            requireInclude: false, // 禁用 require.include
            requireEnsure: false, // 禁用 require.ensure
            requireContext: false, // 禁用 require.context
            requireJs: false, // 禁用 requirejs
            browserrify: false // 禁用 browserify

        }
    }]
}
Tips:parser是语法层面的限制,noParse只能控制哪些文件不进行解析。
3.module.rules
module.rules 是在处理模块时,将符合规则条件的模块,提交给对应的处理器来处理,通常用来配置 loader,其类型是一个数组,数组里每一项都描述了如何去处理部分文件。每一项 rule 大致可以由以下三部分组成:
  • 条件匹配:通过 test、include、exclude等配置来命中可以应用规则的模块文件;
  • 应用规则:对匹配条件通过后的模块,使用 use 配置项来应用 loader,可以应用一个 loader 或者按照从后往前的顺序应用一组 loader,当然我们还可以分别给对应 loader 传入不同参数;
  • 重置顺序:一组 loader 的执行顺序默认是**从后到前(或者从右到左)**执行,通过 enforce 选项可以让其中一个 loader 的执行顺序放到最前(pre)或者是最后(post)。
4.条件匹配
如上所述,条件匹配相关的配置有test、include、exclude、resource、resourceQuery和issuer。条件匹配的对象包括三类:resource,resourceQuery和issuer。
resource:请求文件的绝对路径。它已经根据 resolve 规则解析;
issuer: 被请求资源(requested the resource)的模块文件的绝对路径,即导入时的位置。
举例来说明:从 app.js 导入 './style.css?inline':
  • resource 是/path/to/style.css;
  • resourceQuery 是?之后的 inline;
  • issuer 是/path/to/app.js。
再举一例,下面rule 的配置项,匹配的条件为:来自 src 和 test 文件夹,不包含 node_modules 和 bower_modules 子目录,模块的文件路径为.tsx和.jsx 结尾的文件。
{
    test: [/\.jsx?$/, /\.tsx?$/],
    include: [
      path.resolve(__dirname, ‘src’),
      path.resolve(__dirname, ‘test’)
    ],
    exclude: [
     path.resolve(__dirname, ‘node_modules’),
     path.resolve(__dirname, ‘brower_modules’)
    ]
}

loader 配置

Loader 是解析处理器,通过 loader 我们可以将 ES6 语法的 js 转化成 ES5 的语法,可以将图片转成 base64 的 dataURL ,在 JavaScript 文件中直接 import css 和 html 也是通过对应的 loader 来实现的。
我们在使用对应的 loader 之前,需要先安装它,例如,我们要在 JavaScript 中引入 less,则需要安装 less-loader :
npm i -D less-loader
然后在 module.rules 中指定 *.less 文件都是用 less-loader:
module.exports = {
    module: {
        rules: [
          test: /\.less$/,
          use: ‘less-loader’
        ]
    }
}
简单来理解上面的配置,test 项使用 /\.less$/正则匹配需要处理的模块文件(即 less 后缀的文件),然后交给less-loader 来处理,这里的 less-loader 是个 string,最终会被作为 require() 的参数来直接使用。这样 less 文件都会被 less-loader 处理成对应的 css 文件。
1.Loader 的参数
给 loader 传参的方式有两种:
  • 通过options传入。
  • 通过query的方式传入。
// 通过 options 传入
module: {
    rules: [{
        test: /\.html$/,
        use: [{
            loader: ‘html-loader’,
            options: {
                minimize: true,
                removeComments: false,
                collapseWhitespace: false
            }
         }]
    }]
}
// 通过 queyr 传入
module: {
     rules: [{
         test: /\.html$/,
         use: [{
            loader: ‘html-loader?Minimize=true&removeComments=false&collapseWhitespace=false’,
         }]
     }]
}

Plugin 插件

Plugin 是 Webpack 的重要组成部分,通过 plugin 可以解决 loader 解决不了的问题。Webpack 本身就是有很多插件组成的,所以内置了很多插件,我们可以直接通过 webpack 对象的属性来直接使用,例:webpack.optimize.UglifyJsPlugin
Module.exports = {
    // ...
    Plugins: [
    // 压缩 js
       New webpack.optimize.UglifyJsPlugin()
    ]
}
除了内置的插件,我们也可以通过 NPM 包的方式来使用插件:
// 非默认的插件
const ExtractTextPlugin = require(‘extract-text-webpack-plugin’);
Tips:loader 面向的是解决某个或者某类模块的问题,而 plugin 面向的是项目整体,解决的是 loader 解决不了的问题。

小结

在这一篇中,讲解了Webpack 除entry和 output 外的基础配置项,这里总结下项目经常配置的并且比较重要的配置项列表,供大家复习内容:
  • resolve:模块依赖查找相关的配置;
  • resolve.extensions:可以省略解析扩展名的配置,配置太多反而会导致webpack 解析效率下降;
  • resolve.alias:通过设置 alias 可以帮助 webpack 更快查找模块依赖,精简代码书写时相对路径的书写;
  • module.rules:loader 相关的配置,每个 rule 重要的内容有:
test:正则匹配需要处理的模块文件;
use:loader 数组配置,内部有 loader 和 options;
include:包含;exclude:排除;
plugins:插件。