—— 拥抱html-webpack-plugin的甜蜜
目录一览
webpack3.0学习笔记(一)
webpack3.0学习笔记(二)
webpack3.0学习笔记(三)
前情概要
通过笔记(二)中一整天的试错发现了构建多页面时使用
html-loader生成html文件的诸多弊病,重新回归html-webpack-plugin。
html-webpack-plugin 构建页面
笔记(一)中对
html-webpack-plugin的理解略显片面,用法得当即可定制出一张满足需求的页面。
使用 html-webpack-plugin 插件对模板进行填充完成 html 页面的生成,其中的关键点是支持 ejs 的模板语法。
-
html模板内容:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>this is webpack loader test</title>
<link rel="stylesheet" href="<%= require('./style/index.css') %>">
<link rel="stylesheet" href="<%= require('./style/one.css') %>">
</head>
<body>
<img src="<%= require('./image/shana.jpg')%>" alt="shana" />
<div class="yintama"></div>
</body>
</html>
-
webpack.config.js配置:
const path = require('path')
const url = require('url')
const webpack = require('webpack')
const CleanWebpackPlugin = require('clean-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
module.exports = {
entry: {
app: path.resolve(__dirname,'src/script/index.js'),
one: path.resolve(__dirname,'src/script/one.js'),
vendor: [
'lodash'
]
},
output: {
filename: 'js/[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
module:{
rules: [
{
test: /\.js$/,
use: 'babel-loader',
include: path.resolve(__dirname,'src'),
exclude: path.resolve(__dirname,'node_modules')
},
{
test: /\.css$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name]-[hash:6].[ext]',
outputPath: 'css/'
}
},
"extract-loader",
{
loader: "css-loader",
options: {
minimize: true
}
}
]
},
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [
{
loader: 'url-loader',
options: {
name: '[name]-[hash:6].[ext]',
outputPath: 'image/',
limit: 1,
publicPath: url.format({
hostname:'localhost',
protocol:'http:',
port:8080,
pathname:'/dist/'
})
}
},
'image-webpack-loader'
]
}
]
},
plugins: [
new CleanWebpackPlugin(['dist']),
new HtmlWebpackPlugin({
template: path.resolve(__dirname,'src/index.html'),
filename: 'index.html'
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module){
return module.context && module.context.indexOf("node_modules") !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
})
]
};
在 html 模板页面中通过 ejs 语法配合 webpack 静态资源引入方法 require('xxx'),将其引入打包流程中,配合前两篇笔记提及的几个 loader 对资源文件进行优化分离并获取生成后地址,插入到新构建的 html 页面中。
注意:测试ES6的
import 'xxx'的形式无法引入,猜测是使用node进行模块引用,暂时不支持该语法,使用require('xxx')即可。
如需将CSS文件也进行合并操作,在对应的JS文件内进行引入,配合 css-loader style-loader 解析即可。
项目的优化手段
这里只用到了
uglifyjs,CommonsChunkPlugin等方式,DLL打包类库的问题,之后接构建具体项目的时候再进行尝试。
- 压缩
- 图片压缩使用
image-webpack-loader
基本配置:
{
test: /\.(jpe?g|png|gif|svg)$/i,
use: [{
loader: 'url-loader',
options: {
name: '[name]-[hash:6].[ext]',
outputPath: 'image/',
limit: 1,
publicPath: url.format({
hostname: 'localhost',
protocol: 'http:',
port: 8080,
pathname: '/dist/'
})
}
},
{
loader: 'image-webpack-loader',
options: {
gifsicle: {
interlaced: false,
optimizationLevel: 1
},
mozjpeg: {
progressive: true,
quality: 65
}
}
}]
}
测试了下gif图片的压缩非常有限,还是少用这种格式的图片。
- CSS压缩使用
css-loader
{
test: /\.css$/,
use: [{
loader: 'file-loader',
options: {
name: '[name]-[hash:6].[ext]',
outputPath: 'css/'
}
},
"extract-loader", {
loader: "css-loader",
options: {
minimize: true
}
}
]
}
CSS的压缩直接使用loader进行即可。
- JS压缩使用
uglifyjs-webpack-plugin
配置如下:
plugins: [
new UglifyJSPlugin({
sourceMap: true
})
]
若 webpack 开启了 devtool,这里的 sourceMap 选项设为 true。
使用
uglifyjs-webpack-plugin的时候正好说说webpack的tree shaking特性,虽然不是什么新东西,由于支持ES6的import导入语法才开始广泛使用。
- tree shaking
two.js 代码如下:
function name (){
return 'Misery'
};
function age (){
return 30
};
export {name,age}
index.js 内容如下:
import {name} from './two.js';
function test(){
var element = document.createElement('div');
element.innerHTML = `hello ${name()}`;
element.appendChild(btn);
return element;
}
document.body.appendChild(test());
打包合并后如下:

打包后 two.js 模块中 name 和 age 方法都被打包了,但只有 name 方法被导出了模块, age 由于没有被调用而被忽略了。

使用 uglifyjs-webpack-plugin 压缩后 age 方法已经被彻底剔除,这个方式对不是特别常用的公共模块调用能起到精简代码的作用,具体使用方法视项目而定。
注意:如果项目中使用了
babel可能会在webpack打包前就将代码进行转义导致tree shaking不能生效,因为这功能只支持ES6的import方法,此时只需要使用babel-preset-env插件,并修改.babelrc文件。
{
"presets": [
["env", {
"modules": false
}]
]
}
-
提取公共模块
使用webpack自带的webpack.optimize.CommonsChunkPlugin插件进行公共模块提取。
const path = require('path')
const webpack = require('webpack')
module.exports = {
entry: {
...
vendor: [
'lodash'
]
},
output: {
filename: 'js/[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
...
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks: function(module){
return module.context && module.context.indexOf("node_modules") !== -1;
}
}),
new webpack.optimize.CommonsChunkPlugin({
name: "manifest",
minChunks: Infinity
})
]
};
在 entry 增加公共库文件的chunk,也可将 vue react 这些框架统一写入此处,在插件处配置 webpack.optimize.CommonsChunkPlugin 的设置,minChunks 接收的类型是 Number 和 Function。
当为 Number 时表示 chunk 被引用的次数不低于设定值,则会被合入 vendor 模块;若为 Function,则根据函数返回值进行判断,module.context && module.context.indexOf("node_modules") !== -1 该配置的含义为仅将 node_modules 目录下的公共类库、框架整合进 vendor 模块内,而忽略项目中被多次调用的自建模块。
名为
manifest是个“货物清单”的配置,是webpack中一个特有的配置,目的是将webpack中的引导逻辑单独提取到一个文件中,将其他业务模块中的引导模块逻辑剔除,从而减少重复打包的问题,减小单文件容量。
总结
通过对
html-webpack-plugin的学习,更方便的去构建一张HTML页面,同时处理静态资源文件引入问题。对js、css、图片等文件在生产环境进行压缩的方法,接触了webpack中tree shaking的使用方法。使用CommonsChunkPlugin分离公共模块的方法,并了解创建一个manifest货物清单文件的意义(分离webpack的引导逻辑)。
下章会结合一个vue多页面实例进行构建测试,并引入DLLPlugin和DLLReferencePlugin插件,优化打包速度。
尾巴
在
tree shaking这块卡了很久,一直得不到理想效果,通过排查 才发现是babel提前转义了ES6的modules引发的,需要引入babel-preset-env取消转义ES6modules才得以解决!