概要
Font Awesome(フォントオーサム)はFreeで使えるアイコン集で、このジャンルでほぼデファクトといって良い存在になっています。Free版とPro版がありますがFree版でも約1500種類のアイコンが使えます。
本稿では、node.js環境での開発を前提としたWebフロントエンド開発で Font Awesome5 を使う時のポイントを説明します。Font Awesomeはv5になってフロントエンド開発との連携機能が大幅強化され、お手軽にJavaScriptのバンドルにフォントを埋め込むことができます。
Font Awesomeのアイコンを使う(バンドルする)には以下の3ステップを踏みます。
- Font Awesome を npm でインストールして、
- JavaScriptのコードで必要なアイコンだけを import して
- Font Awesomeの必要最小限が含まれるように webpack でバンドルする
最終的には、1つのbundle.jsの中に、そのアプリで使いたいアイコンだけが取り込まれた状態にします。
つまり、アイコンデータもすべてbundle.jsに入るので、HTML上でCSSを指定する必要も無いし、また必要なアイコンだけ取り込むのでbundle.jsのサイズも最小限にできます
ハマりポイント(例:minimizer(uglify-js)によっては Font Awesome含むビルドが重い!エラい時間がかかる!)もあるので、そこも扱います。
想定読者
- Font Awesomeを使ったことがない、または、JSにバンドルする方式では使ったことがない人
- npmやwebpackは使ったことがある人
対象環境
- npm、Webpack4、ES6、Babel7
- Font Awesomeは Font Awesome v 5.* のFree版を対象にします
- 特定のフレームワーク(Vue/React/Angular等)に特化した内容は扱いませんが知識は応用可能です
ソースコード
記事で使用した全ソースコードはこちらにあります
https://github.com/riversun/font-awesome5-js-example
(1)CDNを使って表示する
どんなアイコンがあるかは https://fontawesome.com/icons?d=gallery&m=free からさがせる。
まずは、インストール不要でHTMLで直接CDNを参照して表示する方法から。
以下のようにするだけで、犬とコメントと猫のアイコンが表示される。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
</head>
<body>
<i class="fas fa-dog fa-3x"></i>
<i class="far fa-comments fa-3x"></i>
<i class="fas fa-cat fa-3x fa-flip-horizontal"></i>
</body>
</html>
表示結果
ある意味一番簡単な方法で、
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.3/css/all.css"
integrity="sha384-UHRtZLI+pbxtHCWp1t77Bi1L4ZtiqrqD80Kn4Z8NTSRyMA2Fd33n5dQ8lWUE00s/" crossorigin="anonymous">
Font AwesomeのCSSを直接CDNから読み込み、
<i class="fas fa-dog fa-3x"></i>
のように<i>タグ
のclassに fas fa-dog fa-3x とクラスを指定するだけで美しいフォントが表示されます。
「おーさむ!」となる。
SolidスタイルとRegularスタイル→Font Awesomeのフォントスタイル
ここをみるとFont Awesomeには大量のアイコンがあることがわかるが、フォントのスタイルは4種類に分類される。
<i class="fas fa-arrow-circle-up"></i>
<i class="far fa-arrow-circle-up"></i>
<i class="fab fa-apple"></i>
Lightスタイル・・・細い線で表現されたアイコン。PRO版のみ。
このようにHTMLにCSSを記載して、<i>タグ
で指定する方法が一番簡単だしとっつきやすい。
次章以降は、本稿の目的である、JSにバンドルしてFont Awesomeを使う方法を見る。
(2)ライブラリ版をつかって表示する
ここからが本編、Font Awesomeをインストールしていく。
まずJavaScriptで使えるFont Awesomeのパッケージだが、大きく2種類ある
便宜的に基本パッケージ版(Basic packages)とSVGコア版(fontawesome-svg-core)という呼び方をするが、前者は手軽かつすぐに使えることを目的とした全部入りのパッケージ、後者はたとえば使いたいアイコンだけを選んだり、挙動を詳細に制御したりできるのが特徴。どちらもJSにバンドルすることができるので、Font Awesomeのアイコンがバンドルされた app.js を作成することができる。
2つのパッケージの目的
名称 | npmパッケージ名 | 目的 | |
基本パッケージ版 | Font Awesome (JavaScript) |
@fortawesome/fontawesome-free (※1) |
すぐに使えること |
SVGコア版 | Font Awesome SVG Core (JavaScript) |
@fontawesome-svg-core (※2) |
APIで詳細に制御できること |
※1 有償版の@fortawesome/fontawesome-proもある
※2 スタイルごとのパッケージもある(後半で説明する)
公式の解説はこちら
(2)-1基本パッケージ版 Font Awesome (JavaScript) のインストールと実行
まず、基本パッケージ版からみていく。
インストール
@fortawesome/fontawesome-freeパッケージをnpm installする
npm install @fortawesome/fontawesome-free --save
package.jsonは以下のようになる
"dependencies": {
"@fortawesome/fontawesome-free": "^5.6.3"
}
コーディング
インストールされた @fortawesome/fontawesome-freeパッケージから必要なモジュールインポートする
SolidタイプとRegularタイプを使いたいので以下のようにする。HTML内に<i>タグ
で記述したブラウザに表示したいだけなので、コードはこのimport文だけでOK。
import '@fortawesome/fontawesome-free/js/fontawesome';
import '@fortawesome/fontawesome-free/js/solid';
import '@fortawesome/fontawesome-free/js/regular';
htmlは以下のようにする。<i>タグ
を使って記述する部分は、CDNからCSSを参照する場合とおなじ。
違いはCSSファイルをインポートするかわりにでバンドル(app.js)を読み込む部分。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>I love fontawesome</title>
</head>
<body>
<i class="fas fa-dog fa-3x"></i>
<i class="far fa-comments fa-3x"></i>
<i class="fas fa-cat fa-3x fa-flip-horizontal"></i>
<script src="js/app.js"></script>
</body>
</html>
ビルドするにあたっては以下のようなpackage.jsonとwebpack.config.jsを用意する。
npm start
でブラウザで確認できるようにするのとnpm run build
でリリース用のバンドルを生成できるようにする。
{
"name": "bundle-font-awesome",
"version": "1.0.0",
"description": "How to bundle font-awesome",
"main": "index.js",
"scripts": {
"start": "webpack-dev-server",
"build": "webpack --config webpack.config.js --mode production"
},
"devDependencies": {
"webpack": "^4.19.1",
"webpack-cli": "^3.1.0",
"webpack-dev-server": "^3.1.11"
},
"dependencies": {
"@fortawesome/fontawesome-free": "^5.6.3"
}
}
const path = require('path');
module.exports = (env, argv) => {
const conf = {
mode: 'development',
devServer: {
open: true,
contentBase: path.join(__dirname, 'public'),
},
entry: {app: './src/index.js'},
output: {
path: path.join(__dirname, 'dist'),
publicPath: '/js/',
filename: '[name].js',
libraryTarget: 'umd'
}
};
return conf;
};
ブラウザで確認する
npm start
このようにバンドル(app.js)を読み込んで、htmlには以下のように
<i class="fas fa-dog fa-3x"></i>
<i class="far fa-comments fa-3x"></i>
<i class="fas fa-cat fa-3x fa-flip-horizontal"></i>
と普通にタグを書いておいただけなのにキチンと表示されるのは、@fortawesome/fontawesome-freeライブラリががんばっているから。
何をどうがんばっているかというと、以下のことをやってくれる
1. ページ内にある<i>
タグを検出して、<i>
タグ→<svg>
タグ(アイコンのベクターイメージ)に自動変換
2. ページ内のアイコンの追加や変更の自動監視
基本パッケージのこのような便利機能によって、うまくアイコンが表示される。
バンドルをビルドする
表示が確認できたところで、次にバンドル(app.js)を生成する。
以下のようにproduction用ビルドをしてバンドル(app.js)の生成がどのようになるか見てみる。
npm run build
Hash: 4181e1ed417c819468de
Version: webpack 4.28.4
Time: 1884ms
Built at: 2019-01-14 11:50:32
Asset Size Chunks Chunk Names
app.js 679 KiB 0 [emitted] [big] app
Entrypoint app [big] = app.js
[0] ./src/index.js 154 bytes {0} [built]
+ 3 hidden modules
app.jsが生成される。全部入りの基本パッケージ版を入れるとapp.jsは 679 KiBとなる。この例の場合で考えると使いたいアイコンが3種類だけなのにバンドルはアイコン全部入りになってしまうのでサイズが大きい。導入は簡単だが、基本パッケージ版ライブラリを取り込むとこのくらいのサイズを覚悟する必要がある。
まとめると↓のようになる。
ライブラリ | バンドル対象 | インポート方法 | Webpack4 Minimizer |
ビルド時間 | サイズ |
基本パッケージ版 | すべてのアイコン | インポート | Default(terser) | 1.8s | 679KiB |
本章のソースコード
(2)-2 SVGコア版Font Awesome SVG Core (JavaScript)のインストールと実行
次は、SVGコア版をつかう。
インストール
SVGコア版のパッケージ@fortawesome/fontawesome-svg-coreをインストールする。
npm install @fortawesome/fontawesome-svg-core
今インストールしたのはAPIとロジックが中心のライブラリなので、追加でSolidスタイルとRegularスタイルのアイコンデータが入っているパッケージもインストールする
npm install @fortawesome/free-solid-svg-icons
npm install @fortawesome/free-regular-svg-icons
すると、package.jsonは↓のようになる。
SVGコア(svg-core)のライブラリと、Regularスタイル、Solidスタイルのライブラリがそれぞれインストールされた状態となる。
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^1.2.12",
"@fortawesome/free-regular-svg-icons": "^5.6.3",
"@fortawesome/free-solid-svg-icons": "^5.6.3"
}
コーディング
それでは、SVGコア版のコードを書いていく。
インストールした3つのパッケージ(svg-core,Solidスタイル,Regularスタイル)をそれぞれimportした後、バンドルしたいアイコンのみ追加する処理を記述する流れとなる。
import {config, dom, library} from '@fortawesome/fontawesome-svg-core';
import {faDog, faCat} from '@fortawesome/free-solid-svg-icons';
import {faComments} from '@fortawesome/free-regular-svg-icons';
library.add(faDog,faComments,faCat);
dom.i2svg();
さっそく、コードを順にみていく。
import {config, dom, library} from '@fortawesome/fontawesome-svg-core';
ここでは、@fortawesome/fontawesome-svg-coreから config,dom,libraryの3つのオブジェクトを取得している。
それぞれの役割は以下の通り。
- config・・・各種挙動の設定用コンフィギュレーションオブジェクト
- dom・・・DOM関連のユーティリティ
- library・・・取り込みたいアイコンを追加できるユーティリティ
import {faDog, faCat} from '@fortawesome/free-solid-svg-icons';
import {faComments} from '@fortawesome/free-regular-svg-icons';
ここでは、使いたいアイコンのみを { }で指定した名前つきインポートをつかって指定する。
使いたいアイコンとして、
SolidスタイルのライブラリからfaDogとfaCat
RegularスタイルのライブラリからfaCommentsを指定してインポートしている。
library.add(faDog,faComments,faCat);
library.addでは、使いたいアイコンを追加する。いま上でインポートした3つのアイコンを利用したいので、library.addをつかってアイコンライブラリ(使用することを宣言するアイコンリスト)に追加する。
このように指定したアイコンだけをインポートすることで、バンドルのサイズを最小限に押さえることができる。
dom.i2svg();
dom.i2svgはその名のとおり<i>タグ
を<svg>タグ
に変換するためのメソッドで、これを実行すると現在のDOMツリー上に存在する<i>タグ
がアイコンに置き換わる。
dom.i2svg()で描画されない場合
さて、DOM構築タイミングによってはdom.i2svgでアイコンが表示されない場合がある。
たとえば、以下のような場合はアイコンが表示されない。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
</head>
<body>
<script src="js/app.js"></script>
<i class="fas fa-dog fa-3x"></i>
<i class="far fa-comments fa-3x"></i>
<i class="fas fa-cat fa-3x fa-flip-horizontal"></i>
</body>
</html>
理由はapp.jsの読み込みよりも後に<i>タグ
が定義されており、かつ現在のコードはapp.js読み込み直後にdom.i2svgが実行しているため。dom.i2svg実行時点では<i>タグ
が未定義となる。
dom.i2svgには要素の追加や変更を監視するような機能は無く実行時点のDOMツリーに対してのみ<i>タグ
の変換を行うため。
この例の場合は、 <script>タグ
の位置を<i>タグ
定義より後に持ってくるというのが一般的な解だが、要素の追加変更を監視できるAPI='dom.watch'があるのそちらを使う手もある。
dom.watch() を使用してiタグをアイコンに自動置換
dom.i2svgのかわりdom.watchを使えば先ほどのようなパターンでも対応してくれる
dom.watch();
dom.i2svgのTIPS
ちょっともどるが、dom.i2svgにはいくつかオプションもあるので簡単にみてみる。
具体的にDOM要素を指定する
↓のようにすると、iタグのsvgタグへの変換対象ノードを指定することができる
dom.i2svg({ node: document.getElementById('example_element_id') })
変換が終わったらコールバックを受け取る
<i>タグ
→<svg>タグ
への変換が終了したときに呼び出されるコールバックを↓のように指定することもできる
dom.i2svg({ callback: ()=>{}})
config の使い方
さきほど↓のように configオブジェクトを取得したがこの使い方を簡単にみておく
import {config, dom, library} from '@fortawesome/fontawesome-svg-core';
configを使うとFont Awesomeの挙動をカスタマイズすることができる。
このようにインポート時にconfigオブジェクトを取得できる。
以下のコード例でみてみる
import {config, dom, library} from '@fortawesome/fontawesome-svg-core';
import {faCat} from '@fortawesome/free-solid-svg-icons';
import {faComments} from '@fortawesome/free-regular-svg-icons';
library.add(faComments,faCat);
dom.i2svg();
上のコードでは、わざとfaCatとfaCommentsしかアイコンライブラリに追加していない状態で以下のHTMLのように<i class="fas fa-dog fa-3x"></i>
で犬のアイコンを表示しようとするHTMLの例となる。
<i class="fas fa-dog fa-3x"></i>
<i class="far fa-comments fa-3x"></i>
<i class="fas fa-cat fa-3x fa-flip-horizontal"></i>
npm start
npm startすると以下のように、fa-dogはJavaScriptのコードでインポートしなかったので、アイコンが見つからないときの?マークが表示される。
configを使うと、こうした場合の挙動を変更できる
さっそくconfigをつかって挙動変更してみる。コードには以下のように config.showMissingIcons = false;を追加する
import {config, dom, library} from '@fortawesome/fontawesome-svg-core';
import {faCat} from '@fortawesome/free-solid-svg-icons';
import {faComments} from '@fortawesome/free-regular-svg-icons';
config.showMissingIcons = false;
library.add(faComments,faCat);
dom.i2svg();
↓のように config.showMissingIcons =falseにすると、アイコンがみつからない場合に MissingIcon(?がアニメするアイコン)を表示しないようなる。
config.showMissingIcons = false;
これでnpm startする
npm start
表示結果
このとおり?マークのアイコンも表示されなくなった。
このようにconfigをつかうと挙動を制御することができる。
その他、いくつかの挙動をConfigで設定することができ公式に説明がある。
バンドルをビルドする
ここまではアイコンを個別にインポートするコードを書いて動作を確認してきたが、
Production用のバンドル(app.js)を生成してみる。
npm run build
Hash: e78c06652d8420836a67
Version: webpack 4.28.4
Time: 548ms
Built at: 2019-01-14 20:56:30
Asset Size Chunks Chunk Names
app.js 33.7 KiB 0 [emitted] app
Entrypoint app = app.js
[0] ./src/index.js + 3 modules 818 KiB {0} [built]
| ./src/index.js 462 bytes [built]
| + 3 hidden modules
サイズは33.7KiB!基本パッケージ版に比べてだいぶ小さくなった。
まとめると以下のとおり。
ライブラリ | バンドル対象 | インポート方法 | Webpack4 Minimizer |
ビルド時間 | サイズ |
基本パッケージ版 | すべてのアイコン | インポート | Default(terser) | 1.8s | 679KiB |
SVGコア版 | 必要なアイコン3個ぶん | インポート | Default(terser) | 0.5s | 33.7KiB |
本章のソースコード
本章では、SVGコアをつかった、最小限のアイコンのインポートをみてきた。
ソースコードは以下のとおり。
Font AwesomeのProductionビルドに非常に時間がかかるパターン
あまり出会わないかもしれないが、出会ってしまうとハマるポイントを整理しておく。
それは、Font Awesomeをインポートして、Productionビルドをするときに、非常に長い時間待たされるパターンのこと。
そのパターンとは、WebpackするときのMinimizer(JSをminifyしてくれるライブラリ)として独自にUglify-JSを使っているプロジェクト場合は要注意。Webpackとセットで使う場合はuglifyjs-webpack-pluginと共に使うことが多いかもしれないが、Uglify-Jsには Tree Shaking(不要なコードを省く処理)に問題があるためビルドに非常に時間がかかる。
では、どうすればいいかというと、基本的には Minimizerを terserに移行すればいい。
terserは、そもそもUglify-jsをforkして作られたモノなので機能も近いし、Webpack4はデフォルトのMinimizerとしてterserが使われるようになっておりMinimizerを明示的に設定しない状態で、Webpack4をつかってProductionビルドする場合は標準ではterser(※)が利用される。
※terserはterser-webpack-pluginというパッケージを明示的にインストールして使うことも可能。そのコード例はこちらにのせた
さて、あまりいないかもしれないが、念のためどうしてもUglify-JSを使い続けたい場合のための回避策を示す。
まず Uglify-jsベースにするために以下のようにパッケージをインストールして、webpack.config.jsを変更する
Uglify-jsでビルドしてみる
npm install uglifyjs-webpack-plugin --save-dev
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
...
optimization: {
minimizer: [
new UglifyJSPlugin({
uglifyOptions: {compress: {drop_console: true}},
}),
],
},
この状態でProductionビルドすると結果は以下のようになる。
npm run build
Hash: 5d6d07bebcafd21daab4
Version: webpack 4.28.4
Time: 81642ms
Built at: 2019-01-14 22:56:33
Asset Size Chunks Chunk Names
app.js 33.3 KiB 0 [emitted] app
なんとUglify-JSベースだと81秒もかかってしまう。
ライブラリ | バンドル対象 | インポート方法 | Webpack4 Minimizer |
ビルド時間 | サイズ |
フル版 | すべてのアイコン | インポート | Default(terser) | 1.8s | 679KiB |
SVGコア版 | 必要なアイコン3個ぶん | インポート | Default(terser) | 0.5s | 33.7KiB |
Uglify-JS | 81.6s | 33.3KiB |
Deep Import(ディープインポート)
前に書いたとおりUglify-JSでは、Tree Shakingで不要コードを探索するための処理に問題がある。そこで、その回避策としてDeep Import(ディープインポート)という方法があるので、以下がそのコード例となる。
import { dom, library} from '@fortawesome/fontawesome-svg-core';
import {faDog} from '@fortawesome/free-solid-svg-icons/faDog';
import {faCat} from '@fortawesome/free-solid-svg-icons/faCat';
import {faComments} from '@fortawesome/free-regular-svg-icons/faComments';
library.add(faDog,faComments,faCat);
dom.i2svg();
Deep Importは以下のようにダイレクトにアイコン(のパスデータが格納されている)モジュールをインポートする方法となる。
ここでは犬のベクターイメージを格納したモジュールを直接importする例を書いた。
import {faDog} from '@fortawesome/free-solid-svg-icons/faDog';
これによりUglify-JSが抱えているTree Shaking問題を回避しビルド(バンドル)時間短縮が期待できる。
実際にビルドしてみると、
npm run build
Hash: c54bade0ad01d67f30c6
Version: webpack 4.28.4
Time: 625ms
Built at: 2019-01-15 18:52:43
Asset Size Chunks Chunk Names
app.js 34 KiB 0 [emitted] app
0.6sで実行できた!
本章のソースコード(deep import)
本章ではDeep ImportをしてTree Shakingを回避する方法をみてきた。
以下がそのソースコード
Font Awesomeのバンドル方法のまとめ
これまでフル版とSVGコア版、それぞれのパッケージでのアイコンのバンドル方法を実際に試しながら見てきた。
アプローチと結果をまとめると以下のようになる。
ライブラリ | バンドル対象 | インポート方法 | Webpack4 Minimizer |
ビルド時間 | サイズ |
フル版 | すべてのアイコン | インポート | Default(terser) | 1.8s | 679KiB |
SVGコア版 | 必要なアイコン3個ぶん | インポート | Default(terser) | 0.5s | 33.7KiB |
Uglify-JS | 81.6s | 33.3KiB | |||
ディープインポート | Uglify-JS | 0.6s | 34KiB |
みてわかるとおり、Font AwesomeのパッケージはSVGコア版-必要最小限のみアイコンをインポート-MinimizerはWebpack4デフォルト(terser)を使う というのがいちばん筋が良さそう、ということがわかる。
まとめ
- Font Awesome5をJSにバンドルする方法について説明しました。
- @fortawesome/fontawesome-svg-core をつかって個別にアイコンをインポートするとバンドルのサイズを最小限に抑えられることがわかりました
- Minimizerに Uglify-JS を使うとコンパイル時間が長くかかる場合があるため、Webpackを使っている場合は Default(terser)の状態でProductionビルドをするのがオススメです。