html-include の使い方

前置き

自作キーボードのコミュニティで使用している質問用フォームを管理していますが、1つのページに全ての質問を載せる形から、問題の内容に応じて質問を分けるようにしました。

この改修により、管理するページ数が1から6に増え、そのうち5つのページでヘッダー等が共通していましたので、共通部分を切り出す方法を探したところ、html-includes - npm というプラグインがちょうど良かったため、早速使うことにしました。

このプラグインの解説をした日本語ページが見当たらなかったので、備忘録を兼ねて簡単な使い方を説明します。

プラグインの概要

あるHTMLファイルに別のHTMLファイルを挿入してくれるプラグインです。各ページに共通する部分を別ファイルに切り出して、それをビルドして一つのファイルに統合することができるようになります。

インストール方法

npm でプラグインをインストールします。

1npm i --save-dev html-includes

それから package.json に次の設定を追加すると、 npm run compile コマンドでビルドできるようになります。

1"scripts": {
2  "compile": "html-includes --src src --dest dist",
3  "compile:watch": "html-includes --src src --dest dist --watch"
4},

html-includes のオプションは次のとおりです。

-src src
統合する html ファイルが保存されているディレクトリを指定する。
-dest dist
統合した html ファイルを保存するディレクトリを指定する。
--watch
ファイルが変更されたら自動的にビルドする。

使い方

大まかな手順は次のとおりです。

  • 共通部分を読み込む側の html ファイルに ${require('読み込みたいファイルのパス')} を記述
  • 上で説明した -src src で指定したディレクトリに共通部分を切り出した html ファイルを保存
  • npm run compile でビルドする
 1# ファイル構成
 2src/
 3└── html
 4    ├── common
 5    │   ├── _firmwareInfo.html
 6    │   ├── _footer.html
 7    │   ├── _head.html
 8    │   ├── _header.html
 9    │   ├── _microcomputerInfo.html
10    │   ├── _otherInfo.html
11    │   ├── _resultForm.html
12    │   ├── _tailOfBodyTag.html
13    │   └── _testMicrocomputerOnly.html
14    ├── BLEProblem.html
15    ├── buildProblem.html
16    ├── designProblem.html
17    ├── firmwareProblem.html
18    ├── index.html
19    ├── memo.html
20    └── otherProblem.html
21
22 dist/
23├── BLEProblem.html
24├── buildProblem.html
25├── designProblem.html
26├── firmwareProblem.html
27├── index.html
28└── otherProblem.html

読み込む側の html ファイルの設定

html ファイルに ${require('/common/_microcomputerInfo.html')} と追加すると、その部分に /common/_microcomputerInfo.html の内容が追加されます。

1# src/html/buildProblem.html
2<div class="border rounded shadow-sm m-3 p-3 bg-white">
3  ${require('/common/_microcomputerInfo.html')}
4</div>
 1# /common/_microcomputerInfo.html
 2  <h2 id="microcomputerInfomationTitle">マイコンの情報</h2>
 3  <div class="form-group p-2" id="microcomputerInfomation">
 4    <label id="labelMicrocomputerInfo">マイコンの種類</label>
 5    <div class="form-check">
 6      <input class="form-check-input" type="radio" name="microcontroller" id="promicro(atmega32u4)" value="Pro Micro(ATmega32U4)">
 7      <label id="labelProMicro(ATMega32U4)" class="form-check-label" for="promicro(atmega32u4)">Pro Micro(ATmega32U4)</label>
 8    </div>
 9    <div class="form-check">
10      <input class="form-check-input" type="radio" name="microcontroller" id="promicro(RP2040)" value="Pro Micro(RP2040)">
11      <label id="labelProMicro(RP2040)" class="form-check-label" for="promicro(RP2040)">Pro Micro(RP2040)</label>
12    </div>
13  </div>
 1# dist/buildProblem.html
 2<div class="border rounded shadow-sm m-3 p-3 bg-white">
 3  <h2 id="microcomputerInfomationTitle">マイコンの情報</h2>
 4  <div class="form-group p-2" id="microcomputerInfomation">
 5    <label id="labelMicrocomputerInfo">マイコンの種類</label>
 6    <div class="form-check">
 7      <input class="form-check-input" type="radio" name="microcontroller" id="promicro(atmega32u4)" value="Pro Micro(ATmega32U4)">
 8      <label id="labelProMicro(ATMega32U4)" class="form-check-label" for="promicro(atmega32u4)">Pro Micro(ATmega32U4)</label>
 9    </div>
10    <div class="form-check">
11      <input class="form-check-input" type="radio" name="microcontroller" id="promicro(RP2040)" value="Pro Micro(RP2040)">
12      <label id="labelProMicro(RP2040)" class="form-check-label" for="promicro(RP2040)">Pro Micro(RP2040)</label>
13    </div>
14  </div>
15</div>

読み込まれる側の html ファイルの設定

読み込まれる側の html ファイルに必要な設定はありませんが、読み込まれる html ファイルのファイル名の一文字目を _ にすると、ビルド時に読み込んだファイルが dist ディレクトリにコピーされなくなります。

また、読み込まれる側の html ファイルに Javascript 式に似た <p>Main content ${props.foo}</p> のような設定を追加しておくと、読み込む側のファイルで ${require('./_main.html') foo="and you can also pass props"} とすることで文字列を受渡しできるようになります。ページタイトルのように共通部分の一部のみ変更する必要がある場合に役に立ちます。

1# src/html/buildProblem.html
2<head>
3  ${require('/common/_head.html') pageTitle="組み立てに関する問題"}
4</head>
 1# common_head.html
 2<meta charset="UTF-8">
 3  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 4
 5  <!-- Font Awesome -->
 6  <link
 7  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
 8  rel="stylesheet"
 9  />
10  <!-- Google Fonts -->
11  <link
12  href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
13  rel="stylesheet"
14  />
15  <!-- MDB -->
16  <link
17  href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/4.0.0/mdb.min.css"
18  rel="stylesheet"
19  />
20
21  <link rel="icon" href="image/favicon.ico">
22
23  <title>問診票テンプレート - ${props.pageTitle}</title>
 1# dist/buildProblem.html
 2<head>
 3  <meta charset="UTF-8">
 4  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
 5
 6  <!-- Font Awesome -->
 7  <link
 8  href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
 9  rel="stylesheet"
10  />
11  <!-- Google Fonts -->
12  <link
13  href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
14  rel="stylesheet"
15  />
16  <!-- MDB -->
17  <link
18  href="https://cdnjs.cloudflare.com/ajax/libs/mdb-ui-kit/4.0.0/mdb.min.css"
19  rel="stylesheet"
20  />
21
22  <link rel="icon" href="image/favicon.ico">
23
24  <title>問診票テンプレート - 組み立てに関する問題</title>
25</head>