CSS
JavaScript
Node.js
TypeScript
webpack

今時のフロントエンド開発2017 (1. 愚痴編)

More than 1 year has passed since last update.

表記ゆれや表現に気をつけながら書いてはいるものの,既に怪しいところがあると思いますのでもう少し丁寧を心がけたいと思います。

良いものを書きたいので指摘などは大歓迎です。

その際はコメントや編集リクエストをいただければドキドキしながら修正します。

大きな変更が加わるときは通知すると思います。


はじめに

これまでのフロントエンドの開発には多くの問題や面倒ごとを抱えています。

その解決手段としてよくビルドツールやaltJSといったワードを目にしますが,これらがどういった目的で利用されているのかについて触れながら進めていきます。

主にパッケージ管理やビルドツールを初めて見る人向けになっているので全編通すとそれなりに長いです。

動かすまでが長めになっていますが理解してしまえば本当に必要な手順はさほど多くありません。

実際に開発を始めるために必要な準備はせいぜい1~2つのファイルを記述してコマンドを数回叩く程度なので,過度な抵抗感を持たずに読んでいただけたらなと思います。

登場するツールや技術は多くの問題を解決してくれますが,これらは飽くまで手段です。

紹介する技術を使うことが今時のフロントエンド開発だといい切りたいわけではないことはご理解ください。


おしながき


基本中の基本

フロントエンドの開発の基本として次のようなものがあります。


  • HTMLを書く

  • CSSを書いてHTMLで読み込む

  • JavaScriptを書いてHTMLで読み込む

この方法については何も間違っていませんし,今でもこういった手法で開発をしている方もいるかもしれません。

しかし,このような方法で開発していると頭を抱えることがあると思います。


無駄の多いデータ

可読性を良くするために改行やインデントを入れるとその分ファイルサイズが増えます。

サーバーからダウンロードしてブラウザ上で実行するという特性上ソースコードに書かれた内容がそのままファイルサイズになりダウンロード時間に影響します。

そう考えると次のような大量のインデントの後のカッコはインデントの方が多く無駄でしかありません。

                    {

"json": "There are a lot of things we can reduce."
}

ファイルサイズが大きくなるということはその分だけサーバーからダウンロードするのに時間がかかり,ブラウザに表示されるまでの時間も長くなります。

GoogleのSEOではページの読み込み速度にも重み付けをしていますし,Amazonの調査では0.1秒遅くなると売上が1%減ると発表しています。

他にも表示速度がコンバージョン率やページビューの低下に影響するなど散々いわれています。

画像などのメディアに比べれば大した容量にはなりませんが,無駄を省けるのであれば省くに越したことはありません。

たかがテキストデータされどテキストデータです。

wwwサーバーでgzipやdeflateでデータを圧縮してから転送する設定がされていれば,テキストデータは圧縮効率が良いため転送量を少なくすることができます。

しかし,レンタルサーバーやブラウザによっては圧縮してから転送できないこともあるので,圧縮機能に任せきるのではなく根本的に解決したいところです。


リクエスト数とサーバーレスポンス

リクエスト数とはブラウザが「このデータを表示したいから送ってくれ」とサーバーに要求する数をいいます。

例えば次のようにHTMLファイル内で2つの画像,外部のCSS内で2つの画像,外部のJavaScriptを2つ読み込みます。


  • HTML自身

  • HTML内の画像 x2

  • CSS自身

  • CSS内の画像 x2

  • JavaScript x2

すると8回のリクエストが発生します。

つまり,ファイルを分割して<link><script src="">で読み込むファイルを増やすということはリクエスト数の増加を意味しています。

ブラウザは複数のリクエストを同時に処理することができますが無限にリクエスト数が増えても良いわけではありません。

ブラウザが同時に行えるリクエスト数は1ホストあたりの上限が決まっており,リクエストの上限に達すると終わったものからキューから外され待機している次のリクエストが発生します。

そして,リクエスト数において重要なのはむしろサーバー側で,たった8回のリクエストでも同時に1,000人が閲覧すれば8,000リクエストになります。

リクエストを捌ききれなくなるとサーバーは503 Service Unavailableなどを返します。

さらに過負荷になるといわゆるサーバーが落ちるといった状態になります。

サーバーの負荷はリクエスト数が大きく関わってくるので少ないに越したことはありません。

以下はおまけですが1回のリクエスト時間の内容とその意味です。


Queueing

リクエストがキューに登録されるのに要した時間

Stalled

プロキシネゴシエーション待ちなどリクエストを送信できるようになるまでの待機時間

Request sent

リクエストの発行にかかった時間

Waiting (TTFB)

リクエストを送信してから最初の1バイトを受信するまでの時間
(TTFBはTime To First Byteの略)

Content Download

コンテンツ自身であるレスポンスデータの受信にかかった時間

PHPやRubyなどのサーバーサイドスクリプトで処理に時間がかかる場合はWaiting (TTFB)が長くなります。


長過ぎるソースコード

過去に数千行にわたって書かれているCSSを見たことがあります。

このコードの修正はひたすら該当箇所をスクロールしては探し出し,修正しては確認をしてまた探すを繰り返す間違い探しだったに違いありません。

開発ではこうした時間を減らしてコードを書くことに専念したいところです。


増え続ける<link> <script src="">

管理しやすくするためにCSSを分割することで読み込むCSSが増えます。

読み込むCSSが増えるということはリクエスト数が増えるということです。

JavaScriptでは外部のJavaScriptを読み込む方法にdocument.write()を使う方法もありますが,記述量も多くブラウザの同時読み込みの恩恵を受けられないのでHTMLに<script src="">を書く方が一般的だと思います。

ただし,どちらもリクエスト数が増えます。

ちなみにCSSから@importで他のCSSを読み込むのもダメです。

@importでもリクエストが増えることは変わりませんし,ブラウザがインポートする側のCSSを読み込み終わるまでインポートされる側のCSSは読み込まれないので無駄な待機時間が生じます。

@importでstyle.cssからいくつかCSSを読み込んだときのリクエストについて御覧ください。

続いてHTMLから<link>で読み込んだ場合ですが同時に読み込めていることがわかります。


開発効率かレスポンスか

一つのファイルにすべて記述するとコードを書いていない時間が長くなり,ファイルを分割するとレスポンスが悪化してしまいます。

このように開発効率とレスポンスは天秤にかかっている状態になります。

どちらも一長一短ですが開発効率を下げるわけにもいかないのでファイルを分割することがほとんどだと思います。


CSSベンダープレフィックスの闇

マークアップはクオリティアップや表現手法などのテクニックのために力や時間を注ぐべきであって,ブラウザごとの対応状況を把握しては対応するというのは馬鹿馬鹿しいことです。

各ブラウザたちもアップデートを重ねるごとに対応が進み今ではほとんどベンダープレフィックス無しに構築することができるようになりました。

しかし,他のバージョンのサポート範囲を広げると未だに必要になります。

そもそも「ブラウザによってプロパティはこう指定する」なんて単純な作業はわざわざ人がする必要なんてありません。

そんなものは自動で付けてやればいいのです。


そもそもJavaScriptがヤバい

誤解を招きそうな書き方をしましたがJavaScriptを悪者にするつもりはありません。

むしろ言語としてはなかなか面白く個人的には好きな言語です。

では何がヤバいかというと他のJavaScriptをインポートするための仕組みがないことや,動的型付け言語であることやプロトタイプベースであるがために適切に設計しないとある程度の規模から開発が難しくなるということです。

例えば,動的型付け言語は実行するまでエラーを出力しないためミスやバグに気づかないことがあり思わぬところで躓いたりします。

オブジェクト指向ではおなじみの名前空間やprivate修飾子もなく,オブジェクトで囲って擬似的な名前空間として扱ったり,privateメンバーにはアンダースコアを付けるといった紳士協定を結んでなんとかしているに過ぎません。

クロージャーを使ったカプセル化もできますがprototypeで定義しなければメモリリークの温床となるのでおすすめできません。

それくらいならまだ諦めてpublicとして扱ったほうがマシです。

しかし,カプセル化ができる方が安全に実装できることは間違いなく,JavaScriptが規模の大きなプロジェクトに向いていないことが分かります。

そんなJavaScriptですが,ちょっとしたDOMの書き換えや動きの追加にとどまらずAjaxとWeb APIによる双方向のデータ通信を利用するなど,かつてとは異なる開発規模になっています。

シングルページアプリケーションの根幹として役割を果たすようになったり,Angular2のようなフレームワークやReactといったライブラリの登場もそれを表しているといって良いでしょう。

※これらの話は度々議論の嵐を呼びますが決して優劣の話をしたいのではなく,議論を再発させたいわけでもないのでうまく汲み取ってください。


HTMLとJavaScriptの依存関係

JavaScriptはHTMLに直接記述したり<script src="">を使って外部から読み込めるわけですが,どこにどんなJavaScriptをどのように書くのかイマイチなところがあります。

例えば,全てのコードを一つのJavaScriptに書いて読み込んだり,ページによって異なるJavaScriptを書いて読み込んだり,ちょっとしたものならHTMLの最下部に<script>で書けたりもします。

ページごとに異なるJavaScriptを書いたりHTMLに直接記述すると,そのコードがHTMLに依存する形になりどのようなJavaScriptが書いてあるか把握することが困難になります。

そうなると理想的なのは一つのJavaScriptに書いていくことですが,前述したとおり長過ぎるソースコードはそれだけで開発効率を悪化させます。

※JavaScriptを分けて最低限のものだけ読み込むことはキャッシュをうまく使ったり不要なコード分の転送量を削減することにもなりますので一つのJavaScriptに書くことが一概に良いとはいえません。


ライブラリの依存関係とバージョンの管理

normalize.cssやjQueryなどのライブラリを使うにはダウンロードしてプロジェクトに配置しなければなりません。

さらにライブラリはライブラリ同士で依存関係を持っている場合もあり,どのライブラリにはどのバージョンのライブラリが必要でといった管理が必要になります。


ちょっとまとめ

長々と書きましたが,つまるところ以下の点が問題になります。


  • CSSやJavaScriptには潜在的な無駄が存在する

  • リクエスト数の増加によってレスポンスが悪化する

  • CSSもJavaScriptもプロジェクトの肥大化に弱く管理がしづらい

  • ソースコードを書くことに集中できない

  • JavaScriptだけでは今日のプロジェクト規模に見合わない

  • HTMLとJavaScriptの依存関係を把握しづらい

  • ライブラリの依存関係とバージョンの管理が大変

これらをクリアすることで今時のフロントエンド開発環境はできあがっていきます。


どう解決していくか


webpackを使う

webpack

webpackはアセットをJavaScriptにバンドルして依存性を解決するビルドツールです。

前述したJavaScriptの問題を一気に解決してしくれるだけでなく,CSSやJavaScriptなどのテキストデータや画像などのメディアを一つのJavaScriptファイルにまとめてくれます。

アセットが一つのファイルになるのでリクエストはバンドルファイルだけになります。

さて,そんなことが技術的に可能なのか不思議に思う方もいると思いますができてしまいます。

CSSは文字列としてJavaScript内で保持しておき,DOM操作によって<style>として展開すればブラウザはCSSを解釈できるようになります。

画像はBase64エンコードすれば文字列として保持しておくことができるので,同じようにHTMLに展開して表示することができます。

webpackはアセットをまとめるだけでなく予め処理をすることもできます。

例えばSassをCSSにコンパイルしたり,JavaScriptを圧縮・難読化したり,画像の画質を落としてファイルサイズを小さくしたりといった具合です。

JavaScriptの圧縮ができるので潜在的な無駄を省くこともできます。

最近は様々なフレームワークでwebpackの機能を含むようになっています。

間違いなく今時のツールです。


Sassを使う

SassはCSSプリプロセッサーと呼ばれるもので独自の記述をして最終的にCSSを出力します。

Sassには次のような機能があります。


  1. 変数を使える

  2. ネームスペースや継承をネストできる

  3. 出力するCSSを一つのファイルにまとめられる

  4. 出力するCSSを圧縮できる


  5. @mixinでスタイルを使いまわせる

  6. 色などの値に算術演算子が使える

  7. 別の単位同士で演算できる

  8. 配列やマップが使える

  9. 条件分岐や繰り返しができる

SassのSCSS記法はCSSと基本構文は同じですのでこれらの機能を覚えるだけで使うことができます。

取り上げた問題を直接解決するためのものではありませんが,コードの記述量を減らしたり管理しやすくする上で非常に役立つのでオススメです。


Autoprefixerを使う

AutoprefixerはCSSにベンダープレフィックスを自動的に付けることができます。

ブラウザの実装状況をまとめたCan I useをもとに必要なベンダープレフィックスを付けてくれます。

これでマークアップに集中できるので質の高いスタイルの構築ができるようになります。


TypeScriptで書く

JavaScriptの代わりにTypeScriptで書きます。

TypeScriptはMicrosoftによって開発された”静的型付けクラスベースオブジェクト指向言語”です。

altJSと呼ばれるもののひとつで,TypeScriptで書いたソースコードはJavaScriptのコードにトランスパイルされ,ブラウザは出力されたJavaScriptを実行することになります。

TypeScriptはトランスパイル前に型チェックをしてくれたりclass構文やジェネリクスが使えたりします。

JavaScriptに似た構文で書きますが,ジェネリクスなどの存在からもはや異なる言語といっても良さそうです。

むしろJavaやC#に似ているので静的型付け言語を書ける人はすんなり覚えることができます。

静的型付け言語を知らない人もこれを気に覚えてみるのも良いでしょう。

静的型付けであることや将来性からTypeScriptを選択しています。

習得するコストがかかることは間違いないので無理して使う必要はありませんし,BabelやCoffeeScriptでコードを書くことに慣れているのであればこれらを選択しても良いです。


組み合わせる

これらはそのままでは独立していてSassのコンパイルやTypeScriptのトランスパイルは個別にコマンドを実行しなければなりません。

しかし,コードを書いてはコマンドを実行するのでは効率が悪いのでwebpackを起点に一度に全部やってもらいます。


おわりに

実はこれらの技術はどれも最近出たばかりのものというわけではなく,存在自体は数年前からあったものです。

バージョンアップと共に勝手も良くなり,その都度これらの技術に関することは取り上げられてきました。

バージョンによって書き方が異なったり新しい情報の選別が難しかったりしたので,何も知らない状態からこれらの技術に触れられるようにまとめようと思いました。

各技術の仕様やバージョンによる違いはその技術について詳しく説明しているものが数多くありますので,そちらを見ていただくことをオススメします。


回答や補足

いくつか目立ったことに関してできるだけ考えを交えながら回答していきたいと思います。


gulpは?gruntは?

gulpやgruntは使いません。

これらはすばらしいタスクランナーであることは違いありませんし,多くの方がその恩恵にあずかっていると思います。

ですが,これらタスクランナーにはプラグイン作者への依存度が高いことやドキュメントがばらばらといった問題から不要だという意見もあります。

私もその意見の持ち主の一人で,代わりにnpm-scriptsを使っています。

詳しいことについては次の記事を読んでみることをオススメします。

さて,gulpやgruntを不要とした理由ですが実はwebpackにもあてはまってしまいます。

しかし,webpackはビルドツールで根本的にはタスクランナーとは異なるものです。

どちらも高機能なため似たような手段の選択肢として取り上げられることがありますが,ビルドツールとして競合するのはgulpやgruntではなくBrowserifyなどがそれにあたります。

タスクランナーの代わりにnpm-scriptsを使いアセットのバンドルにはビルドツールであるwebpackを使っていますが,もし本編で紹介する内容以外に効率化したい手続きがあり,それがnpm-scriptsだけでは逆に複雑になってしまう場合はタスクランナーの出番だと思っています。


規模と用途について

TypeScriptやwebpackの必要性についてプロジェクトの規模によっては不要だという意見がありますが,これは間違いありません。

ツールや言語は飽くまで手段なのでご自身の思う開発規模に見合った手段を選ぶべきだと思います。

TypeScriptのような静的型付け言語は手続きや記述量が多くなります。

しかし,静的型付けされていることでミスやバグが減るだけでなく記述量に関してもかなり楽になっているので最終的なスピード感に関してはさほど変わりは無いようにも思います。

ただ,新しい技術を習得するコストに見合わないと感じたり,プロジェクトに対してTypeScriptという選択が合致していないのであればそれは他の手段を選ぶ十分な理由になります。

そしてWebアプリWebサイトなど用途によってもこれらの技術が必要かについてですが,私自身webpackについては必要だと思っています。

webpackに関してはデータの圧縮やレスポンスの最適化ができるのでWebサイトにおいても大いに役立つと思いますし,他の代替手段についてはあまりないようにも思います。

これまでのCSSスプライトやJavaSciptの圧縮・難読化といった技術だけで十分なレスポンスを確保でき,その手続きが手間にならないのであればこれらの技術は必ずしも要求されるものではありませんが,webpackはこれらの手続きを自動的に行えることにも意味があります。

何れにせよ,必要としているもの必要とされているものを見極め,その時々に最適な手段を選べることが望ましいことは違いありません。