乐趣区

细说webpack-5-webpack的常见配置下

大家好!我是萝卜,上篇介绍了与 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:插件。
退出移动版