フロントエンド開発の概要について
ここでは、フロントエンド開発の概要について説明していきます。
*元記事はこちらです。(英語)
この記事でカバーしているものについて
- Single-page Apps (SPAs)
- New-age JavaScript
- User Interface
- State Management
- Coding with Style
- Testing
- Linting JavaScript
- Linting CSS
- Types
- Build System
- Package Management
- Continuous Integration
- Hosting
- Deployment
Single-page Apps (SPAs)
かつてはサーバーサイドレンダリングという、別のURLを開くごとにページをリフレッシュしてサーバーから新たなHTMLページを送る手法が主流でしたが、最近のSPAsではクライアントサイドレンダリングというものが主流になっています。
まずブラウザがサーバーから、最初のページとそれに伴ってアプリ全体に必要なスクリプトやスタイルシートをロードします。
そしてユーザーが別のページに遷移する場合に、ページはリフレッシュされません。
ページのURLが、HTML5 History APIを通じてアップデートされます。
新しいページに必要なデータは、サーバーへのAJAXリクエストを通してブラウザにより、大抵JSONフォーマットで引き出されます。
それからSPAはJavascriptを通してデータを用いて動的にページをアップデートしますが、それは既に最初のページでダウンロード済みです。
このモデルは、ネイティブモデルアプリの動作によく似ています。
この方法のメリットとしては、以下のようなものがあります。
- アプリのレスポンスが良くなり、ユーザーが全ページのリフレッシュによるページナビゲーション間の閃光を見なくても済む。
- わずかなHTTPリクエストがサーバーに送られるだけで済み、それぞれのページのロードに同じアセットをもう一度ダウンロードしなくても済むようになる。
- クライアントとサーバー間の関係に、明確な分類点ができる。どういうことかというと、サーバー側のコードを変更せずとも異なるプラットフォーム(モバイル、チャットボット、スマートウォッチなど)に対して容易に新たなクライアントを構築できる。APIの約定が破綻していない限りは、サーバーとクライアントのテクノロジースタックをそれぞれ独立した状態で変更できる。
逆に、デメリットとしては以下のようなものがあります。
- 多くのページに必要なフレームワーク、コード、アセットのローディングによっては、初期ページのロードが重くなる。
- 1つのエントリーリクエストに対する全てのリクエストを走らせるのと、クライアントサイドにそこから必要なものを持って行かせるために、サーバー側でもう1つステップが必要となる。
- SPAsは内容を表現するためにJavascriptを頼っているが、全ての検索エンジンがクローリングの際にJavascriptを実行しているわけではなく、その場合は空っぽの中身を見る可能性がある。その場合は、アプリのSEOにおいて問題が起きてしまう。
大きな開発チームになるほど、クライアントとサーバー間の明確な分類がある方が良く、その方がクライアント側とサーバー側のコードを別々に開発したりリリースできるからです。
参考リンク
- Single Page App: advantages and disadvantages
- The (R)Evolution of Web Development
- Here’s Why Client Side Rendering Won
New-age JavaScript
Javascriptは非常に多方面で役立つ言語で、Webサーバー、ネイティブモバイルアプリ、デスクトップアプリを構築するのに使うことができます。
ここ最近まで、全てのブラウザがES2015(ES6)の全ての仕様を実行できているわけではありませんでした。
Babelのようなツールのおかげで、開発者が彼らのアプリ内でES2015を書けるようになり、BabelがそれをES5にわかるようにしてブラウザで矛盾がないようにしました。
ES5とES2015の両方に慣れておくのは、とても大切なことです。
ES2015はまだ比較的新しく、多くのオープンソースコードやNode.jsアプリはまだES5で書かれています。
ブラウザコンソールでデバッグをしていると、ES2015のシンタックスが使えないかもしれません。
一方でこの文章内でも後に紹介する多くの新しめのライブラリは、ES2015で書かれています。
ES5とES2015の復習及び探求については、ある程度時間をかけてください。
ES2015にはやや重めの機能があって、“Arrows and Lexical This”、“Classes”, “Template Strings”、“Destructuring”、“Default/Rest/Spread operators”と“Importing and Exporting modules”がそれに当たります。
参考リンク
- Learn ES5 on Codecademy
- Learn ES2015 on Babel
- ES6 Katas
- Free Code Camp
- You Don’t Know JS (Advanced content, optional for beginners)
User Interface — React
もし直近においてjavascriptのプロジェクトがフロントエンドエコシステムを採用していたら、それはReactでしょう。
ReactはFacebookの人たちによって構築された、オープンソースのライブラリです。
Reactにおいて、開発者はWebインターフェースのためのコンポーネントを作成し、それらを組み合わせます。
Reactは、多くの革新的なアイデアをもたらすのと同時に、最も効率のいい学習方法について開発者たちに考えさせてくれました。
昔はHTML、Javascript、CSSを別々に学ぶのがいいと言われていましたが、Reactは全くの逆でHTMLとCSSをJavascript内で書くことを推奨しています。
Reactの特徴としては、以下のことが挙げられます。
- 平叙的 — jQueryにおいては、アプリを次の状態にするのに開発者がDOMの一連の操作の流れについていく必要があったが、Reactにおいてはコンポーネント内の状態を変更するだけで、viewがその状態によって自分自身をアップデートしてくれる。また、render()メソッド内のmarkupを見ることで、コンポーネントがどのようになるか決めるのも簡単にできる。
- 機能的 —viewというのは、propsとstateの純粋な関数である。大抵のケースにおいて、Reactのコンポーネントはprops(外部パラメータ)とstate(内部データ)によって定義されている。同じpropsとstateにおいては、同じviewが生成される。 純粋な関数はテストが簡単で、関数のコンポーネントにおいても同様である。Reactにおけるテストは、コンポーネントのインターフェースがきちんと定義されているため簡単であり、異なるpropsとstateを供給することによってコンポーネントのテストが可能で、表示される出力の比較もできる。
- メンテナンスが容易 — コンポーネントベースの流儀に沿ってviewを書くことで、その再利用性が高まる。コンポーネントのpropTypesを定義すると、Reactのコードをそのコンポーネントを使うには何が必要か明らかにしてくれる読み取り機としてセルフドキュメント化してくれる。また、viewとロジックはコンポーネントの中に一通り揃っており、他のコンポーネントに影響を与えたりあるいは受けるべきではない。同じpropsがコンポーネントに供給される限りは、大規模のリファクタリングにおいてコンポーネントを移し替えるのは簡単である。
- ハイパフォーマンス — Reactは、メモリ内においてDOMの軽めの仮想表現をすることができる。Reactにおいては、実際のDOM自身ではなくてDOMのインメモリ表現のリレンダリングを参照している。コンポーネントの基礎となるデータに変更があった場合、新たな仮想表現が生成され、前のものと比較される。その違いが、実際のブラウザのDOMにパッチされる。
- 学習しやすい — ReactのAPIにおいて学ぶべきものの数はそれほど多くなく、頻繁に変更されることもあまりない。
- 開発者の経験 — Reactによる開発経験をより良くするためのツールは多くあり、React Developer Toolsはブラウザの拡張でそれによりコンポーネントやviewを精査したり、そのpropsやstateをいじったりすることができる。Hot reloadingでは、ブラウザをリフレッシュすることなくブラウザ内のコードを変更したりできる。ライブラリのアップデートがされた場合、Facebookはcodemod scriptsというコードを新たなAPIにマイグレートさせるものを供給してくれている。
Reactが一体何でどんなことをしてくれるのかについては、チュートリアルに従って◯✖️ゲームを作ってみることをオススメします。
より深く理解するには、React RouterによるReact Fundamentalsをチェックしてみてください。
FacebookによるCreate React Appは、新たなReactのプロジェクトを始めるのに非常にオススメです。
Reactはフレームワークではなくライブラリであり、view以下のレイヤー(アプリの状態)については扱いません。
これについては、また後ほど出てきます。
参考リンク
- React Official Tutorial
- React Fundamentals
- Simple React Development in 2017
- Presentational and Container Components
代替手段
State Management — Flux/Redux
アプリの規模が大きくなってくるとその構造が段々汚くなり、共通のデータをシェアしたりする必要が出てきますが、Reactにはそれに適した方法がありません。
Reactは単なるviewのlayerであり、モデルやコントローラーのようにアプリの他のレイヤーをどのように構築するか命令するものではないのです。
それを解決するためにFacebookが開発したのがFluxであり、一方向性のデータフローを用いることによって、Reactの構成可能なviewのコンポーネントをアプリのアーキテクチャーが補うことができます。
Fluxについて詳しく知るには、ここを見てください。
要約すると、Fluxの型には以下のような特徴があります。
- 一方向性のデータフロー — アップデートの軌跡をより簡単に追えるように、そのアプリに予見できるようにする。
- 関係性の分割 — Fluxアーキテクチャーのそれぞれのパートには、明確な責任があって、尚且つしっかり分離されている。
- 平叙的なプログラミングと相性がいい — ストア側からviewに対して、状態間でどのように遷移するかを明示することなくアップデートを送ることができる。
Flux自体はフレームワークではないものの、開発者はそのパターンのインプレメンテーションに追いついていく必要がありました。
そのため、新たに出てきたのがReduxです。
ReduxはCommand patternとElm architectureというFluxからの概念を合体させ、事実上最近の国営のライブラリ開発者はReactと一緒に使っています。
そのコアとなる概念が、以下になります。
- アプリの状態が、1つの平易な古いJavaScriptのオブジェクト(POJO)によって描かれている。
- 状態を修正するために、アクション(POJO)を手早く済ませる。
- Reducerは純粋な関数で、新たな状態を生み出すのに現在の状態とアクションを必要とする。
概念自体はシンプルですが、アプリに対して以下のことを可能にするのでとても強力なものと言えます。
- その状態をサーバー上で表現し、クライアント上で立ち上げる。
- トレース、ログ、バックトラックがアプリ全体で変化する。
- インプレメントを元通りにしたりやり直すのが機能的に簡単になる。
Reduxの作者であるDan Abramovは、Reduxの基礎的な部分と発展的な部分のわかりやすい学習用ビデオチュートリアルを作成してくれています。
Reduxの学習に際しては、これらが非常に役立つはずです。
ビューと状態の結合
ReduxがReactと共に使われる必要の絶対性がない中で、お互いを上手く使い合わせることが強く推奨されています。
ReactとReduxには、共通する考えや特徴があるからです。
- 機能的な構成パラダイム — Reactはビュー(純粋な関数)を構成する一方で、Reduxは純粋なリデューサー(純粋な関数)を構成する。出力は、入力群と同じ形だと予見できる。
- 推論するのが簡単 — ReactとReduxは、デバッグをシンプルにしてくれます。データフローが一方向性であるため、データフロー(サーバーのレスポンスやユーザーインプット)の追跡が簡単になり、どのレイヤーで問題が起きているのか決めるのが楽になる。
- レイヤーの構造 — アプリのそれぞれのレイヤーにおいて、Fluxのアーキテクチャーは純粋な関数で明確な責任を持っている。それにより比較的、純粋な関数に対するテストを書くのが簡単になる。
- 開発者の経験 — アプリの開発中にデバッグと調査をするためのツール(例えばRedux DevTools)を作成するには、多くの努力が必要となる。
アプリでリモートのAPIリクエストを作成するような非同期の呼び出しを扱わなければならない場合、redux-thunkとredux-sagaが解決するのに役立つでしょう。
ただこれは理解するのに時間がかかるので、必要になったらトライしてみることをオススメします。
react-reduxがReduxに対するReactの公式のものとなり、学習も簡単です。
参考リンク
- Flux Homepage
- Redux Homepage
- Egghead Course — Getting Started with Redux
- Egghead Course — Build React Apps with Idiomatic Redux
- React Redux Links
- You Might Not Need Redux
代替手段
Coding with Style — CSS Modules
CSSは、HTML要素をどのように見えるか表現するためのルールです。
いいCSSを書けるようになるまではかなりの時間や経験が必要で、とても大変です。
それゆえ、経験豊富なフロントエンド開発者たちが、SMACSS、BEM、SUIT CSSなどを使って複雑なプロジェクトに対してきちんと整備されたCSSを書いていけるようになるための、しっかりした方法論を作成してくれています。
かつてはJSの中でCSSを書くという共同で一致したアプローチはありませんでしたが、今ではCSS Modulesというものに頼ることができます。
CSS Modulesは、CSSにおいて既存のCSSを改良してグローバル名前空間の問題を取り除こうとします。
これにより、デフォルトでローカルにあるスタイルをコンポーネントに要約することが可能になります。
CSS Modulesによって、巨大な開発チームでもアプリの一部分を上書きしたり矛盾を起こしたりすることなく、再利用できるCSSを書くことが可能になります。
ただCSS Modulesはブラウザが認識できる通常のグローバル名前空間的なCSSにもコンパイルされているので、通常のCSSがどのように動作するか学習するのは大切です。
もしCSSに対して全くのビギナーという場合は、HTML & CSS courseにトライしてみるといいでしょう。
次にSass preprocessorというCSSを拡張したものについて書いてあるものを読んで、最後にCSS modulesについて学習しましょう。
参考リンク
- Learn HTML & CSS course on Codecademy
- Intro to HTML/CSS on Khan Academy
- SMACSS
- BEM
- SUIT CSS
- CSS Modules Specification
- Sass Homepage
- Answers to Front End Job Interview Questions — HTML ⭐
- Answers to Front End Job Interview Questions — CSS ⭐
代替手段
Maintainability
コードは書くよりも読まれることの方が多く、実際にチームが巨大化するほど多くのエンジニアがいくつものプロジェクトに跨って仕事をしています。
コードの読みやすさや保全性に安定性が、強く求められます。
Testing — Jest + Enzyme
Jestは、テストのプロセスで余計な煩わしさを感じなくても済むようにFacebookが開発したテスト用ライブラリです。
JestはReactコンポーネントとReduxの状態から生成された出力を保存でき、それをシリアライズ化されたファイルとして保存することも可能で、そのため自分自身で手作業によって期待される出力に追いついていく必要はありません。
また、Jestは元々モッキングやアサーションにテストカバレッジが備わっています。
1つのライブラリで、それら全てをカバーすることが可能なのです。
EnzymeというAirbnbが開発したものを、Reactコンポーネントをテストする際に使うことが推奨されています。
JestとEnzymeを使えば、フロントエンドのテストを楽しく簡単に書くことができます。
JestとEnzymeのドキュメントは非常に簡潔に書かれているので、その2つについて学習したければそれを読むだけで十分です。
参考リンク
- Jest Homepage
- Testing React Applications with Jest
- Enzyme Homepage
- Enzyme: JavaScript Testing utilities for React
代替手段
Linting JavaScript — ESLint
Linterとは、静的にコードを分析して問題を発見するためのツールであり、同時に潜在的にバグや実行中のエラーを防ぐ、といったことを実施するコーディングスタイルです。
ESLintは、JavaScriptコードのリンティングにおいて高い拡張性とカスタマイズ性を誇るツールです。
チームはそれぞれのカスタムルールを設定するために、リントのルールを作ることができます。
Grabにおいては、Airbnbのeslint-config-airbnbのプリセットが使われ、それはAirbnb JavaScript style guideにおいて既に共通の優れたコーディングスタイルとして形成されています。
もし新たなルールを作ろうという場合でなければ、ESLintについて特に学習することはありません。
エラーが表面化した場合に、Googleでオススメのスタイルを探せばいいということを認識しておいてください。
参考リンク
代替手段
Linting CSS — stylelint
CSSの静的解析ツールを使うことで、CSSコードのクオリティとコーディングスタイルを維持することができます。
CSSのリンティングにおいては、stylelintというツールを使います。
ESLintのように、stylelintは基準寸法的なやり方でデザインされ、開発者はそのルールのオンオフを切り替えたり、そのためのカスタムプラグインを書くこともできます。
CSSに加え、stylelintはSCSSをパースすることが可能で、Lessに対する実験的な土台も持ちます。
ESLintについて学んだことがあれば、stylelintも似たようなものなので簡単だと思います。
最近ではstylelintは、FacebookやGithubにWordpressなどの大きな企業で使われています。
実はstylelintには1つデメリットがあって、自動で修正してくれる機能は不完全で、限られた数のルールの中でしか修正できません。
ただこれは、時が経てば解決されるでしょう。
参考リンク
代替手段
Types — Flow
Typesはコード内のバグやエラーを素早く見つけるのに役立つのと、その可読性も高めてくれます。
プロジェクトチームに新たなメンバーが入ってきた場合にも、それぞれのオブジェクトがどんな変数を保持していて、それぞれの関数でどんなパラメータが返ってくるのか明らかにするのも簡単になります。
Typesをコードに加えることで多少最初は面倒になることもあるかと思いますが、複雑なプロジェクトになってくると何度も変更があってコードのメンテナビリティが大変になり、そういった意味ではtypesを入れておくことのメリットは大きいと思います。
実際にしばらく触っていなかったコードの修正が必要になったとき、typesのおかげで簡単にコードを理解できて修正した部分にも自信が持てました。
JavaScriptに対する静的なtypesにおいて、最も大きな2つのコンテンダーはFlow (by Facebook)とTypeScript (by Microsoft)です。
最近では、前者の方が使われることが多いと思います。
前者の方が学習曲線が緩やかで、既存のコードベースをFlowにマイグレートするのにも少ない労力で済みます。
Facebookによって開発されたので、FlowはReactのエコシステムとより良い相互作用を起こします。
Flowの作者の1人であるJames Kyleは、FlowとTypeScriptの比較記事を書いています。
参考リンク
代替手段
Build System — webpack
webpackはフロントエンドプロジェクトをコンパイルするモジュールバンドラーであり、その独立性を最後のバンドルに入れてユーザーに届けるためのものです。
大抵の場合、プロジェクトにおいてはwebpackの形が既に設定されていて、開発者がそれを変更することは滅多にありません。
webpackの学習には、webpack walkthroughが最適でしょう。
まずはこれに従ってやってみて、カスタマイズが必要になった場合にドキュメントを参照するのがいいと思います。
参考リンク
代替手段
Package Management — Yarn
node_modulesディレクトリの中を覗き見ると、大量のディレクトリがその中にあることに恐怖を覚えるかもしれません。
それぞれのbabelプラグインやlodash関数は、それ自身のパッケージです。
いくつものプロジェクトがあると、それぞれのプロジェクトでこれらのパッケージに重複が出てきてますが、大体は同じようなものになると思います。
新しいプロジェクトでnpm installを実行する度に、既に別のプロジェクトで存在しているにも関わらず、こういったパッケージが繰り返しダウンロードされることになってしまいます。
npm installを使った非決定的なパッケージのインストールには他にも問題があって、CIサーバーが全ての依存性をインストールする場合に互換性の破壊を含む細かいアップデートもちょいちょい存在するため、CIのビルドが失敗することがあります。
これはライブラリの作者がsemverに敬意を払い、エンジニアがAPIの約束事をきっちり守っていれば起こらないことでしょう。
これを解決するために、Yarnというものがあります。
非決定的なパッケージのインストールにおける問題は、yarn.lockというファイルを通して扱い、これが全てのマシンに跨って全てのインストール結果がnode_modulesにおいて全て正確に同じファイル構造になることを保証してくれます。
Yarnはマシン内のグローバルcacheディレクトリを利用しており、パッケージをもう一度ダウンロードしなくても済む前にダウンロードしてくれます。
また、オフラインでの依存性のインストールも可能となります。
Yarnにおいて最も頻繁に使われるコマンドは、ここでチェックできます。
Yarnの他の大抵のコマンドは、npmのものと似ており、代わりにnpmバージョンを使ってもいいでしょう。
お気に入りのコマンドはyarn upgrade-interactiveであり、これによって依存性をアップデートでき、モダンJavaScriptのプロジェクトが多くの依存性を必要としている場合には特に使えるものだと思います。
npm@5.0.0が2017年5月にリリースされましたが、Yarnが解決できるであろう多くの問題があるようなのでチェックしておきましょう。
参考リンク
代替手段
Continuous Integration
我々は、継続的インテグレーションパイプラインのためにTravis CIというものを使います。
TravisはGitHub上で非常にポピュラーなCIであり、そのbuild matrix的な特徴は多くのプロジェクトを含むリポジトリにとっては有用なものです。
以下のようなことをするために、Travisを設定します。
- プロジェクトに対してリンティングを実行する。
- プロジェクトに対してユニットテストを実施する。
- もしテストが成功した場合は
- Jestによって生成されたテスト範囲をCodecovにアップロードする。
- buildディレクトリでwebpackと共にプロダクションバンドルを生成。
- <hash>.tarとしてtar buildディレクトリを、S3 bucketという他の全てのtarがビルドされている場所にアップロードする。
- Slackに対して通知を送り、Travisのビルド結果を報告する。
参考リンク
代替手段
Hosting — Amazon S3
慣例では、WebサーバーはWebページに対するリクエストを受け取ってサーバー上のコンテンツを引き出し、リクエストに対して動的なHTMLページを返却します。
以前に述べたようにこれがサーバーサイドレンダリングというもので、最近のWebアプリケーションではこの方法は使わず、Webサーバーは静的な資産ファイルを遅れば十分になっています。
NginxとApacheはオプションを可能にし、物事を始めたり実行するのに多くの形状は必要ありません。
注意すべきは、Webサーバーは1つのエントリーポイントに向けて全てのリクエストを設定しなければならないだろうということで、クライアントサイドのルーティンで引き継ぎます。
フロントエンドルーティンに対する流れは、以下のようになります。
- Webサーバーが、特定のルートに対するHTTPリクエストを受け取る。(例えば/users/john)
- サーバーが受け取ったルートに関わらず、静的な資産ディレクトリからindex.htmlを抜き出す。
- index.htmlは、クライアントサイドルーティンを扱うJavaScriptのフレームワーク/ライブラリの負荷を上昇させるスクリプトを含む。
- クライアントサイドルーティンライブラリが現在のルートを読み、そのルートについてMVG(あるいは同等、関連のある)フレームワークに伝える。
- MVC JavaScriptフレームワークがルートに基づく望ましいビューを引き出し、もし必要であればAPIからデータを呼び出す。例えばUsersControllerの負荷を上げることについて、ユーザーネームJohnというデータをJSONで抜き出し、ビューと結合してページ上で描写する。
静的コンテンツの供給にぴったりな練習として、それをCDN上でキャッチングしたりプッティングしたりするというのがあります。
Amazon Simple Storage Service (S3)をよく使いますが、これを使うと特別なルーティンの形を伴う我々自身のWebサーバーを持つ必要が無くなります。
S3上でよく出てくるWebアプリが、Hubになります。
参考リンク
代替手段
Deployment
ユーザーにプロダクトを届けるための最後のステップが、デプロイメントです。
我々はAnsible Towerという、ビルドしたものを簡単にデプロイすることができる強力な自動操作ソフトウェアを使います。
我々の全てのコミット、成功したビルドは、セントラルS3バケットにアップロードされます。
リリースのためにはsemverとリリースのために自動的に生成されたリリースノートのコマンドに従い、リリースするときには最新のコミットをつけるためのコマンドを実行してコードを対象の環境にプッシュしてやります。
Travisがそのタグづけされたコミット上でCIステップを実行し、tarファイル(1.0.1.tarのようなもの)と共に、ビルドのためにS3バケットにそのバージョンをアップロードするでしょう。
タワー上ではシンプルに.tarの名前を指定する必要があり、ホスティングバケットにデプロイしたらタワーは以下のことを実行します。
- 望ましい.tarファイルをビルドS3バケットからダウンロードする。
- 指定された環境のために、コンテンツを引き出してコンフィギュレーションファイルの中で交換する。
- ホスティングバケットにコンテンツをアップロードする。
- Slackに通知を送り、デプロイメントの成功を知らせる。
全てのプロセスは通常30秒以内に終わり、タワーを使うことでデプロイメントもロールバックも簡単にできます。
もしデプロイメントの失敗に気づいたら、前の状態のタグを探してそれをデプロイすればいいだけです。