🗂

webpackでHTMLをいい感じにする。

2022/06/02に公開

お久しぶりです。
最近、Alpine.jsが気になっている、スピッカートの金山(@spicato_kana)です。

今回は、共通パーツをテンプレート化したり、階層別に分けたり、webpack で HTML をいい感じにしていきます!

そういえば、webpack って頭文字小文字だったんですね。
この記事を書くときに知りました。

webpack とプラグインの html-webpack-plugin、globule を使っています。

結論

ディレクトリ構成(例)
〜
└── src
  ├── 〜
  └── html
      ├── page
      │   └── index.html
      │   └── about
      │       └── about.html
      │       └── about01
      │           └── about01.html
      └── template
          └── _header.html
package
npm install -D html-webpack-plugin
npm install -D globule
webpack.config.js
// モジュールの読み込み
const path = require("path");
const fs = require("fs");
const globule = require("globule");
const HtmlWebpackPlugin = require("html-webpack-plugin");

const app = {
  entry: {
  index: "./src/assets/js/index.js",
  about: "./src/assets/js/about.js",
  about01: "./src/assets/js/about01.js",
  },

  ~~~
}

const htmlDir = globule.find({
  src: "*.html",
  srcBase: "src/html/page",
  matchBase: true,
  prefixBase: true,
});
htmlDir.forEach((htmlFile) => {
  const fileName = path.basename(htmlFile);
  const chunksName = fileName.replace(".html", "");
  const subDir = htmlFile.replace("src/html/page/", "").replace(fileName, "");

  if (fileName.charAt(0) === "_") {
    return;
  } else if (fileName === "index.html") {
    app.plugins.push(
      new HtmlWebpackPlugin({
        filename: fileName,
        template: htmlFile,
        chunks: [chunksName],
        hash: true,
        // 共通ヘッダーを読み込む
        header: fs.readFileSync("./src/html/template/_header.html", "utf8"),
        // ie11に対応する場合
        // scriptLoading: "blocking",
      })
    );
  } else {
    app.plugins.push(
      new HtmlWebpackPlugin({
        filename: `${subDir}/index.html`,
        template: htmlFile,
        chunks: [chunksName],
        hash: true,
        // 共通ヘッダーを読み込む
        header: fs.readFileSync("./src/html/template/_header.html", "utf8"),
        // ie11に対応する場合
        // scriptLoading: "blocking",
      })
    );
  }
});

解説

今回は HTML 関連のみの解説なので、それ以外の webpack の設定は省略しています。

const htmlDir = globule.find({
  src: "*.html",
  srcBase: "src/html/page",
  matchBase: true,
  prefixBase: true,
});

ここで、src/html/page から *.html を探しています。
ちなみに、オプションのmatchBase: trueで、サブディレクトリからも探し、
prefixBase: trueで、src/html/page も含むようにしています。

今回の場合だと、src/html/page/index.htmlsrc/html/page/about/about.html を取得してきます。


const fileName = path.basename(htmlFile);
const chunksName = fileName.replace(".html", "");
const template = htmlFile.replace("src/html/page/", "").replace(fileName, "");

// 表示結果
{ fileName: 'about01.html', chunksName: 'about01', subDir: 'about/about01/'}
{ fileName: 'index.html', chunksName: 'index', subDir: '' }
{ fileName: 'about.html', chunksName: 'about', subDir: 'about/' }

それぞれ上記のような結果が返ってきます。


if (fileName.charAt(0) === "_") {
  return;
} else if (fileName === "index.html") {
  app.plugins.push(
    new HtmlWebpackPlugin({
      filename: fileName,
      template: htmlFile,
      chunks: [chunksName],
      hash: true,
      // 共通ヘッダーを読み込む
      header: fs.readFileSync("./src/html/template/_header.html", "utf8"),
    })
  );
} else {
  app.plugins.push(
    new HtmlWebpackPlugin({
      filename: `${subDir}/index.html`,
      template: htmlFile,
      chunks: [chunksName],
      hash: true,
      // 共通ヘッダーを読み込む
      header: fs.readFileSync("./src/html/template/_header.html", "utf8"),
    })
  );
}

if (fileName.charAt(0) === "_") { return; }は、ファイル名の最初の文字が _ だったら、除外しています。
_base.htmlとか作って、コピペして使い回す時に便利です。

if (fileName === "index.html"){}は、階層別に分けたりするための条件分岐です。

index.htmlはそのまま出力し、それ以外はサブディレクトリを考慮して出力しています。
出力結果は、

dist(例)
─── dist
  ├── 〜
  ├── index.html
  └── about
      ├── index.html
      └── about
          └── index.html
          └── about01
              └── index.html

ちなみに、ここのheader: fs.readFileSync("./src/html/template/_header.html", "utf8"),で共通パーツを読み込んでいます。
readFileSyncは、ファイルの中身を読み込んでいます。

_header.html(例)
<div class="header__inner">ヘッダーだよ</div>
index.html(例)
<body>
  <div id="app">
    <div class="container">
      <header class="header"><%= htmlWebpackPlugin.options.header %></header>

      <main class="main">
        <h1>見出し</h1>
      </main>

      <footer class="footer"></footer>
    </div>
  </div>
</body>

上記のように使用します。
他にも、フッターや、メニューなど共通するものはテンプレート化しておくと便利です!


まとめ

今回は、webpack で HTML を整えていきました。
Web 制作では、HTML をサブディレクトリで分けたいとか、フレームワークを使うほどでもないけどヘッダーとかテンプレートにしたいとかあると思います。
よっぽどのことがない限りはこの設定で HTML は従分賄えるんじゃないでしょうか。。。!

ぜひ試してみてください。


宣伝

現在弊社ではエンジニアを募集しています!

少しでも興味を持っていただければ、下記リンクをご覧ください!
スピッカートについてや、募集要項がより詳しく記載されています。

https://spicato.com/blog/14/2022recruit/


参考サイト

https://yumegori.com/webpack5-setting-method#chapter-15
https://github.com/jantimon/html-webpack-plugin
https://github.com/cowboy/node-globule

spicato Inc.

Discussion