JavaScript
es6
FontAwesome
webpack
babel

1年後に差がつくFont Awesome5 ~フロントエンド開発(ES6,Webpack4,Babel7)への導入~


概要

Font Awesome(フォントオーサム)はFreeで使えるアイコン集で、このジャンルでほぼデファクトといって良い存在になっています。Free版とPro版がありますがFree版でも約1500種類のアイコンが使えます。

image.png

本稿では、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にバンドルする方式では使ったことがない人


  • npmwebpackは使ったことがある人


対象環境



  • npmWebpack4ES6Babel7

  • 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を参照して表示する方法から。

以下のようにするだけで、犬とコメントと猫のアイコンが表示される。


index.html

<!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>

表示結果

image.png

ある意味一番簡単な方法で、

 <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>タグclassfas fa-dog fa-3x とクラスを指定するだけで美しいフォントが表示されます。

「おーさむ!」となる。


SolidスタイルとRegularスタイル→Font Awesomeのフォントスタイル

ここをみるとFont Awesomeには大量のアイコンがあることがわかるが、フォントのスタイルは4種類に分類される。



  • Solidスタイル・・・中が塗りつぶされているアイコンimage.png

    Solidスタイルにはfasというクラスを指定する


Solidスタイルの例

<i class="fas fa-arrow-circle-up"></i>




  • Regularスタイル・・・線画で表現されたアイコンimage.png

    Regularスタイルにはfarというクラスを指定する


Regularスタイルの例

<i class="far fa-arrow-circle-up"></i>




  • Brandsスタイル・・・ブランドのアイコン image.png

    Brandsスタイルにはfabというクラスを指定する


Brandsスタイルの例

<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は以下のようになる


package.json(抜粋)

  "dependencies": {

"@fortawesome/fontawesome-free": "^5.6.3"
}


コーディング

インストールされた @fortawesome/fontawesome-freeパッケージから必要なモジュールインポートする

SolidタイプとRegularタイプを使いたいので以下のようにする。HTML内に<i>タグで記述したブラウザに表示したいだけなので、コードはこのimport文だけでOK。


index.js

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)を読み込む部分。


index.html

<!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.jsonwebpack.config.jsを用意する。

npm startでブラウザで確認できるようにするのとnpm run buildでリリース用のバンドルを生成できるようにする。


package.json

{

"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"
}
}


webpack.config.js

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

表示結果

image.png

このようにバンドル(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)の生成がどのようになるか見てみる。


production用ビルドを実行する

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


本章のソースコード

https://github.com/riversun/font-awesome5-js-example/tree/master/step01_basic


(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スタイルのライブラリがそれぞれインストールされた状態となる。


package.json(抜粋)

  "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した後、バンドルしたいアイコンのみ追加する処理を記述する流れとなる。


index.js

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スタイルのライブラリからfaDogfaCat

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を使えば先ほどのようなパターンでも対応してくれる


要素の追加や変更を監視してiタグをアイコンに置き換えてくれる

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オブジェクトを取得できる。

以下のコード例でみてみる


index.js(dogを追加していない状態)

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();

上のコードでは、わざとfaCatfaCommentsしかアイコンライブラリに追加していない状態で以下の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のコードでインポートしなかったので、アイコンが見つからないときの?マークが表示される。

表示結果

faa.gif

configを使うと、こうした場合の挙動を変更できる

さっそくconfigをつかって挙動変更してみる。コードには以下のように config.showMissingIcons = false;を追加する


index.js

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(?がアニメするアイコン)を表示しないようなる。


MissingIconを表示しないようにする

config.showMissingIcons = false;


これでnpm startする

npm start

表示結果

image.png

このとおり?マークのアイコンも表示されなくなった。

このようにconfigをつかうと挙動を制御することができる。

その他、いくつかの挙動をConfigで設定することができ公式に説明がある。


バンドルをビルドする

ここまではアイコンを個別にインポートするコードを書いて動作を確認してきたが、

Production用のバンドル(app.js)を生成してみる。


production用ビルドを実行する

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コアをつかった、最小限のアイコンのインポートをみてきた。

ソースコードは以下のとおり。

https://github.com/riversun/font-awesome5-js-example/tree/master/step02_import


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(※)が利用される。

terserterser-webpack-pluginというパッケージを明示的にインストールして使うことも可能。そのコード例はこちらにのせた

さて、あまりいないかもしれないが、念のためどうしてもUglify-JSを使い続けたい場合のための回避策を示す。

まず Uglify-jsベースにするために以下のようにパッケージをインストールして、webpack.config.jsを変更する


Uglify-jsでビルドしてみる


インストール

npm install uglifyjs-webpack-plugin --save-dev



webpack.config.js抜粋

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(ディープインポート)という方法があるので、以下がそのコード例となる。


index.js(ディープインポートの例)

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を回避する方法をみてきた。

以下がそのソースコード

https://github.com/riversun/font-awesome5-js-example/tree/master/step03_import_deeply


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ビルドをするのがオススメです。