开场之前,先隆重的介绍一下今天的主角webpack。它是个啥呢?用官方的话说,webpack 是一个用于现代 JavaScript 应用程序的 静态模块打包工具。需要特别说明的一点是,webpack配置文件遵循的common.js语法规范的。
一、核心概念
在使用webpack进行打包之前,需要先了解webpack的几个核心概念:
- enrty(入口)
- output(出口)
- loader
- plugin(插件)
- mode(模式)
二、webpack安装配置
自从webppack4.0之后,webpack就多了一个搭配使用的好兄弟——webpack-cli。想用webpack就必须同时安装webpack-cli。凭啥官网说要一起用就非得一起用啊,我不能整点花活装一下?经过一番探索之后,在webpack的源码中发现下面这段代码,断绝了我的念想:
const cli = {
installed: isInstalled("webpack-cli"),
};
if (!cli.installed) {
console.error(`We will use "${packageManager}" to install the CLI via "${packageManager} ${installOptions.join("")} ${cli.package}".`;
} else {
runCli(cli);
}
当然,webpack-cli其实是很有用处的,比如说,想要执行一次打包 npx webapack --configwebpack-cli提供的能力。在package.json里面配置命令行,可以将复杂的打包命令简化。
"start": "webpack serve --open --config ./config/webpack.dev.js",
"build": "webpack --config ./config/webpack.prod.js"
说到这里,不知道有没有人对webpack的配置文件命名产生过好奇。不知道大家有没有对webpack.config.js这个文件名称产生好奇,反正我是很好奇。经过一番探索发现,原来如此:
const defaultConfigFiles = [
"webpack.config",
".webpack/webpack.config",
".webpack/webpackfile",
]
.map((filename) =>
// Since .cjs is not available on interpret side add it manually to default config extension list
[...Object.keys(interpret.extensions), ".cjs"].map((ext) => ({
path: path.resolve(filename + ext),
ext: ext,
module: interpret.extensions[ext],
})),
)
.reduce((accumulator, currentValue) => accumulator.concat(currentValue), []);
二、entry(入口)常见应用场景
最简单的单文件入口配置:
entry: './path/to/my/entry/file.js',
在webpack优化的时候,会将第三方依赖或者公共文件剥离出来,这时会用到多入口配置,当然这里只说一下entry配置,具体优化实现,等到我们后面讲到webpack打包优化的时候再具体说一下:
entry: {
main: './src/app.js',
vendor: './src/vendor.js',
},
多页面的时候,配置多入口文件:
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js',
},
三、output(出口)
通过配置ouput选项,告诉webpack程序往哪里输出编译文件。需要注意的是,即使是存在多个入口文件,也只有一个出口文件,而且出口文件必须是绝对路径(通过path模块处理路径)。 如果使用相对路径,打包编译的时候会报错:
[webpack-cli] Invalid configuration object. Webpack has been initialized using a configuration object that does not match the API schema.
- configuration.output.path: The provided value "./dist" is not an absolute path!
-> The output directory as **absolute path** (required).
四、loader
在开始使用loader之前,我们先介绍一下loader的执行顺序:从右到左、从下到上。这是一种很常见的函数式编程逻辑。具体实现逻辑需要查看loader-runner源码,在 iteratePitchingLoaders 函数中有这样一段代码
if loaderContext.loaderIndex >= loaderContext.loaders.length) {
return processResource(options, loaderContext, callback);
}
1.处理css样式
{
test: /\.css$/,
use: [
{ loader: 'style-loader' },
{
loader: 'css-loader',
options: {
modules: true
}
},
{ loader: 'sass-loader' }
]
}
- 处理图片文件 在webpack 5 中,可以使用内置的 Asset Modules 模块来处理文件资源,类似于 file-loader
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
- 处理字体文件
{
test: /.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
},
五、plugins(插件)
- html-webpack-plugin
new HtmlWebpackPlugin({
title: '首页',
template: path.resolve(__dirname, '../public/index.html'),
favicon: path.resolve(__dirname, '../public/favicon.svg'),
meta: {
keywords: 'webpack5, webpack',
description: 'webpack 是一个模块打包器。它的主要目标是将 JavaScript 文件打包在一起,打包后的文件用于在浏览器中使用,但它也能够胜任转换(transform)、打包(bundle)或包裹(package)任何资源(resource or asset)。',
author: 'wyf',
viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no',
},
minify: true,
chunks: ['main']
}),
- clean-webpack-plugin
new CleanWebpackPlugin()
需要特殊注意的是,clean-webpack-plugin 的新版本之后,抛出的方式不再是export defaults CleanWebpackPlugin,是export { CleanWebpackPlugin },所以引入插件的时候需要注意。
六、mode
通过将配置设置为production|development
来将 DefinePlugin
中 process.env.NODE_ENV
的值设置为production|development
,如果没有设置,webpack 会给 mode
的默认值设置为 production
。
我在使用中发现,通过mode配置存在process.env.NODE_ENV为undefined的情况,这个时候,不要慌,通过在script命令中配置set NODE_ENV=production就可以轻松解决了。