webpack 插件之Html-Webpack-Plugin
1. 为什么我们需要这个插件
先来看一个应用场景。
我们自己打算搭建一个网站,这个网站有很多个页面,我们为每个页面创建一大堆的css样式,js脚本,然后尝试用webpack进行打包。
于是我们在webpack.config.js里这样写了:
const path = require('path');
module.exports = {
//两个页面相应的js
entry:[
"./src/scripts/hello.js",
"./src/scripts/main.js"
],
output:{
filename:'main.bundle.js',
path:path.resolve(__dirname,'dist')
}
}
两个文件的内容如下:
hello.js
function sayHello (){
alert('Hello');
}
sayHello();
main.js
function sayMain(){
alert('Main');
}
sayMain();
但是我们这里的output只有一个path关键字,无疑,它们会被整合到一起。
我们在/dist目录下创建Index.html加载打包后的js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript" src="./main.bundle.js"></script>
</body>
</html>
打开浏览器看看结果
这当然对于浏览器对服务器的请求数减小是极好的,但有的时候我们并不想这样做,因为两个模块没有依赖关系,我们想把它分开来
于是我们打算用webpack把它们打成两个包(在实际的应用场景中,这两个互不依赖的模块可能还有更多的依赖模块),修改过后的webpack.config.js如下
const path = require('path');
module.exports = {
//两个页面相应的js
entry:{
helloModule:"./src/scripts/hello.js",
mainModule:"./src/scripts/main.js"
},
output:{
filename:'main.bundle.js',
path:path.resolve(__dirname,'dist')
}
}
接着用webpack进行打包
webpack抛出了一个错误
Conflict: Multiple assets emit to the same filename main.bundle.js
大致的意思多个资源不能用一个文件名
这里我们可以把output的filename改一下
output:{
filename:'[name].bundle.js',
path:path.resolve(__dirname,'dist')
}
改成[name],这样当webpack打包的时候,会依次把entry里的模块的名称传入进来。
我们顺利打包,并得到两个bundle
[0] ./src/scripts/hello.bundle.js 60 bytes {1} [built]
[1] ./src/scripts/main.bundle.js 57 bytes {0} [built]
然后我们在index.html里依次引入hello.bundle.js和main.bundle.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
</head>
<body>
<script type="text/javascript" src="main.bundle.js"></script>
<script type="text/javascript" src="hello.bundle.js"></script>
</body>
</html>
运行起来也是成功的。
但是这种以模块名命名的bundle总会不安全,想象一个场景,团队合作的时候的模块名冲突,好像并没有什么不妥,但是你需要修改的地方就是模块名,还有Index.html里引入的地方,这样也是麻烦。
那有没有方法可以避免这种冲突呢?
2. 使用唯一标识命名
除开用模块名进行命名,webpack还允许我们使用hash 和 chunkhash进行命名。
下面是hash和chunkhash的命名方式
output:{
filename:'[hash].bundle.js',
path:path.resolve(__dirname,'dist')
}
output:{
filename:'[chunkhash].bundle.js',
path:path.resolve(__dirname,'dist')
}
那么二者有什么不同呢?
对于二者的不同,webpack官网给出了解释:
[hash] is replaced by the hash of the compilation.
译:hash代表的是compilation的hash值。
[chunkhash] is replaced by the hash of the chunk.
译:chunkhash代表的是chunk的hash值。
2.1 什么是hash
在webpack打包的过程中会产生一个compilation对象,这个对象是以文件为对比的,什么意思呢?也就是说,compilation对象代表某个版本的资源对应的编译进程。只要在打包的文件中,有一个文件发生改变,就会废弃当前的compilation对象,而重新创建一个。也就是说,只要有文件进行了变更,每次变更创建的compilation对象都不一样。
而hash是根据compilation对象来的,而不是根据文件来的,每个打包的过程中只会有一个compilation对象,那么hash的值也是唯一的,但是不幸的是,如果是用hash值做为文件名,因为hash在整个打包过程中的值是唯一的,因此会引起冲突。
从上面我们可以知道,当本次打包的源文件中,有一个文件发生了修改,那么整个hash的值就会变,也就是说,每个文件的hash值都是一样的!因此会发生冲突!
output:{
filename:'[hash].bundle.js',
path:path.resolve(__dirname,'dist')
}
用webpack打包,还是会抛出一个错误
Conflict: Multiple assets emit to the same filename main.bundle.js
这也证实了hash是针对compilation唯一的事实。
那么有没有是针对文件的唯一标识,来避免这种冲突呢?
答案就是chunkhash
2.2 了解chunkhash
chunkhash就是针对于文件的,每次文件修改只会修改相对于自己的chunkhash的值,不会影响到其他文件。
用chunkhash来做bundle的文件名再合适不过了。
于是我们修改webpack.config.js
output:{
filename:'[chunkhash].bundle.js',
path:path.resolve(__dirname,'dist')
}
编译通过~!
现在我们成功的避免了命名冲突,但是现在我们必须面临一个问题,每次文件修改后,chunkhash的值会改变,chunkhash又是和bundle的名字绑定在一起,所以我们就必须在每次文件修改完之后去引用它的地方的名字,对于程序员来说,我们需要去掉这种机械重复的劳动。
于是html-webpack-plugin登场了!!!!!
3. 什么是html-webpack-plugin
html-webpack-plugin是webpack里的插件,我们可以通过在webpack.config.js里增加plugins字段来引入这个插件。
它能帮助你自动生成引入这些bundle的html文件,从而减少你的工作量。而你只需要修改一下webpack.config.js即可以引入它。
首先我们需要做的是在这个项目里安装html-webpack-plugin依赖。
npm install --save-dev html-webpack-plugin
等待安装完毕,在webpack.config.js里将它引入
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
hello:"./src/scripts/hello.js",
main:"./src/scripts/main.js"
},
output:{
filename:'[chunkhash].bundle.js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new HtmlWebpackPlugin()
]
}
然后我们用webpack再次打包,可以看到在dist目录下已经产生了打包后的bundle以及一个index.html文件,这个index.html文件是由html-webpack-plugin创建的,而并非我们创建的,打开这个html文件如下
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<script type="text/javascript" src="f213fbbcef9ce12fdbb3.bundle.js"></script><script type="text/javascript" src="8a5558af06376743f39d.bundle.js"></script></body>
</html>
接下来我们就可以在这个index.html里愉快的开发了。
等等!万一我在dist目录下已经有一个正在开发的index.html怎么办,用html-webpack-plugin只会让我的index.html被替换掉,这是我们不希望的结果。如果有版本管理工具还好,还能够很快的恢复,如果没有版本管理岗工具也没有备份这个就很悲剧了。
然而事实上html-webpack-plugin允许接收一些参数来适应更多的应用场景:
title: 用来生成页面的 title 元素
filename: 输出的 HTML 文件名,默认是 index.html, 也可以直接配置带有子目录。
template: 模板文件路径,支持加载器,比如 html!./index.html
inject: true | 'head' | 'body' | false ,注入所有的资源到特定的 template 或者 templateContent 中,如果设置为 true 或者 body,所有的 javascript 资源将被放置到 body 元素的底部,'head' 将放置到 head 元素中。
favicon: 添加特定的 favicon 路径到输出的 HTML 文件中。
minify: {} | false , 传递 html-minifier 选项给 minify 输出
hash: true | false, 如果为 true, 将添加一个唯一的 webpack 编译 hash 到所有包含的脚本和 CSS 文件,对于解除 cache 很有用。
cache: true | false,如果为 true, 这是默认值,仅仅在文件修改之后才会发布文件。
showErrors: true | false, 如果为 true, 这是默认值,错误信息会写入到 HTML 页面中
chunks: 允许只添加某些块 (比如,仅仅 unit test 块)
chunksSortMode: 允许控制块在添加到页面之前的排序方式,支持的值:'none' | 'default' | {function}-default:'auto'
excludeChunks: 允许跳过某些块,(比如,跳过单元测试的块)
这里我们使用template设置模板文件。
比如我现在有个正在开发的index.html文件,内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<h1>hello world</h1>
</body>
</html>
然后我们在html-webpack-plugin中添加template参数
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry:{
hello:"./src/scripts/hello.js",
main:"./src/scripts/main.js"
},
output:{
filename:'[chunkhash].bundle.js',
path:path.resolve(__dirname,'dist')
},
plugins:[
new HtmlWebpackPlugin({
template:"./dist/index.html"
})
]
}
这时,我们再次webpack一下,打开index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
</head>
<body>
<h1>hello world</h1>
<script type="text/javascript" src="f213fbbcef9ce12fdbb3.bundle.js"></script><script type="text/javascript" src="8a5558af06376743f39d.bundle.js"></script></body>
</html>
可以看到很顺利的进行引入。
4. 结尾
webpack的插件让webpack极具活力,拥有很强的扩展性