html-webpack-plugin详解
引⾔
我们来看看主要作⽤:
为html⽂件中引⼊的外部资源如script、link动态添加每次compile后的hash,防⽌引⽤缓存的外部⽂件问题
可以⽣成创建html⼊⼝⽂件,⽐如单页⾯可以⽣成⼀个html⽂件⼊⼝,配置N个html-webpack-plugin可以⽣成N个页⾯⼊⼝
html-webpack-plugin
插件的基本作⽤就是⽣成html⽂件。原理很简单:
将 webpack中`entry`配置的相关⼊⼝chunk  和  `extract-text-webpack-plugin`抽取的css样式插⼊到该插件提供的`template`或者`templateContent`配置项指定的内容基础上⽣成⼀个html⽂件,具体插⼊⽅式是将样式`link`插⼊到`head`元素中,实例化该插件时可以不配置任何参数,例如下⾯这样:
var HtmlWebpackPlugin = require('html-webpack-plugin')
webpackconfig = {
plugins: [
new HtmlWebpackPlugin()
]
}
不配置任何选项的html-webpack-plugin插件,他会默认将webpack中的entry配置所有⼊⼝thunk和extract-text-webpack-plugin抽取的css样式都插⼊到⽂件指定的位置。例如上⾯⽣成的
html⽂件内容如下:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Webpack App</title>
<link href="index-af150e90583a89775c77.css" rel="stylesheet"></head>
<body>
<script type="text/javascript" src="common-26a14e7d42a7c7bbc4c2.js"></script>
<script type="text/javascript" src="index-af150e90583a89775c77.js"></script></body>
</html>
当然可以使⽤具体的配置项来定制化⼀些特殊的需求,那么插件有哪些配置项呢?
html-webpack-plugin配置项
插件提供的配置项⽐较多,通过源码可以看出具体的配置项如下:
1this.options = _.extend({
2  template: path.join(__dirname, 'default_index.ejs'),
3  templateParameters: templateParametersGenerator,
4  filename: 'index.html',
5  hash: false,
6  inject: true,
7  compile: true,
8  favicon: false,
9  minify: false,
10  cache: true,
11  showErrors: true,
12  chunks: 'all',
13  excludeChunks: [],
14  chunksSortMode: 'auto',
15  meta: {},
16  title: 'Webpack App',
17  xhtml: false
18 }, options);
title: ⽣成的html⽂档的标题。配置该项,它并不会替换指定模板⽂件中的title元素的内容,除⾮html模板⽂件中使⽤了模板引擎语法来获取该配置项值,如下ejs模板语法形式:
<title>{%= o.htmlWebpackPlugin.options.title %}</title>
filename:输出⽂件的⽂件名称,默认为index.html,不配置就是该⽂件名;此外,还可以为输出⽂件指定⽬录位置(例如'html/index.html')
关于filename补充两点:
1、filename配置的html⽂件⽬录是相对于webpackConfig.output.path路径⽽⾔的,不是相对于当前项⽬
⽬录结构的。
2、指定⽣成的html⽂件内容中的link和script路径是相对于⽣成⽬录下的,写路径的时候请写⽣成⽬录下的相对路径。
template: 本地模板⽂件的位置,⽀持加载器(如handlebars、ejs、undersore、html等),如⽐如handlebars!src/index.hbs;
关于template补充⼏点:
1、template配置项在html⽂件使⽤file-loader时,其所指定的位置不到,导致⽣成的html⽂件内容不是期望的内容。
2、为template指定的模板⽂件没有指定任何loader的话,默认使⽤ejs-loader。如template: './index.html',若没有为.html指定任何loader就使⽤ejs-loader
templateContent: string|function,可以指定模板的内容,不能与template共存。配置值为function时,可以直接返回html字符串,也可以异步调⽤返回html字符串。
inject:向template或者templateContent中注⼊所有静态资源,不同的配置值注⼊的位置不经相同。
1、true或者body:所有JavaScript资源插⼊到body元素的底部
2、head: 所有JavaScript资源插⼊到head元素中
3、false:所有静态资源css和JavaScript都不会注⼊到模板⽂件中
favicon: 添加特定favicon路径到输出的html⽂档中,这个同title配置项,需要在模板中动态获取其路径值
hash:true|false,是否为所有注⼊的静态资源添加webpack每次编译产⽣的唯⼀hash值,添加hash形式如下所⽰:
html <script type="text/javascript" src="common.js?a3e1396b501cdd9041be"></script>
chunks:允许插⼊到模板中的⼀些chunk,不配置此项默认会将entry中所有的thunk注⼊到模板中。在配置多个页⾯时,每个页⾯注⼊的thunk应该是不相同的,需要通过该配置为
不同页⾯注⼊不同的thunk;
excludeChunks: 这个与chunks配置项正好相反,⽤来配置不允许注⼊的thunk。
chunksSortMode: none | auto| function,默认auto;允许指定的thunk在插⼊到html⽂档前进⾏排序。
>function值可以指定具体排序规则;auto基于thunk的id进⾏排序; none就是不排序
xhtml: true|fasle, 默认false;是否渲染link为⾃闭合的标签,true则为⾃闭合标签
cache: true|fasle, 默认true;如果为true表⽰在对应的thunk⽂件修改后就会emit⽂件
showErrors: true|false,默认true;是否将错误信息输出到html页⾯中。这个很有⽤,在⽣成html⽂件的过程中有错误信息,输出到页⾯就能看到错误相关信息便于调试。
minify: {....}|false;传递 html-minifier 选项给 minify 输出,false就是不使⽤html压缩,minify具体配置参数请点击
下⾯的是⼀个⽤于配置这些属性的⼀个例⼦:
new HtmlWebpackPlugin({
title:'rd平台',
template: 'src/index.html', // 源模板⽂件
filename: './index.html', // 输出⽂件【注意:这⾥的根路径是ports.output.path】
showErrors: true,
inject: 'body',
chunks: ["common",'index']
})
配置多个html页⾯
html-webpack-plugin的⼀个实例⽣成⼀个html⽂件,如果单页应⽤中需要多个页⾯⼊⼝,或者多页应⽤时配置多个html时,那么就需要实例化该插件多次;
即有⼏个页⾯就需要在webpack的plugins数组中配置⼏个该插件实例:
……
plugins: [
new HtmlWebpackPlugin({
template: 'src/html/index.html',
excludeChunks: ['list', 'detail']
}),
new HtmlWebpackPlugin({
filename: 'list.html',
template: 'src/html/list.html',
thunks: ['common', 'list']
}),
new HtmlWebpackPlugin({
filename: 'detail.html',
template: 'src/html/detail.html',
thunks: ['common', 'detail']
})
]
...
如上例应⽤中配置了三个⼊⼝页⾯:index.html、list.html、detail.html;并且每个页⾯注⼊的thunk不尽相同;类似如果多页⾯应⽤,就需要为每个页⾯配置⼀个;
配置⾃定义的模板
不带参数的html-webpack-plugin默认⽣成的html⽂件只是将thunk和css样式插⼊到⽂档中,可能不能满⾜我们的需求;
另外,如上⾯所述,三个页⾯指定了三个不同html模板⽂件;在项⽬中,可能所有页⾯的模板⽂件可以共⽤⼀个,因为html-webpack-plugin插件⽀持不同的模板loader,所以结合模板引擎来共⽤⼀个模板⽂件有了可能。
css常用模板所以,配置⾃定义模板就派上⽤场了。具体的做法,借助于模板引擎来实现,例如插件没有配置loader时默认⽀持的ejs模板引擎,下⾯就以ejs模板引擎为例来说明;
例如项⽬中有2个⼊⼝html页⾯,它们可以共⽤⼀个模板⽂件,利⽤ejs模板的语法来动态插⼊各⾃页⾯的thunk和css样式,代码可以这样:
<!DOCTYPE html>
<html >
<head>
<meta charset="utf-8">
<title><%= htmlWebpackPlugin.options.title %></title>
<% for (var css in htmlWebpackPlugin.files.css) { %>
<link href="<%=htmlWebpackPlugin.files.css[css] %>" rel="stylesheet">
<% } %>
</head>
<body>
<div id="app"></div>
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
<script type="text/javascript" src="<%=htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
<% } %>
</body>
</html>
你可能会对代码中的上下⽂htmlWebpackPlugin数据感到迷惑,这是啥东东?其实这是html-webpack-plugin插件在⽣成html⽂件过程中产⽣的数据,这些数据对html模板⽂件是可⽤的,并且我们看<%%>是不是给java⾥⾯的jsp差不多了。
⾃定义模板上下⽂数据
html-webpack-plugin在⽣成html⽂件的过程中,插件会根据配置⽣成⼀个对当前模板可⽤的特定数据,模板语法可以根据这些数据来动态⽣成html⽂件的内容。
那么,插件⽣成的特殊数据格式是什么,⽣成的哪些数据呢?从源码或者其官⽹都给出了答案。从源码中可以看出模板引擎具体可以访问的数据如下:
var templateParams = {
compilation: compilation,
webpack: Stats().toJson(),
webpackConfig: compilation.options,
htmlWebpackPlugin:
files: assets,
options: self.options
}
};
从中可以看出,有四个主要的对像数据。其中compilation为所有webpack插件提供的都可以访问的⼀个编译对象,此处就不太做介绍,具体可以⾃⼰查资料。下⾯就对剩下的三个对象数据进⾏说明。
webpack
webpack的stats对象;注意⼀点:
这个可以访问的stats对象是htm⽂件⽣成时所对应的stats对象,⽽不是webpack运⾏完成后所对应的整个stats对象。
webpackConfig
webpack的配置项;通过这个属性可以获取webpack的相关配置项,如通过webpackConfig.output.publicPath来获取publicPath配置。当然还可以获取其他配置内容。htmlWebpackPlugin
html-webpack-plugin插件对应的数据。它包括两部分:
htmlWebpackPlugin.files: 此次html-webpack-plugin插件配置的chunk和抽取的css样式该files值其实是webpack的stats对象的assetsByChunkName属性代表的值,该值是插件配置的chunk块对应的按照web
packConfig.output.filename映射的值。例如对应上⾯配置插件各个属性配置项例⼦中⽣成的数据格式如下:
"htmlWebpackPlugin": {
"files": {
"css": [ "inex.css" ],
"js": [ "common.js", "index.js"],
"chunks": {
"common": {
"entry": "common.js",
"css": [ "index.css" ]
},
"index": {
"entry": "index.js",
"css": ["index.css"]
}
}
}
}
这样,就可以是⽤如下模板引擎来动态输出script脚本
<% for (var chunk in htmlWebpackPlugin.files.chunks) { %>
<script type="text/javascript" src="<%=htmlWebpackPlugin.files.chunks[chunk].entry %>"></script>
<% } %>
htmlWebpackPlugin.options: 传递给插件的配置项,具体的配置项如上⾯插件配置项⼩节所描述的。
插件事件
不知道你发现没有,html-webpack-plugin插件在插⼊静态资源时存在⼀些问题:
在插⼊js资源只能插⼊head或者body元素中,不能⼀些插⼊head中,另⼀些插⼊body中
不⽀持在html中⽂件内联*,例如在⽂件的某个地⽅⽤<script src="xxx.js?__inline"></script>来内联外部脚本
为此,有⼈专门给插件作者提问了;对此插件作者提供了插件事件,允许其他插件来改变html⽂件内容。具体的事件如下:
Async(异步事件):
* html-webpack-plugin-before-html-generation
* html-webpack-plugin-before-html-processing
* html-webpack-plugin-alter-asset-tags
* html-webpack-plugin-after-html-processing
* html-webpack-plugin-after-emit
Sync(同步事件):
* html-webpack-plugin-alter-chunks
这些事件是提供给其他插件使⽤的,⽤于改变html的内容。因此,要⽤这些事件需要提供⼀个webpack插件。例如下⾯定义的MyPlugin插件。function MyPlugin(options) {
// Configure your plugin
}
MyPlugin.prototype.apply = function(compiler) {
// ...
compiler.plugin('compilation', function(compilation) {
console.log('The compiler is starting a ');
compilation.plugin('html-webpack-plugin-before-html-processing', function(htmlPluginData, callback) {
htmlPluginData.html += 'The magic footer';
callback(null, htmlPluginData);
});
});
};
然后,在fig.js⽂件中配置Myplugin信息:
plugins: [
new MyPlugin({options: ''})
]