Nuxt.js(v2)でMaterialDesignIconsの好きなアイコンだけのWebフォントを作って利用する

# Nuxt.js(v2)でMaterialDesignIconsの好きなアイコンだけのWebフォントを作って利用する

#### 動作確認済みバージョン

- Nuxt.js(2.11.0)
- Node.js(v12.15.0)
- npm(v6.13.4)
- @mdi/font(v4.9.95)
- @mdi/font-build(v1.7.0)
- terminal(zsh)

#### 更新履歴

- 2020/2/10 初稿

#### 目標

- @mdi/fontを読み込んで表示する
- @mdi/font-buildで指定のアイコンのみbuildして表示する
- バンドルサイズを比較する

---

## Material Design Iconsとは

5000種類上のアイコンを提供している[Material Desing Icons](https://materialdesignicons.com/)をNuxt.jsで使う場合はCDN利用が一般的です。ですが、目標は指定のアイコンのみフォント化して大幅な軽量化を図ることです。

今回はCDNを利用せず、npmパッケージを利用して読み込んでいきます。

## @mdi/fontを読み込んで表示する

[`@mdi/font`](https://www.npmjs.com/package/@mdi/font)パッケージをインストールし、nuxt.config.jsへパッケージのCSSを読み込ませます。CDNと同様に全てのアイコンを読み込んでバンドルサイズを確認してみます。

↓

```Console:terminal
% npm i @mdi/font
```

```javascript:nuxt.config.js
export default {
  css: [
    '@mdi/font/css/materialdesignicons.css'
  ],
```

動作確認用のページを作成します。今回使用するアイコンは2つです。

↓

```vue:pages/test.vue
<template>
  <div>
    <i class="mdi mdi-face" />
    <i class="mdi mdi-nuxt" />
  </div>
</template>
```

`% npm run dev`で動作確認をします。faceアイコンと、Nuxtアイコンが表示されたら成功です。

### 全読み込み時のバンドルサイズを確認

「アイコンフォント」と「アイコンを呼び出すCSS」のバンドルサイズを確認します。バンドルサイズは、build時にanalyzeオプションを指定することで確認できます。

↓

```Console:terminal
% npx nuxt build -a
```

ビルド中にフォントデーターのバンドルサイズについて警告が出ます。5,000種ものアイコンが格納されているにしてはとても軽いデーターです。しかし、2つのアイコンのためにはやりすぎな印象があります。

↓

```
WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).This can impact web performance.

Assets:
  fonts/2d0a0d8.eot (825 KiB)
  fonts/d006653.woff2 (276 KiB)
  fonts/b4917be.woff (395 KiB)
  fonts/f511123.ttf (825 KiB)
```

ビルドが終わるとバンドルサイズのレポートが表示されます。materialdesignicons.cssの容量の大きさが目立ちます。なので、アイコンフォントとCSSの2つを同時に改善していきます。

↓
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217743/649b08f2-f44c-2f37-da60-8a63862691b8.png)

## @mdi/font-buildパッケージを使う

[@mdi/font-build](https://www.npmjs.com/package/@mdi/font-build)パッケージは、SVG画像をWebフォントへと変換します。また、出力される内容は@mdi/fontと同様のCSSも梱包されます。なので、開発時は全てのアイコンを、リリース時には利用アイコンのみへとシームレスな対応が可能です。

### タスクの整理

一手間かかるためタスクを先に書き出します。

- [ ] @mdi/font-buildをインストール
- [ ] 格納ディレクトリと設定ファイルの作成
- [ ] 公式サイトからSVG画像を取得しSVGディレクトリへ格納
- [ ] CheatSheetからフォントのメタ情報を取得しmeta.jsonへ記載
- [ ] font-build.jsonへビルド設定を記載
- [ ] ビルド＆表示テスト
- [ ] .gitignoreファイルの修正

### @mdi/font-buildをインストール

[@mdi/font-build](https://www.npmjs.com/package/@mdi/font-build)パッケージインストールします。

↓

```Console:terminal
% npm i @mdi/font-build
```

### 格納ディレクトリと設定ファイルの作成

一目でわかるよう、ルートにfontディレクトリを作成し必要な設定ファイル等を作成します。最終的なディレクトリ構成は以下となります。

↓

```
font/
  ┣ svg/            // ← 変換するSVG画像を格納するディレクトリ
    ┣ face.svg
    ┗ nuxt.svg
  ┣ font-build.json // ← ビルド設定用ファイル
  ┣ meta.json       // ← フォントのメタ情報記載用ファイル
  ┗ dist/　         // ← ビルド後に出力されるディレクトリ
```

ターミナルコマンドで必要なディレクトリとファイルを作成します。エディターで制作しても構いません。

↓

```Console:terminal
% mkdir -p font/svg
% cd font/
% touch font-build.json meta.json
```

### 公式サイトからSVG画像を取得しSVGディレクトリへ格納

[公式サイト](https://materialdesignicons.com/)から指定のアイコンを探し、1つづつSVG画像をダウンロードしていきます。ダウンロードしたSVG画像は、先ほど作成したSVGディレクトリに格納します。

今回の指定は「face」アイコンと「nuxt」アイコンの2つです。以下の手順でSVG画像をダウンロードします。

1. [公式サイト](https://materialdesignicons.com/)へアクセス
2. 検索窓に「face」入力（検索結果のソートに少し時間がかかります）
3. ソートされたアイコン群の中から「指定のアイコン」を探してクリック
4. アイコン情報ドロワー右下の「Iocn Package▼」をクリック
5. 「.SVG Optimized」をクリックしてSVG画像をダウンロード
6. ダウンロードされた「face.svg」を作成したSVGディレクトリへ格納

nuxtアイコンも同様にダウンロード・格納したら完了です。

### CheatSheetからフォントのメタ情報を取得しmeta.jsonへ記載

先ほど作成した「meta.json」へ、フォントのメタ情報を記載していきます。記載できるメタ情報は、アイコンの作者やバージョンをはじめ多岐に渡りますが、今回は最低限の名前（name）と文字コード(codepoint)のみを記載していきます。

各アイコンの名前（name）と文字コード(codepoint)は、公式サイトの[Cheatsheet](https://cdn.materialdesignicons.com/4.9.95/)から確認できます。名前でブラウザ検索をして探すとすぐに見つかります。

記載例は以下です。

↓

```json:font/meta.json
[
  {
    "name": "nuxt",
    "codepoint": "F0131"
  },
  {
    "name": "face",
    "codepoint": "F643"
  }
]
```

### font-build.jsonへビルド設定を記載

先ほど作成した「font-build.json」へビルド設定を記載していきます。設定ではプレフィックスの変更から各種出力ファイルの名称の変更などが可能ですが、@mdi/fontからの移行をスムーズにするため、そのままの名称を利用します。

記載例は以下です。

↓

```json:font/font-build.json
{
  "name": "Original icons",
  "prefix": "mdi",
  "fileName": "materialdesignicons",
  "fontName": "Material Design Icons",
  "fontFamily": "materialdesignicons",
  "version": {
      "major": 0,
      "minor": 0,
      "patch": 1
  }
}
```

Tips:バージョン情報は必須です。プロジェクトに合わせて変更してください。

### ビルドテスト

準備完了です。fontディレクトリへ移動し`% npx @mdi/font-build`を実行します。フォントファイルをはじめ、html、scss、cssファイルがdistディレクトリへ出力されたら成功です。

↓

```Console:terminal
% cd font/
% npx @mdi/font-build

↓
// 出力結果抜粋

Generating from 2 icons:
- Folders created.
- Generated ttf, eot, woff, and woff2false
- Generated index.html
- Generated original-name.scss
  - Generated _animated.scss
  - Generated _core.scss
  - Generated _extras.scss
  - Generated _functions.scss
  - Generated _icons.scss
  - Generated _path.scss
  - Generated _variables.scss
- Generated original-name.css / *.min.css / *.map
```

### 表示テスト

nuxt.config.jsで、@mdi/fontパッケージから読み込んでいたCSSを先ほど書き出したCSSへ変更ます。変更後、rootディレクトリへ戻って`% npm run dev`を実行してください。

うまくフォントが表示されたら成功です。

```javascript:nxut.config.js
export default {
  css: [
    //'@mdi/font/css/materialdesignicons.css'
    '~~/font/dist/css/materialdesignicons.css'
  ],
  ...
```

```Console:terminal
% npm run dev
```

### .gitignoreの修正

Nuxt.jsのデフォルトでは、「dist」という名称のディレクトリはGitの管理から除外されるため、「font/dist」ディレクトリも除外されます。

.gitignoreファイルを修正し生成したフォントもGit管理できるようにします。

↓

```.gitignore
...
# Nuxt generate
# dist // 「dist」を「./dist」へ変更
dist/**
...
```

## バンドルサイズを比較する

`% npx nuxt build -a`を実行して、バンドルサイズを確認します。

まずは、アイコンフォントサイズを確認・比較します。

↓

```
// 変更前

fonts/2d0a0d8.eot    825 KiB  
fonts/b4917be.woff    395 KiB
fonts/d006653.woff2    276 KiB  
fonts/f511123.ttf    825 KiB

// 変更後

fonts/cab1055.eot   2.08 KiB
fonts/8a63b2b.woff    1.2 KiB
fonts/6cffea0.ttf   1.86 KiB
```

かなり軽くなりました。成功です…が、woff2ファイルがバンドルされていません。焦らずにレポートの中を探してみると、app.jsの中にCSSにバンドルされていました。@mdi/font-buildでは、Webフォントがとても軽い場合、base64形式でCSSへバンドルされます。

↓
![02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/217743/8cda0025-7ec7-16d5-7e4f-aded14d80626.png)


以上で、目標達成です。

## おまけ

気付いた方もいるかもしれませんが、@mdi/font-buildを利用すると自作のSVGもWebフォントに変換可能です。自分で作った画像がMDIと同じ仕様で使えるとか胸熱です。

また、やりたいことが増えてしまった…。シンプルに生きたい。