10年ぶりくらいに Web 開発に再デビューしなくてはならなくなった筆者が見た、現代のフロントエンド開発の基本知識についてまとめます。フレームワークを使ったシングルページアプリケーション開発が対象です。若干の不正確には目をつむってズバリ言い切るスタイルで書いていきます。
Node.js
現代のフロントエンド開発には Node.js を使います。フロントエンド開発を強力にサポートするいくつものツールが Node.js で実装されているからです。
Web 開発で言語処理系というと、Ruby on Rails のような Web アプリケーションフレームワークを思い浮かべるかもしれません。もちろん Node.js にもそのようなフレームワークはいくつも存在しますが、フロントエンド開発で使うツールはそれとは全然関係ありません。
これらのツールを使うことによって解決するのは、以下のような要望です :
- JavaScript コードを複数ファイルに分割して書きたい。
- JavaScript コードを最新の仕様で書きたい。
Node.js が JavaScript 言語の処理系なので「クライアント側のブラウザーが実行するはずの JavaScript コードを Node.js が実行したりするのか?」などとつい考えてしまうかもしれませんが、もちろんそんなことはありません。Node.js で実装されたこれらのツールの主な役割はソースコードをソースコードに変換することです。ブラウザーでは直接実行できない、複数ファイルに分割されていたり最新の仕様で書かれていたりするソースコードを、実行可能なソースコードに変換するのです。ここでいう「ソースコード」は JavaScript がメインですが、HTML や CSS を変換することも可能です(そして実際によく行われています)。
先程の2つの要望の前者(ファイル分割)を解決するツールが Browserify または webpack で、後者(最新仕様の言語)を解決してくれるのが Babel や TypeScript です。
Node.js で Web サーバーを書くのは簡単ですから、これらのツールの変換結果をブラウザーで即座に見られるようにする開発用サーバーも簡単に提供できます。また Node.js はローカルファイルも自由に操作できるので、本番環境で動かすために変換結果をまとめてファイルにして出力する機能も提供できます。
Node.js プロジェクトのレイアウト
Node.js について最初に知っておいたほうがいいと思うのは Node.js が JavaScript ファイルを実行するやり方についてです。Node.js で実行する場合必ず1つのファイルから始まります :
$ node foo.js
foo.js から require
を使用して別ファイルにあるコードを利用できます。
var bar = require('./bar.js');
bar.someFunction();
後は芋づる式に複数のファイルをロードすることができます。require('baz')
と書くと node_modules/baz ディレクトリにあるファイルがロードされたりします。詳しい仕様が次のドキュメントに書いてあるのでぜひ読んでほしいです :
- Modules | Node.js Documentation (日本語←ただし古い)
この仕様の内容を理解すると Node.js プロジェクト(つまりフロントエンド開発用プロジェクト)のディレクトリ・ファイルレイアウトが理解でき、プロジェクトの各ファイルがどのような役割を果たしているかが明確になります。
npm
npm は Node.js の標準パッケージマネージャーです。フロントエンド開発においては次の2つの機能を利用します :
- Node.js のパッケージをダウンロードする。
- 定型 node コマンドを実行する。
Babal や webpack といったツールは Node.js のパッケージとして配布されています。これらは更に他の Node.js パッケージにも依存しています。npm install
を実行すると package.json に書かれたパッケージを node_modules にダウンロードしてくれます。その際、依存するパッケージも同時にダウンロードしてくれます。
さらに、ちょっと驚くことに Web フレームワークのコードも(本来は Node.js で実行されるものでないにも関わらず)Node.js のパッケージとして配布されています。それらも npm でダウンロードされ、Babal や webpack で変換されてブラウザーで実行できる形で提供されるのです。
npm-scripts
npm がフロントエンド開発に提供する2番目の機能は node
コマンドの定型的な実行です。この定型コマンドラインも package.json に記述します1。例えば以下のような例が典型的です :
-
npm start
とすると、開発サーバーを起動する。 -
npm test
とすると、ユニットテストを実行する。 -
npm run build
とすると、Babel や webpack を実行してソースコードを変換・結合し、ブラウザーが直接読める形のファイルとして出力する。出力されたファイルは別の場所にデプロイして Apache や Nginx でホストすることもできる。
このような定形作業をタスクと呼びます。npm run xxx
で実行できる任意の数のタスクを package.json に記述できます2。少し前までは専業のタスクランナーとして Grunt や Gulp などがもてはやされましたが、これらについては忘れて npm-scripts を使えばよろしいようです3。
package.json の例を示します :
{
"name": "my-app",
"version": "0.1.0",
"dependencies": {
"react": "^15.6.1",
"react-dom": "^15.6.1"
},
"devDependencies": {
"react-scripts": "1.0.7"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject"
}
}
JavaScript
10年前、JavaScript の仕様は ECMAScript 3 で長期間(安値)安定していました。2009年に JSON のサポートや strict モード、getter/setter そしてFunction.prototype.bind() をはじめとする若干の新メソッドを提供する小規模な更新 ECMAScript 5 (ES5) が策定されました(ES4 は合意がならず策定無し)。
そして 2015 年、本格的な大規模更新が施された ECMAScript 6 (ES6) が策定されました。2015 年からは毎年バージョンアップされることになることが決まっているため、年号をつけて ES2015 とも呼ばれています。
ES2015 の機能は広範囲に渡っているので一言では書けませんが、特筆すべきものはクラス class { ... }
のサポートとアローファンクション () => ...
でしょう。新機能の全体像については以下の記事がわかりやすいと思います(英語) :
JavaScript サポート状況は以下が詳しいです :
ブラウザー別のサポート状況でいうと、いわゆるモダンブラウザーは ES2015 を実装していますが、モジュール関連(複数 JavaScript ファイル)は実装対象から除かれています。これはブラウザーはローカルファイルからモジュールを読むわけにはいかず、どのようにモジュールをロードするのかについての仕様がまだ決まっていないからです。
トランスパイラーとポリフィル
最新の JavaScript 仕様でコーディングしたい場合で、古いブラウザにも対応したい場合にはトランスパイラーとポリフィルを使います。
ポリフィルはブラウザー側で読み込むと関数やメソッドを追加・変更して最新仕様に合うようにしてくれるものです。一方トランスパイラーはコードをまるごと変換するツールで、主に最新の文法要素を古いブラウザでも実行できるコードに変換してくれます。トランスパイラーで変換しても、ブラウザー側での実行時にはポリフィルが必要になる場合がある(当該関数・メソッドを利用しないなら不要)ので注意しなければなりません。
現時点で ES2015, ES2016, ... を利用するなら、Babel を使います。Babel は本来トランスパイラーですが、ポリフィルもサポートされています(→ Polyfill · Babel)。
AltJS
JavaScript 言語本体がなかなか進化しなかったので、独自の拡張を JavaScript に施した AltJS というのが流行していました(CoffeeScript が代表でしょうか)。JavaScript が活発に進化している現在では、フロントエンド開発の初心者が考慮するに値する AltJS は TypeScript だけだと言えるでしょう。TypeScript は JavaScript とは違って強い型付けを持っていますので、型が好きな人は TypeScript を選ぶ理由があります。
バンドラー
現時点ではブラウザーはもちろん、Babel, TypeScript などのトランスパイラーもモジュール(複数ファイル)に対応していません。この解決には別の Node.js パッケージを使うようになっていて、それらはバンドラーと呼ばれます。
Browserify は JavaScript コードの require
を読んで必要な JavaScript ファイルを結合してくれる機能だけをもつツールです。非常にシンプル。
webpack は JavaScript の他に CSS や HTML, 画像ファイルといったいろいろなアセットを結合してくれる多機能ツールです。
HTML5 と CSS
HTML5 については、開発者観点から見て1点だけ知っておいたほうがいいことがあります。それはカスタムデータ属性 data-*
についてです。
開発をしていると HTML の要素(ノード)に独自の属性をつけたい場合がよくあります。この要望を仕様化したのがデータ属性です。data-*
の *
の部分に好きな名前をつけて属性とすることができます。この属性は DOM でアクセスできることが保証され、CSS も適切に適用されることが保証されています。
これまでも勝手な属性を HTML に付けることは可能でしたが、それはブラウザーの実装がたまたまそうなっていたというだけで、HTML5 で正式に仕様化されたということです。
CSS については Sass と PostCSS について調べてみたほうがよさそうです(手抜き)。
Wrap up
以上のことを理解しておけば、React なり AngularJS なりのチュートリアルを試すときに戸惑ったり面食らったりせずにすむと思います。
References and further reading
- Create React App — User Guide
- ゼロから始めるJavaScript生活
- レトロエンジニアのための近代Webフロントエンド事情
- Javaな人向けクライアントサイドJavaScript開発入門
- Googleトレンドに見る2016年人気のCSS・JavaScript・タスクランナー
- JavaScriptのモジュールシステムの歴史と現状
- JavaScriptはなぜトレンドが毎年変わると思われていたのか
- 春からはじめるモダンJavaScript / ES2015
- 2015年までにWebのフロントエンドが辿ってきた道
- フロントの未来の話
- 【翻訳】「Front-end Developer Handbook 2017」 2016年のまとめと2017年の予測
- JavaScriptフレームワーク選定の議論