前言-为什么要使用webpack
- 转换ES6
- 转换jsx(vue、ts等 )
- css前缀补全/预处理器(sass、less等)
- 图片压缩
- 压缩混淆
配置⽂件名称
webpack 默认配置⽂文件:webpack.config.js,可通过webpack --config来指定配置文件。
在webpack.config.js中,除了我们常用的导出对象的方式,还有多种配置类型。
- 导出为一个函数
module.exports = function(env, argv) {
return {
mode: env.production ? 'production' : 'development',
devtool: env.production ? 'source-maps' : 'eval',
...
};
};
该函数会传入两个参数。一个是环境对象(environment),另一个是map对象。
- 导出一个 Promise
这种方法便于需要异步地加载所需的配置变量。
module.exports = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
entry: './app.js',
/* ... */
})
}, 5000)
})
}
- 导出多个配置对象
当运行
webpack时,所有的配置对象都会构建。例如,导出多个配置对象,对于针对多个构建目标(例如 AMD 和 CommonJS)打包一个 library 非常有用。
module.exports = [{
output: {
filename: './dist-amd.js',
libraryTarget: 'amd'
},
entry: './app.js',
mode: 'production',
}, {
output: {
filename: './dist-commonjs.js',
libraryTarget: 'commonjs'
},
entry: './app.js',
mode: 'production',
}]
下面我们介绍一些配置文件中的核心配置概念。
核心概念--Entry
入口起点(entry point)指示webpack应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack会找出有哪些模块和库是入口起点(直接和间接)依赖的。
我们可以在entry中设置一个或多个入口文件,默认值为 ./src。
- 设置单个入口
用法:entry: string|Array
// webpack.config.js
module.exports = {
entry: './path/to/my/entry/file.js'
};
向 entry 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”。在你想要多个依赖文件一起注入,并且将它们的依赖导向(graph)到一个“chunk”时,传入数组的方式就很有用。
即当使用Array<string>时,仍会仅生产一个bundle文件(下同,会生成对应的一个chunk文件)。
- 设置多个入口
用法:entry: {[entryChunkName: string]: string|Array}
// webpack.config.js
module.exports = {
app: './src/app.js',
vendors: './src/vendors.js'
};
上面的单个入口同下面的配置:
// webpack.config.js
module.exports = {
main: './path/to/my/entry/file.js'
};
常见场景
- 多页面应用程序
const config = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
每个HTML页面单独引入一个index.js,需要3个分离的依赖图。
这里可以使用CommonsChunkPlugin来抽取公共逻辑代码,减少。
当然,这种写法在每次新增或删除⻚页⾯,都需要改webpack配置。这里我们可以使用glob.sync来动态设置。
entry: glob.sync(path.join(__dirname, './src/*/index.js'))
- 用于实现组件库的压缩版本和非压缩版本
const config = {
entry: {
"index": "./src/index.js",
"index.min": "./src/index.js"
},
optimization: {
minimize: true,
minimizer: [
new TerserPlugin({
include: /\.min\.js$/,
}),
],
}
};
这里我们使用TerserPlugin来实现代码压缩(后面介绍),并且仅压缩*.min.js文件。
当然,在使用的该组件库的时候,我们可以设置package.json的main为index.js,在该文件中动态引入压缩版本和非压缩版本。
// index.js
if (process.env.NODE_ENV === "production") {
module.exports = require("./dist/index.min.js");
} else {
module.exports = require("./dist/index.js");
}
核心概念--Output
配置
output选项可以控制webpack如何向硬盘写入编译文件。注意,即使可以存在多个入口起点,但只指定一个输出配置。
output属性的最低要求为必须是一个对象,且具有以下两个属性(更多的属性说明见这里):
- filename,用于输出文件的名称
- path,用于输出文件所在目录的绝对路径
- 单入口配置
module.exports = {
entry: './path/to/my/entry/file.js',
output: {
filename: 'bundle.js’,
path: __dirname + '/dist'
},
};
- 多入口配置
module.exports = {
entry: {
app: './src/app.js',
vendors: './src/vendors.js'
},
output: {
filename: '[name].js',
path: __dirname + '/dist'
},
};
由于多个入口,会生成多个bundle文件,所以这里使用占位符来进行区分。
核心概念-Loaders
webpack开箱即用只支持JS和JSON两种文件类型,需要loader将其他类型的文件转为webpack能够处理的文件类型,并添加到依赖图中完成打包。
loader支持链式传递。
loader类似于其他构建工具中“任务(task)”,本身是一个函数。传递链中的第一个loader将返回值传递给下一个loader。在最后一个 loader,返回 webpack 所预期的 JavaScript。
loader可以是同步的,也可以是异步的。loader接收查询参数,用于对loader传递配置;也能够使用options对象进行配置。
常见的loader
| 名称 | 描述 |
|---|---|
| babel-loader | 转换ES6等JS新语法特性 |
| ts-loader | 将TS转为JS |
| css-loader | 支持.css文件的加载和解析 |
| less-loader | 将less文件转为.css文件 |
| file-loader | 处理图片文件 |
| raw-loader | 将文件以字符串的形式导入,比如.txt文件 |
| less-loader | 将less文件转为.css文件 |
| thread-loader | 多进程打包css和js文件 |
更多loader可以参考这里。
使用loader
我们一般在配置文件中通过module.rules来设置loader使用方式。
注:还有内联方式(每个import语句中显式指定loader)和CLI方式,但是不推荐。
module.exports = {
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
其中,test用于匹配待处理的文件;use表示使用的loader,当需要使用多个loader时,use的值可以为数组,其loader的执行顺序为从右到左。
module: {
rules: [
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
}
]
}
]
}
如上所示,处理.css文件时,会先执行css-loader,再执行style-loader。
此外,除了调整loader的顺序,也可以设置loader的enforce属性来调整执行的优先级。
- pre 优先处理
- normal 正常处理(默认)
- inline 其次处理(不推荐)
- post 最后处理
module: {
rules: [
{
test: /\.less$/,
use: 'less-loader',
enforce: 'pre'
},
{
test: /\.less$/,
use: 'css-loader'
},
{
test: /\.less$/,
use: 'style-loader',
enforce: 'post'
}
]
},
在上面的配置文件,loader的执行顺序就是:less-loader-->css-loader-->style-loader。
核心概念-Plugins
插件是webpack的支柱功能,用于bundle文件的优化,资源管理理和环境变量量注⼊入,即解决loader无法处理的事情,会作用于整个构建过程。
插件是一个具有apply方法的JavaScript对象。apply方法会被webpack compiler调用,并且在整个编译生命周期都可以访问compiler对象。
插件就是一个含有apply方法的类,在apply方法中会传入compiler(即webpack实例)。我们可以通过调用compiler中暴露的钩子函数,完成在webpack构造过程的相关功能。
class MyExampleWebpackPlugin {
apply(compiler) {
compiler.hooks.emit.tapAsync(
'MyExampleWebpackPlugin',
(compilation, callback) => {
...
callback();
}
);
}
}
这里调用的钩子函数的tapAsync方法(异步调用),取决于不同的钩子类型,也可以在某些钩子上访问tap(同步调用)和tapPromise。
更多插件相关内容可参考这里。
常用插件
| 名称 | 说明 |
|---|---|
| CommonsChunkPlugin | 提取块之间共享的通用模块 |
| CopyWebpackPlugin | 将单个文件或整个目录复制到构建目录 |
| CleanWebpackPlugin | 清理构建目录 |
| MiniCssExtractPlugin | 将CSS单独提取为一个文件 |
| HotModuleReplacementPlugin | 启用热模块更换(HMR) |
| HtmlWebpackPlugin | 轻松创建HTML文件来服务您的捆绑软件 |
| TerserPlugin | 使用Terser缩小项目中的JS |
更多插件内容可以参考这里。
Webpack构建流程
Webpack的运行流程是一个串行的过程,从启动到结束会依次执行以下流程:
- 初始化参数
从配置文件和CLI配置语句中读取与合并参数,得出最终的参数。
- 开始编译
用上一步得到的参数初始化Compiler对象,加载所有配置的插件,执行对象的run方法开始执行编译。
- 确定入口
根据配置中的entry找出所有的入口文件
- 编译模块
从入口文件出发,调用所有配置的loader对模块进行翻译,再找出该模块依赖的模块,再递归本步骤直到所有入口依赖的文件都经过了本步骤的处理。
- 完成模块编译
- 输出资源
根据入口和模块之间的依赖关系,组装成一个个包含多个模块的chunk文件,再把每个chunk文件转换成一个单独的bundle文件加入到输出列表。
- 输出完成
在确定好输出内容后,根据配置确定输出的路径和文件名,把文件内容写入到文件系统。
总结
Webpack配置文件中,通过entry设置编译入口文件,在output设置编译后的文件目录,通过loader引入其他类型文件,通过plugin来实现一些loader无法达到的功能。