I rencently notice that webpack is based itself on chunks. Therefore, writing plugin logic based on chunk may be adaptable to the core and sprite of webpack.
In webpack, the basic element is chunks
. The values in entry
of webpack config are chunks
. For instance, index
and detail
in the following entry config are chunks' names; In most cases, chunk is a js file. But if you require stylesheet or other files in js, a js chunk will include not only js file but also the files you require.
entry: {
index: xxx
detail: xxx
}
What about assets? Assets are files will be exported by webpack. They can be any file types like stylesheets, images, html and so on.
src/index.html --> dist/index.html
<!DOCTYPE html>
<html lang="en" id="html">
<head>
<meta charset="UTF-8">
<meta http-equiv="x-dns-prefetch-control" content="on" />
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, minimal-ui" />
<title>html-res-webpack-example</title>
</head>
<body>
<div class="preview-wrapper"></div>
</body>
</html>
src/page/preview/main.js --> dist/js/preview/preview.js
require('./index.scss');
var init = function() {
// some code here
};
src/page/preview/index.scss --> dist/css/preview/preview.css
html, body {
margin: 0;
}
// some code here
webpack.config.js
var config = {
hash: "-[hash:6]",
chunkhash: "-[chunkhash:6]",
contenthash: "-[contenthash:6]"
};
var webpackconfig = {
entry: {
'preivew/preview': [path.join(config.path.src, "/page/preview/main.js")],
},
/**
* webpack options below
*/
.
.
.
output: {
publicPath: (config.env === 'prod') ? config.cdn : config.defaultPath,
path: path.join(config.path.dist),
filename: "js/[name]" + config.chunkhash + ".js"
},
.
.
.
plugins: [
// some other plugins
new ExtractTextPlugin("./css/[name]" + config.contenthash + ".css"),
new HtmlResWebpackPlugin({
filename: "index.html",
template: "src/index.html",
chunks:{
'index': {
attr: { // attributes for index chunk
js: "async=\"true\"",
css: "offline",
},
inline: { // inline or not for index chunk
js: true,
css: true
}
}
},
});
]
};
package.json
"scripts": {
"dev": "webpack --progress --colors --watch",
"publish-mac": "export NODE_ENV=prod&&webpack -p --progress --colors",
"publish-windows": "SET NODE_ENV=prod&&webpack -p --progress --colors"
},
One thing need to be noticed is hash
and chunkhash
. The difference between hash
and chunkhash
is that hash
is the same for all resources and chunkhash
is different for specific resource. Usually you are recommended to use chunkhash
instead (Exception for style files required in an entry js file. They share the same chunkhash
if you use extract-text-webpack-plugin. Please use contenthash
instead in order to ensure hash for stylesheet changes only when content changes).
Another thing worth being noticed is the order of chunks
. The order of resources that will be injected is based on the order of chunks
in html-res-webpack-plugin
.
Sometimes, you may need to use external common resources. If this is the case, please write options like following:
chunks:{
'qreport': {
external: true, // tell the plugin not to append publicPath
res: "//s.url.cn/pub/js/qreport.js?_bid=2231", // resource path
attr: {
js: "async=\"true\"",
}
}
}
Sometimes there are more than one html pages in your projects. In this situation, please use similar iteration code to add plugins for different html pages
var config = {
hash: "-[hash:6]",
chunkhash: "-[chunkhash:6]",
contenthash: "-[contenthash:6]"
};
var route = ['index', 'detail'];
var webapckConfig = {
entry: {
"js/index": "xxx/index",
"js/detail": "xxx/detail",
}
};
let pageMapping = {
'detail': {
'js/detail': {
attr:{
js: "",
css: "",
}
},
},
'index': {
'js/index': {
attr:{
js: "",
css: "",
}
},
}
};
webpackConfig.addPlugins = function(plugin, opt) {
devConfig.plugins.push(new plugin(opt));
};
route.html.forEach(function(page) {
webapckConfig.addPlugins(HtmlResWebpackPlugin, {
filename: page + ".html",
template: "src/" + page + ".html",
favicon: "src/favicon.ico",
chunks: pageMapping[page],
});
});
webpack.config.js
new HtmlResWebpackPlugin({
filename: "index.html",
template: "xxx/index.html",
favicon: "xxx/favicon.ico",
chunks:[
'js/index',
],
}),
new HtmlResWebpackPlugin({
filename: "index.html",
template: "src/index.html",
templateContent: function(tpl) {
// some modification of tpl
// you can use this.options [user input plugin options]
// and this.webpackOptions [webpack config] here, you can open index.js of the plugin
// to check what options are offered
return tpl;
}
}),
copy-webpack-plugin-hash is a plugin that helps copy files directly without webpack parsing. I add namePattern
option feature for it so that files generated by this plugin can also have hash (Once the main repo accepts my pull request, I will delete this temporary repo).
If you use copy-webpack-plugin for example, you can use html-res-webpack-plugin
easily. For example, if you copy /xxx/libs
folder to libs/
. If the folder contains react
and react-dom
, you can add chunks libs/react/
and libs/react-dom
in html-res-webpack-plugin
.
plugins: [
new CopyWebpackPlugin([
{
from: '/xxx/libs/',
to: 'libs/'
}
], {
namePattern: "[name]-[contenthash:6].js"
}),
new HtmlResWebpackPlugin({
filename: "index.html",
template: config.path.src + "/resource-copy-plugin-1/index.html",
chunks:[
'libs/react',
'libs/react-dom',
'js/index',
],
}),
]
filename
:- is required
- generated filename
template
:- is required
- template source
chunks
:- is required
- [Array|Object]
- injected chunks
- examples:
[Array]
entry: {
'index': xxx,
'detail': xxx,
'libs/react': xxx,
}
plugins: [
new HtmlResWebpackPlugin({
/** other config */
chunks: [
'index',
'detail',
'libs/react'
]
})
]
[Object]
plugins: [
new HtmlResWebpackPlugin({
/** other config */
chunks: {
'qreport': {
external: true // tell the plugin not to append publicPath
res: "xxx" // resource path
},
'index': {
attr: { // attributes for index chunk
js: "async=\"true\"",
css: "offline",
},
},
'detail': {
inline: { // inline or not for detail chunk
js: true,
css: true
}
},
'libs/react': nulls
}
})
]
htmlMinify
:- is optional
- please checkout html-minifier to see detail options. If set false | null, html files won't be compressed.
favicon
:- is optional
- favicon path, for example, "src/favicon.ico"
templateContent
:- is optional
- a point for developer to modify html content before output.
this.options
andthis.webpackOptions
can be used in such a function.
I add automatic testing samples fo for the plugin to ensure stablity and relablility. I starting using this plugin in internal development.
If you still don't understand README, you can checkout examples in specWepback where testing samples are located.
- v0.0.1 resouce inline and md5
- v0.0.2 customized name and hash
- v0.0.3 support favicon file
- v0.0.4 fix adding prefix and adding md5 bugs
- v0.0.5 offer templateContent to modify html content before output
- v0.0.7 compatible with webpack2.0 README
- v1.0.0 rewrite the whole thing and add testing function
- v1.0.1 allow external resource to be injected