Webpack 项目中 html-webpack-plugin 和 public 目录的关系

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第7天,点击查看活动详情

这几天一直在研究 Webpack 这些打包工具链,在研究项目启动的过程中,初步认识到平时开发时是通过 Webpack-dev-server 调用 Webpack 的打包能力结合静态资源服务器能力开发的

大概是下面这个图片

IMG

但是由于 webpack-dev-server 为了更快,它的打包文件都是放在缓存里的,对于我来说其实可以说是黑盒操作

首先来到我的第一个疑问

public 目录是什么?

通常基于 Webpack 的项目都会有一个 public 文件夹

比如 Vue CLI@4.5.15 创建的项目

├── node_modules
├── public
|  ├── favicon.ico
|  └── index.html
├── src
├── babel.config.js
├── package.json
├── package-lock.json
└── README.md

比如 creat react app@5.0.1

├── node_modules
├── public
|  ├── favicon.ico
|  ├── index.html
|  ├── logo192.png
|  ├── logo512.png
|  ├── manifest.json
|  └── robots.txt
├── src
├── .gitignore
├── babel.config.js
├── package.json
├── package-lock.json
└── README.md

共同点是都有 public/index.htmlpublic/favicon.ico,如果你尝试修改 index.html 还可以在浏览器中看到相应的修改

IMG

首先解密 public 目录是干什么的?

静态资源目录

public 其实是作为 Webpack 的静态资源目录,通过 http 请求的方式访问 public 目录下的文件内容,默认 http://localhost:port/ 会返回 publicindex.html

那么可不可以返回其它的文件呢?

当然可以,但是需要改动的地方会比较长,webpack-dev-server 提供的静态资源是依靠一下库的赋能

webpack-dev-server <- express <- serve-static

回到正题Webpack 作为前端工具链重要的一环,有一个很重要的作用就是热更新,并将热更新修改内容反映在我们的浏览器中,这也就是我们需要 public/index.html,为什么需要静态资源服务器

那么来到我的第二个疑问

既然 public/index.html 是热更新的入口,那么为什么没有看到脚手架创建的 publc/index.html 中存在和打包文件相关的部分呢?

我之前写过一篇文章基于 Webpack 从 0 到 1 启动一个 Vue 项目 – 掘金

里面纯手工运行了一个 Vue 项目(以单文件组件(SFC/.vue)的方式)

但里面的 index.html 需要依赖打包文件

<!-- index.html -->
<body>
  <div id="app"></div>
</body>
<script type="text/javascript" src="bundle.js" charset="utf-8"></script>
// webpack.config.js
// ...
module.exports = {
  mode: "development",
  devServer: {
    static: {
      directory: path.join(__dirname, "public"),
    },
    port: 8080,
  },
  // ...
  entry: path.join(__dirname, "./main.js"),
  output: {
    publicPath: "",
    filename: "bundle.js",
  },
  // ...
};

但脚手架的默认 index.html 是没有任何的 js 引入的,以 Vue CLI 为例

<!-- index.html -->
<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>
  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>

但你先别急,脚手架项目运行之后的 index.html 可不是上面这样的,实际如下

<!DOCTYPE html>
<html lang="">
  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <link rel="icon" href="/favicon.ico">
    <title>vue-cli-create-demo</title>
  <link href="/js/app.js" rel="preload" as="script"><link href="/js/chunk-vendors.js" rel="preload" as="script"></head>
  <body>
    <noscript>
      <strong>We're sorry but vue-cli-create-demo doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
    </noscript>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  <script type="text/javascript" src="/js/chunk-vendors.js"></script><script type="text/javascript" src="/js/app.js"></script></body>
</html>

对比上下两个 index.html 的内容,多出了 .js 的引入以及 <%= htmlWebpackPlugin.options.title %> 被覆盖

所以脚手架它们到底是怎么做到呢?

IMG

html-webpack-plugin

回到上面的两个 index.html 里面有个 htmlWebpackPlugin 很关键,其实答案就在它身上,文档上关于 html-webpack-plugin 的介绍是这样的

HtmlWebpackPlugin 简化了 HTML 文件的创建,以便为你的 webpack 包提供服务。这对于那些文件名中包含哈希值,并且哈希值会随着每次编译而改变的 webpack 包特别有用。你可以让该插件为你生成一个 HTML 文件,使用 lodash 模板提供模板,或者使用你自己的 loader

那么它和 public/index.html 有什么联系呢?我从它的配置上得到了答案

IMG

其实这个 html-webpack-plugin 干的活就是会在 webpack 打包时生成 index.html 并且放置在 webpack-dev-server 设置的静态资源服务文件夹,因此会替换掉默认的 index.html
通常用户设置的 index.html 会作为 webpack-dev-server 调用 Webpack 打包能力时通过 html-webpack-plugin 的模版,基于此生成一个 index.html,因此像 Vue CLI 此类脚手架会在配置中设置插入打包文件的脚本引入,因此无需用户手动配置

大致流程如下图

IMG

要实现基本脚手架的这个能力可以参照下面的实现

// webpack.config.js
// ...
module.exports = {
  // ...
  plugins: [
    // ...
    new HtmlWebpackPlugin({
      // 设置模板
      template: "public/index.html",
      // 设置 html-webpack-plugin 以 defer 模式自动引入打包文件
      scriptLoading: "defer",
    }),
  ],
};

总结

为什么需要 html-webpack-plugin?比如生产环境的打包 js 文件其实是动态文件名,像这种情况不能够写死,因此就需要 html-webpack-plugin 去帮忙配置

参考资料

  1. HtmlWebpackPlugin – Wepback 文档
  2. html-webpack-plugin – github
© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容