webpack行程
1. 第一天:webpack安装与基本使用
2. 第二天:如何处理各种格式的文件
3. 第三天:如何生成自定义Html文件
4. 第四天:如何实现开发模式下的配置
5. 第五天:如何实现第三方插件的引入
6. 第六天:如何提取公用代码同时减少冗余代码
7. 第七天:研究vue-cli脚手架源码
第三天:如何生成自定义Html文件
webpack学习之旅的第三天,我们稍作调整。前面两天的行程有点太累,今天我们针对前面的构建流程进行一些优化,比如:我们每次创建新项目都需要手动创建一个Html文件同时手动插入script标签并预先引入构建后的JS资源文件。如果我们每次打包创建的bundle名称加入了hash值 ( 为了缓存的目的 ) ,那么我们就需要手动去修改html文件中引入的JS资源文件名,显然这种方式是不科学的。再比如,我们目前遇到的仅仅是单页面应用,如果我们打包的是多页面应用,比如我们有两个页面,分别对应了两个JS文件入口,那么我们的entry就是两个chunk,构建后的bundle也是两个。难不成我们要手动创建两个html文件,然后分别引入不同的JSbundle文件吗?所以这就引出了我们今天要去参观的html-webpack-plugin插件。
1. clean-webpack-plugin
在了解html-webpack-plugin之前,我们先来了解 clean-webpack-plugin 插件。这个插件的作用很简单,就是用来清除我们文件目录的工具。如果使用了hash值,那么每次打包构建后的文件都会不一样,手动清除目录是很繁琐的。我们需要工具来提高效率。使用方法也很简单,第一步肯定是要安装这个插件
npm install clean-webpack-plugin --save-dev
接着我们只需要在webpack.config.js 配置文件的 plugins 选项中添加即可。 凡是插件,需要添加到 plugins 选项中。这个选项接收一个数组,数组中堆满了所有构建需要用到的各种插件。
const cleanWebpackPlugin = require("clean-webpack-plugin") module.exports = { plugins: [ new cleanWebpackPlugin([ "dist" ], { verbose: false }) ] }
使用方法非常简单,第一个参数paths是文件夹数组,可以指定要删除的目录或者目录下的特定后缀名的文件等等。第二个参数 options 可以指定 root 根目录 ( 默认是 __dirname ) ,也可以指定 verbose ( true表示在终端输出中会显示删除的日志信息 false表示不显示) ,还可以指定 exclude 数组以表示哪些文件需要排除在外。
2. html-webpack-plugin
现在正式开始讲解html-webpack-plugin插件的使用。这个插件的目的是帮助我们生成一个包含构建后资源的html文件。那么我们是否需要一个html模板呢? 俗话说是先有鸡还是先有蛋呢?我们想创建一个html文件,那肯定是预先需要一个html壳子的呀,然后插件在这个基础之上帮我们把各种标签如 script meta style title等等信息插入到它应该存放的位置。所以我们就先创建一个最简单的html文件,如下
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title><%= htmlWebpackPlugin.options.title %></title>
</head>
<body>
</body>
</html>
只有DOCTYPE head title 和 body,除此之外什么都没有写。接着我们来看插件如何帮助我们生成最终的html文件。
第一步是安装这个插件
npm install html-webpack-plugin --save-dev
接着 webpack.config.js 配置文件中的 plugins配置如下
const htmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { entry: { app: path.resolve(__dirname, "./src/index.js") }, output: { path: path.resolve(__dirname, "./dist"), filename: "[name]-[hash:4].js" }, plugins: [ new htmlWebpackPlugin({ template: path.resolve(__dirname, "./index.html"), filename: "app.html", title: "使用html-webpack-plugin插件自动插入打包资源文件来生成html文件", chunks: [ "app" ], inject: "body", favicon: path.resolve(__dirname, "./favicon.ico"), meta: { "utf-8": { "charset": "utf-8" }, "viewport": "width=device-width, initial-scale=1.0", "X-UA-Compatible": { "http-equiv": "X-UA-Compatible", "content": "ie=edge" } }, minify: false }) ] }
接着简短介绍下options中各个参数的含义
template: 模板文件,就是前面提到的那个壳子
filename: 自动生成的html文件名称,可以指定二级目录
title: html文件中的title标签内容,也就是页面标题,注意,一定要预先在模板中使用 <%= htmlWebpackPlugin.options.title %> 进行占位。
chunks: 指定哪些chunk打包后的文件才能够放入到html文件中,这里就涉及到我们前面提到的应用场景了,两个页面分别对应不同的chunk
excludeChunks: 指定哪些chunk被排除在外,不会添加到html文件中。
inject: 指定页面需要的这些资源文件插入到html文件的哪个部分,比如 true | body 表示插入到 body标签中, head 表示插入到 head标签中, false则表示不要插入,由我开发者来自行插入。
favicon: 指定页面的 favicon小图标文件
meta: 指定页面的meta 信息,参考上图即可。针对meta标签格式不是 <meta name="xxx", content="yyy" /> 这种情形的请使用上面的方式进行定义,如 charset=utf-8等等
minify: 指定页面输出格式,默认可以不设置。当你有html文件输出格式要求时再来设置。具体各个选项可以查看官方文档,给出传送门 html-minifier的git仓库,一般可以设置为如下简单要求:
{
minify: {
collapseWhitespace: true,
removeComments: true,
removeRedundantAttributes: true,
removeScriptTypeAttributes: true,
removeStyleLinkTypeAttributes: true,
useShortDoctype: true
}
}
collapseWhitespace 是否把所有的空白符全部去掉
removeComments 是否把注释去掉
removeRedundantAttributes 是否将多余的标签特性移除,比如input标签默认type=text,比如 a标签的name和id重复了会移除name
removeScriptTypeAttributes 是否移除 script标签type特性 type="text/javascript"
removeStyleLinkTypeAttributes 是否移除 link style标签的 type特性
useShortDoctype 是否使用更短的DOCTYPE文档声明
OK,有了上面这些准备工作,我们在 ./src/index.js 文件中引入CSS样式文件,然后简单添加一句 console.log("hello") 打印语句。通过webpack命令最后构建的日志信息如下
具体源代码可以查看项目git仓库中的 /day3/demo1
3. 高级内容
一般来说,对于普通开发者,了解这个插件到这种程度就可以了,可就是有这么一群人,他们会想,为什么我们要预先提供一个html壳子呢?难道就没有办法让插件给我们自动生成一个html壳子呢?于是我在官网上找到了这么一个插件 html-webpack-template 。天呐,还真有这种工具呢,那我们就一起来快快使用吧。
第一步依然是需要先安装这个插件
npm install html-webpack-plugin html-webpack-template --save-dev
接着就是调整我们的 html-webpack-plugin 的 options 选项了,首先 template 参数指定为 这个插件实例 require("html-webpack-template")
接着这个插件给我们丰富了 html-webpack-plugin的 options选项,同时也规定了 inject 参数必须为 false 其他常用参数选项可以参考如下代码
const HtmlWebpackPlugin = require("html-webpack-plugin"); module.exports = { plugins: [ new HtmlWebpackPlugin({ inject: false, template: require("html-webpack-template"), title: "使用html-webpack-template生成html文件", appMountId: "myApp", appMountHtmlSnippet: "<p>我是通过appMountHtmlSnippet插入的内容</p>", appMountIds: ["gallery", "panel"], bodyHtmlSnippet: "<p>我是插入到body中的p标签</p>", lang: "zh-CN", window: { "userName": "joy" }, chunks: [ "app" ], meta: [ { name: "viewport", content: "width=device-width, initial-scale=1.0", } ] }) ] }
常用的扩展参数选项讲解
a. appMountId 表示 会创建这样一个ID的DIV标签,同时将 appMountHtmlSnippet 内容插入其中
b. appMountHtmlSnippet 表示 插入到 appMountId 的 html片段内容
c. appMountIds 表示会创建一系列ID的DIV标签
d. bodyHtmlSnippet 表示会插入到body内部起始位置的 html片段内容
e. lang 指定 html文件的 语言
f. window 定义全局作用域下的变量
g. meta 接收一个数组,默认已经为我们添加了 charset 与 <meta http-equiv="x-ua-compatible" content="ie=edge"> 这两个meta标签。
其余meta可以通过 {name, content}方式插入
h. headHtmlSnippet 表示会插入到head 标签内部起始位置的 html片段内容
通过webpack构建后生成的html文件如下
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta content="ie=edge" http-equiv="x-ua-compatible"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>使用html-webpack-template生成html文件</title> <link href="app.css" rel="stylesheet" /> </head> <body> <a href="./a.js">我是插入到body中的a链接</a> <div id="myApp"> <p>我是通过appMountHtmlSnippet插入的内容</p> </div> <div id="gallery"></div> <div id="panel"></div> <script type="text/javascript"> window['userName'] = "joy"; </script> <script src="app-3a6b.js" type="text/javascript"></script> </body> </html>
具体源代码可以查看项目git仓库 /day3/demo2
4. 后记
第三天,我们只是简化了最终html文件的生成过程,以及通过插件使得我们可以自由定义最终生成的html文件的各种样式。而针对webpack内部对JS资源构建的细节并没有深入介绍。今天介绍的这两个生成html文件的插件可以配合我们第二天介绍的 html-loader 一起使用,比如我们在html模板中引入了一张图片,最后打包时我们会将这张图片的资源引用地址进行修改等等。这部分的源代码及讲解放到后面再说,还是先Mark,因为不是很常用。另外,大家有没有发现我们每次调整源码之后都需要手动再次执行webpack命令,而且最终的 Index.html文件时都是通过file协议打开的,有没有办法实现Http协议打开呢,毕竟我们最终的产品都是通过http协议访问的。大家别急,这就是我们第四天即将前往的景点——webpack-dev-server插件。