Vite×11tyを実制作で使うための準備

Vite × 11tyを用いた開発環境で、入出力先のディレクトリ構成やヘッダ・フッタ等の共通パーツ化をし、実制作で使うための準備をしていきます。
2023.11.19

この記事では、Vite × 11tyを実業務で利用するための検証を行います。これまでの記事ではHTMLファイルの生成やSassの導入等を行いました。

今回は、入出力先のディレクトリ構成やヘッダ・フッタ等の共通パーツ化をし、より実制作で使いやすい状態にしていきたいと思います。

実現したいこと

  1. 入出力ディレクトリ(src、dist)を指定したい
  2. 任意のディレクトリ名、ファイル名で出力したい
  3. ヘッダ・フッタの共通パーツ化
  4. ヘッダは共通化するがtitle等はページ個別に指定したい

これらをVite × 11tyで設定する方法を検証していきます。

対象ユーザーのスキル
  • HTML
  • CSS
  • JS
  • GulpやWebpack等のビルドツール利用経験

検証

検証時の環境
  • macOS バージョン: 13.6
  • node: v18.16.0
  • Google Chrome バージョン: 119.0.6045.123

Viteプロジェクトの作成

Viteで新規プロジェクトを作成します。11tyのプラグインを使用したいので4系でバージョン指定します。
参考サイト:eleventy-plugin-vite「A plugin to use Vite v4.0」11tyのプラグインはViteの4系で動作確認されている
create-vite Version create viteの4系の最新は4.4.1

npm create vite@4.4.1

ウィザードが開始されるので、設定を選択していきます。

Ok to proceed? (y) y
✔ Project name: … vite-project
✔ Select a framework: › Vanilla
✔ Select a variant: › JavaScript

vite-projectディレクトリが作成され、package.jsonやサンプルファイルが生成されました。
プロジェクトのルートに移動して動作を確認します。

cd vite-project
npm install
npm run dev

表示されたURLにアクセスすると、Hello Viteが表示されました。http://localhost:5173/
挙動の確認ができたのでサンプルファイルは削除しておきます。 package.json、node_modulesだけ残します。

rm -r public index.html style.css main.js counter.js javascript.svg

HTMLテンプレートを使いたいので、Viteで11tyを扱うためのプラグインをインストールします。

npm install --save-dev vite-plugin-eleventy

vite.config.jsを作成して、プラグインを使うための設定をファイルに記述します。

cat << EOF > vite.config.js
import { eleventyPlugin } from "vite-plugin-eleventy";

export default {
  plugins: [eleventyPlugin()],
}
EOF

Sassも使いたいのでインストールしておきます。

npm install --save-dev sass

動作を確認するため、HTMLテンプレートファイルを作成します。

cat << EOF > index.njk
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Viteのテストだよ</title>
  <link href="/style.scss" rel="stylesheet">
</head>

<body>
  <h1>見出し</h1>
  <p>本文</p>
</body>
</html>
EOF

SCSSファイルを作成します。

cat << EOF > style.scss
body {
  background: #000;
  color: #fff;
  p {
    font-size: 18px;
  }
}
EOF
npm run dev

作成したファイルが表示されました。http://localhost:5173/

ビルドしてみます。

npm run build

distディレクトリが生成されました。

tree -I node_modules
.
├── dist
│   ├── assets
│   │   └── index-c6a46e2d.css
│   └── index.html
├── index.njk
├── package-lock.json
├── package.json
├── style.scss
└── vite.config.js

ここまでは以前の記事MicroCMSとViteで作るかんたん静的サイトで紹介している内容になります。今回の検証の下準備として作業手順のみを紹介しましたので、それぞれの詳細に関しては以前の記事を参照してください。

検証1. 入出力ディレクトリ(src、dist)を指定したい

プロジェクトのルートディレクトリと出力先の設定を行います。以下の設定で既存ファイルを上書きします。

vite.config.js

import { eleventyPlugin } from "vite-plugin-eleventy";

export default {
  plugins: [eleventyPlugin()],
  root: "src",
  build: {
    outDir: "../dist",
    emptyOutDir: true,
  }
}

rootはプロジェクトのrootディレクトリを指定、ここではsrcとします。
参考サイト:vite 共通オプション root

buildに出力先を設定する、rootからの相対パスで指定します。
参考サイト:vite ビルドオプション build.outDir

emptyOutDirをtrueにしておくとビルドの都度、出力先を空にしてから出力します。
参考サイト:vite ビルドオプション build.emptyOutDir
ページや画像ファイルを削除したとき、ファイル名を変更したときに出力先に不要ファイルを残したくないので、基本的にはtrueにしておきます。

srcディレクトリを作成し、入力元となるファイルを移動します。

mkdir src
mv index.njk src/index.njk
mv style.scss src/style.scss

移動後のディレクトリ構成はこちらです。

tree -a -I node_modules
.
├── dist
│   ├── assets
│   │   └── index-c6a46e2d.css
│   └── index.html
├── package-lock.json
├── package.json
├── src
│   ├── index.njk
│   └── style.scss
└── vite.config.js

正常にビルドできました。

% npm run build

> vite-project@0.0.0 build
> vite build

vite v4.5.0 building for production...
✓ 2 modules transformed.
../dist/index.html                 0.22 kB │ gzip: 0.22 kB
../dist/assets/index-c6a46e2d.css  0.06 kB │ gzip: 0.07 kB
✓ built in 153ms

検証2. 任意のディレクトリ名、ファイル名で出力したい

ランディングページなどの1枚もののサイトであれば、ここまでの設定でも十分に利用できますが、複数ページを展開していく際に、ディレクトリ名もファイル名も指定したいケースが出てきます。
そこで、下記の構成のサイトを出力できるよう設定していきます。

/index.html
/about/index.html
/services/service01.html
/services/service02.html

参考サイト:11ty PERMALINKS vite.config.jsのrootで指定したディレクトリに配置されたファイルがどのようにbuild.outDirに出力されるか

ドキュメントルートにindex.htmlを生成したい

Input: src/index.njk
Output: dist/index.html

aboutディレクトリを作ってindex.htmlを配置したい

このケースには2つのパターンがあります。
Input: src/about.njk
Output: dist/about/index.html

Input src/about/index.njk
Output dist/about/index.html
どちらも同じ結果になりますが、srcディレクトリ内の異なるファイルが同じパスに出力されるケースがあるため、競合しないように留意する必要があります。
srcとdistのディレクトリ構成が揃ってたほうが個人的には分かりやすいので、こちらの構成にします。servicesディレクトリも同じ方針で配置します。

src/index.njk
src/about/index.njk
src/services/service01.njk
src/services/service02.njk

動作確認に必要なファイルをコピーで作成します。

mkdir src/about
mkdir src/services/
cp src/index.njk src/about/index.njk
cp src/index.njk src/services/service01.njk
cp src/index.njk src/services/service02.njk

servicesディレクトリ配下に任意のファイル名でhtmlを出力したい

Input: src/services/service01.njk
Output: dist/services/service01.html

Input src/services/service02.njk
Output dist/services/service02.html

index.html以外のファイル名を生成したい場合、YAMLの Frontmatterのpermalinkを使って設定します。
参考サイト:Front Matter
service01.njkの冒頭にパスを追記します。

service01.njk

---
permalink: /services/service01.html
---
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Viteのテストだよ</title>
  <link href="/style.scss" rel="stylesheet">
</head>

<body>
</body>
</html>

service02.njkにも同様にこちらの記述を追記します。

service02.njk

---
permalink: /services/service02.html
---

ビルドしてみます。

npm run build

想定どおりに出力されました。

tree dist
dist
├── about
│   └── index.html
├── assets
│   └── style-c6a46e2d.css
├── index.html
└── services
    ├── service01.html
    └── service02.html

assetsの中のCSS、JS、画像ファイルを分けたい

アセットファイルを拡張子に応じて指定したディレクトリに出力するためのロジックを記述しています。

vite.config.js

import { eleventyPlugin } from "vite-plugin-eleventy";

export default {
  plugins: [eleventyPlugin()],
  root: "src",
  build: {
    outDir: "../dist",
    emptyOutDir: true,
    rollupOptions: {
      output: {
        assetFileNames: function(assetInfo) {
          const cssRegex = new RegExp('\.css$', 'i');
          const imgRegex = new RegExp('\.(gif|jpeg|jpg|png|svg|webp)$', 'i');
          if(cssRegex.test(assetInfo.name)) {
            return 'css/[name].min.[ext]';
          } else if(imgRegex.test(assetInfo.name)) {
            return 'images/[name].[ext]';
          } else {
            return 'assets/[name].[ext]';
          }
        }
      }
    }
  }
}

参考サイト:Rollup output.assetFileNames ビルド時に出力するファイルパスのパターンと、パターンを返すための関数が使える
参考サイト:Rollup AssetInfo assetInfoはgenerateBundleからfileNameを除いたもの
参考サイト:JavaScript RegExp 正規表現を使ってファイル名に特定の拡張子が含まれているかを確認しています。
参考サイト:RegExp.prototype.ignoreCase プロパティiを付けて大文字小文字を区別せずに照合します。

検証3. ヘッダ・フッタの共通パーツ化

_includesディレクトリの中にレイアウトテンプレートを配置すると全ページから参照されるようになるので共通化が出来ます。
参考サイト:11ty DIRECTORY FOR INCLUDES
レイアウトテンプレートのディレクトリ名や配置場所を変更したい場合は.eleventy.jsファイルを作成して設定する必要がありますが、今回は.eleventy.jsファイルを用意せずにデフォルト設定のまま使用します。
_includesディレクトリの中にサイト共通のレイアウトテンプレートとするHTMLファイルを作成します。

mkdir src/_includes
cat << EOF > src/_includes/mylayout.njk
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Viteのテストだよ</title>
  <link href="/style.scss" rel="stylesheet">
</head>

<body>
  <header>
    <a href="/">HOMEへ戻る</a>
  </header>

  <article>
    {{ content | safe }}
  </article>

  <footer>
    &copy; 知恵蔵.com
  </footer>
</body>
</html>
EOF

全ページで表示したい<header>、<footer>を配置しています。{{ content | safe }} の箇所に各ページのコンテンツが挿入されます。(headerとfooterのパーツファイルを作成して各ページからincludeで読み込む方法とは逆の考え方になります。)
contentを読み込む際にsafeを付けるとエスケープ処理をしないのでHTMLタグが使えるようになります。
参考サイト:11ty PREVENT DOUBLE-ESCAPING IN LAYOUTS
index.njkがmylayout.njkを参照するように書き換えます。ヘッダ・フッタはmylayout.njkに記述があるので、index.njkにはコンテンツ部分のみを書きます。
参考サイト:11ty LAYOUTS

/src/index.njk

---
layout: mylayout
---
<h1>見出し</h1>
<p>本文</p>

ビルドしてみます。

npm run build

生成されたdist/index.htmlのソースを確認してみます。

/dist/index.html

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Viteのテストだよ</title>
  
  <link rel="stylesheet" href="/css/style.min.css">
</head>

<body>
  <header>
    <a href="/">HOMEへ戻る</a>
  </header>

  <article>
    <h1>見出し</h1>
    <p>本文</p>
  </article>

  <footer>
    &copy; 知恵蔵.com
  </footer>
</body>
</html>

mylayout.njkとindex.njkの内容が反映されていることが確認できました。

検証4. ヘッダは共通化するがtitle等はページ個別に指定したい

ヘッダのmetaタグはサイトの全ページで同じ記述にするわけにはいかないので、titleの個別設定を行います。front matterの記述でtitleの変数化ができます。
参考サイト:11ty LAYOUTS

mylayout.njk

---
title: Viteのテストだよ
---
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>{{ title }}</title>
  <link href="/style.scss" rel="stylesheet">
</head>

<body>
  <header>
    <a href="/">HOMEへ戻る</a>
  </header>

  <article>
    {{ content | safe }}
  </article>

  <footer>
    &copy; 知恵蔵.com
  </footer>
</body>
</html>

ページ個別にfront matterの記述でtitleを書くとmylayout.njkのtitleよりも優先されます。
参考サイト:11ty SOURCES OF DATA

index.njk

---
layout: mylayout
title: タイトル書き換えるなり
---
  <h1>見出し</h1>
  <p>本文</p>

ビルドしてみます。

npm run build

生成されたhtmlファイルのソースを確認してみます。

/dist/index.html

<title>タイトル書き換えるなり</title>


これで、ランディングページや小規模なサイトであれば、制作に必要な要件を満たせたかと思います。実際の業務でも使ってみたいと思います。