# Plugin
# html-webpack-plugin
我们使用固定的模板,在每次打包的时候自动生成一个 index.html 文件,并且它会自动帮我们引入我们打包后的 .js 文件。
安装
npm install --save-dev html-webpack-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [new HtmlWebpackPlugin()]
}
src/index.js
console.log(1);
src/index.html
<html>
<head>
<meta charset="UTF-8" />
<title>webpack App</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
打包后,可以看到 dist 目录下自动生成了一个 html 文件,文件内容如下:
dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1"><script defer src="main.js"></script></head>
<body>
</body>
</html>
这个html文件中自动引入了我们打包后的 JS 文件。
以上是 html-webpack-plugin 初步使用,后续会介绍它更高级的使用。
# clean-webpack-plugin
这个插件的主要作用是在运行打包命令前,删除 dist 目录中之前生成的文件,以保证 dist 目录里是最新的文件。
安装
npm install --save-dev clean-webpack-plugin
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: './src/index.js',
output: {
filename: 'main.js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
随便在 dist 目录下新建一个文件,然后运行打包命令 npx webpack 进行打包,可以看到刚随便新建的文件被删除了。
这里只介绍了两种我们最最常用的两个 plugin 更多 plugin 请查阅官方文档 (opens new window) 。
# Entry和Output
# 多文件入口的配置
src/index.js
console.log('index');
src/main.js
console.log('main');
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
执行打包命令后,发现 dist 目录下生成了 index.js 和 main.js 两个文件,index.html 文件中自动引入了这两个文件。
# 使用CDN
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
// CND 地址
publicPath: '//cdn.bootcdn.net',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
打包后的文件 dist/index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Webpack App</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<script defer src="//cdn.bootcdn.net/index.js"></script>
<script defer src="//cdn.bootcdn.net/main.js"></script>
</head>
<body>
</body>
</html>
# SourceMap
它是一种映射关系,它映射了打包后的代码和源代码之间的对应关系,一般通过 devtool 来配置

从上图可以看出给 devtool 配置合适的sourcemap 不仅有利于提高打包速度还有利于调试错误、代码维护。
- 开发环境下:推荐使用cheap-module-eval-source-map
Webpack5 中上面这样使用会报错

改为:eval-cheap-module-source-map
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: {
index: './src/index.js',
main: './src/main.js'
},
output: {
publicPath: '//cdn.bootcdn.net',
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
- 生产环境下:推荐使用cheap-module-source-map
# 自动打包
在前面我们修改文件后想要看到效果必须执行两个操作
- 重新运行打包命令
- 刷新浏览器
这样的操作好烦人,有没有什么当我们修改文件后自动打包、自动刷新浏览器的方法呢?
# --watch
src/index.js
const root = document.getElementById('root')
const div = document.createElement('div')
div.textContent = 'hello'
root.appendChild(div)
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
]
}
在终端执行如下命令:
npx webapck --watch
当我们再修改 index.js 文件时,就会自动打包了,但是不会自动刷新浏览器。
# webpack-dev-server
安装
npm install --save-dev webpack-dev-server
webpack.config.js
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
// 如果不指定,不会自动刷新浏览器
target: 'web',
devServer: {
contentBase: './dist',
// 指定使用的端口号
port: 8085,
// 是否自动打开浏览器
open: true
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin()
]
}
当我们像上面这样配置完成之后,当我们再次修改 index.js 文件后,不但会被自动打包,还会自动刷新浏览器。
# 热更新(HMR)
src/number.js
export function displayNum() {
const num = document.createElement('div')
num.setAttribute('id', 'num')
num.textContent = '1'
document.body.appendChild(num)
}
src/createInput.js
export function createInput() {
const input = document.createElement('input');
document.body.appendChild(input);
}
src/index.js
import './index.scss'
import { displayNum } from './number'
import { createInput } from './createInput'
createInput()
displayNum()
启动项目后,在输入框随意输入些内容
当我们将 1 修改为 11 时,虽然页面变化了,但是我们在输入框中输入的内容也没了。这样明显不合适,有什么办法既可以修改数字还能保持输入框的内容呢?这时我们可以使用模块热替换。
模块热替换(HMR - hot module replacement)功能会在应用程序运行过程中,替换、添加或删除 模块,而无需重新加载整个页面。主要是通过以下几种方式,来显著加快开发速度:
- 保留在完全重新加载页面期间丢失的应用程序状态。
- 只更新变更内容,以节省宝贵的开发时间。
- 在源代码中 CSS/JS 产生修改时,会立刻在浏览器中进行更新,这几乎相当于在浏览器 devtools 直接更改样式。
const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
contentBase: './dist',
port: 8085,
open: true,
hot: true,
hotOnly: true
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}不。
# JS热更新
对上述 index.js 文件做如下修改,当我们再次启动项目,修改 number.js 中值的时候,输入框中的内容就不会被清除掉了。
src/index.js
import './index.scss'
import { displayNum } from './number'
import { createInput } from './createInput'
createInput()
displayNum()
if (module.hot) {
module.hot.accept('./number.js', function() {
document.body.removeChild(document.getElementById('num'))
displayNum()
})
}
如果浏览器出现如下提示,可以通过取消 webpack.config.js 文件中的 hotOnly: true 来消除。
Ignored an update to unaccepted module ./src/index.js
# CSS热更新
借助于 style-loader,使用模块热替换来加载 CSS 实际上极其简单。此 loader 在幕后使用了 module.hot.accept,在 CSS 依赖模块更新之后,会将其 patch(修补) 到 <style> 标签中。
src/index.scss
body {
background: blue;
}
src/index.js
import './index.scss'
import { createInput } from './createInput'
createInput()
src/createInput.js
export function createInput() {
const root = document.getElementById('root');
const input = document.createElement('input');
root.appendChild(input);
console.log(44);
}
webpack.config.js
const path = require('path');
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
module.exports = {
mode: 'development',
devtool: 'eval-cheap-module-source-map',
entry: './src/index.js',
output: {
filename: '[name].js',
path: path.resolve(__dirname, 'dist')
},
target: 'web',
devServer: {
contentBase: './dist',
port: 8085,
open: true,
hot: true,
hotOnly: true
},
module: {
rules: [
{
test: /\.s[ac]ss$/i,
use: ["style-loader",
{
loader: "css-loader",
options: {
modules: true
}
},
"sass-loader", "postcss-loader"],
}
]
},
plugins: [
new HtmlWebpackPlugin({
template: 'src/index.html'
}),
new CleanWebpackPlugin(),
new webpack.HotModuleReplacementPlugin()
]
}
运行 npm start 命令后,在启动的页面的输入空中随便输入点内容
当我们将 index.scss 文件中的样式从蓝色改为红色后,在到浏览器中查看,样式变成了红色,输入框中的内容还在。
# ES6语法
src/index.js
const testES6 = () => {}
注释掉 webpack.config.js 中的 devtool 配置项。终端运行 npx webpack 命令。查看打包后的 JS 文件。

上面我们在代码中使用了 ES6 的箭头函数,打包后的文件也是 ES6 的箭头函数,但是有些浏览器对 ES6 支持的不是很友好,有可能报错,怎么解决这种问题呢?
# 使用 babel-loader 转换ES6代码
安装
npm install -D babel-loader @babel/core @babel/preset-env
配置 webpack.config.js
module: {
rules: [
{
test: /\.m?js$/,
exclude: /(node_modules|bower_components)/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env']
}
}
}
]
}
查看打包后的输出文件:
dist/main.js
// 省略其他代码
eval("var testES6 = function testES6() {};\n\n//# sourceURL=webpack://early-experience/./src/index.js?");
// 省略其他代码
可以看到箭头函数被转换成了 function 函数。