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