在上一篇 Webpack 快速上手中,我们对 Webpack 已经有了一个大概的了解,现在我们开始了解 Webpack 的配置。
webpack.config.js
const path = require('path');
// 引入 html-webpack-plugin
const HtmlWebpackPlugin = require('html-webpack-plugin')
// 引入 clean-webpack-plugin
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const time = new Date().getTime()
module.exports = {
// 入口
// entry: './src/index.js',
entry: {
index: './src/index.js',
print: './src/print.js',
another: './src/another_module.js'
},
devServer: {
contentBase: './dist', // 资源位置
host: 'localhost', // 服务器地址,如果需要在其他 PC 上访问,则将 'localhost' 改为 '0.0.0.0'
port: '8080',
hot: true // 热加载
},
devtool: 'eval-cheap-module-source-map', // source-map 模式
// 插件
plugins: [
// 清理 ./dist 目录无用的文件
new CleanWebpackPlugin(),
// new webpack.HotModuleReplacementPlugin(),
// 生成 html 文档,并自动引入「输出」文档
new HtmlWebpackPlugin({
title: '管理输出'
})
],
// 输出
output: {
// filename: 'main.js',
filename: '[name].bundle.' + time + '.js', // 输出文件名格式
path: path.resolve(__dirname, 'dist') // 输出文件位置
},
// 代码分离,将重复引用的代码分离出来
optimization: {
splitChunks: {
chunks: 'all'
}
},
// 模块
module: {
rules: [
{
// 样式加载模块
test: /\.css$/i,
use: ['style-loader', 'css-loader']
},
{
// 图像加载模块
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource'
},
{
// 字体加载模块
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource'
},
/*********** 数据加载模块 >>> ************/
{
// csv 加载模块
test: /\.(csv|tsv)$/i,
use: ['csv-loader']
},
{
// xml 加载模块
test: /\.xml$/i,
use: ['xml-loader']
}
// json 加载模块:因 nodejs 默认支持 JSON 加载,所以无需再安装第三方模块
/*********** <<< 数据加载模块 ************/
, {
// babel 加载模块
test: /\.js$/,
// exclude: /(node_mod)/
loader: 'babel-loader',
// include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')]
// use: {
// loader: 'babel-loader',
// options: {
// presets: ['@babel/preset-env']
// }
// }
}
]
}
}
对应的 package.json
{
"name": "webpack-test",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"_start": "webpack serve --open firefox",
"start": "webpack serve --inline --progress",
"build": "webpack"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@babel/cli": "^7.12.10",
"@babel/core": "^7.12.10",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"babel-loader": "^8.2.2",
"clean-webpack-plugin": "^3.0.0",
"css-loader": "^5.0.1",
"csv-loader": "^3.0.3",
"html-webpack-plugin": "^4.5.0",
"style-loader": "^2.0.0",
"webpack": "^5.4.0",
"webpack-cli": "^4.2.0",
"webpack-dev-server": "^3.11.0",
"xml-loader": "^1.2.1"
},
"dependencies": {
"@babel/polyfill": "^7.12.1",
"lodash": "^4.17.20"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
分步讲解
入口
entry
在此我们可以看到,entry
由单条更改为了对象方式,也就是说,我们的入口不仅仅可以只有一个,更是可以拥有多个入口。通过上一篇文章的学习,我们知道 webpack 可以打包使用了的资源,而entry
提供了一个打包的入口,通过这个入口,就可以将入口文件里面所有相关的资源(递归搜索)打包在一起。-
开发模式下的小服务器
devServer
webpack-dev-server
提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。执行命令安装
webpack-dev-server
:
npm install webpack-dev-server -D --registry=https://registry.npm.taobao.org
进行如上配置之后,可以在 package.json
中增加脚本启动方式 start
:
{
"name": "webpack_test",
"version": "1.0.0",
"description": "",
"private": true,
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "webpack serve --inline --progress",
"build": "webpack"
},
"keywords": [],
"...": ...
}
然后在命令行中执行如下命令启动服务器
npm start
此处仅为示例,更多配置请参考 webpack-dev-server
-
开发工具
devtool
此选项控制是否生成,以及如何生成 source map。我们打包过后的代码和我们自己写的源码是不一样的,如果没有 source map ,那么在开发过程中调试程序的时候,代码报错时,我们无法将错误准确的定位到我们的源码中。
有关详细信息请参考 Devtool
-
插件
plugins
plugins
选项用于以各种方式自定义 webpack 构建过程。webpack 附带了各种内置插件,可以通过webpack.[plugin-name]
访问这些插件。请查看 插件页面 获取插件列表和对应文档, 但请注意这只是其中一部分,社区中还有许多插件。如果你想自己写一个插件,请参考如何 编写一个插件
这里我紧用了几个插件示例。
html-webpack-plugin
插件:可以为我们动态生成 html 文档,并自动引入「output」打包之后的文档到该 html 文档中。clean-webpack-plugin
插件:在我们每次打包工程之前,它将为我们清空目标输出目录,以保证输出目录中的资源都是我们最后需要的。 -
输出
output
可以通过配置 output 选项,告知 webpack 如何向硬盘写入编译文件。注意,即使可以存在多个 entry 起点,但只能指定一个 output 配置。此属性的最低要求,是为其配置一个输出文件的文件名
output.filename
output: {
filename: 'bundle.js',
}
如果是多个入口起点
output: {
// filename: 'main.js',
// 输出文件名格式
filename: '[name].bundle.' + time + '.js',
// 输出文件位置
path: path.resolve(__dirname, 'dist')
}
更多配置请参考 output
-
代码分离
optimization
此特性能够把代码分离到不同的 bundle 中,然后可以按需加载或并行加载这些文件。代码分离可以用于获取更小的 bundle,以及控制资源加载优先级,如果使用合理,会极大影响加载时间。常用的代码分离方法有三种:
- 入口起点:使用
entry
配置手动地分离代码。 - 防止重复:使用 Entry dependencies 或者
SplitChunksPlugin
去重和分离 chunk。 - 动态导入:通过模块的内联函数调用来分离代码。
这里用了「防止重复」的方式,并使用了
SplitChunksPlugin
插件,可以将公共的依赖模块提取到已有的入口 chunk 中,或者提取到一个新生成的 chunk。 - 入口起点:使用
-
模块
module
模块 选项决定了如何处理项目中的 不同类型的模块。在这里我使用了一些加载模块
loader
,都在rules
中配置。这些loader
可能来自第三方,需要我们单独安装它们才能正常使用。[Rule]
创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。 这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。