サーバーサイド経験者に贈るフロントエンド技術の学び方

この記事は [クラウドワークス Advent Calendar 2018](https://qiita.com/advent-calendar/2018/crowdworks) の4日目の記事です。

## はじめに

クラウドワークスのフロントエンドチームでエンジニアをしています @eighty8 です。 
2018年9月に入社しました。
長らくソシャゲのサーバーサイドエンジニアをやってまして、本格的にフロントエンド開発に携わるのはクラウドワークスに来てからになります。

フロントエンドエンジニア1年生になってみて最初に感じたことは、 
**「周辺技術が多過ぎてどっから手をつけていいのかわからんちん。。。」**
です。 

[Frontend Roadmap](https://github.com/kamranahmedse/developer-roadmap#-frontend-roadmap) というのを見みてもわかる通り、フロントエンドもこのボリューム ∑(ﾟωﾟﾉ)ノ
今まで断片的に情報は掻い摘んできてたんですが、それじゃダメだ・・・と痛感しました。
まあ、これらすべてに精通している必要はないと思いますが、HTML書けます、CSS書けます、ついでにJavaScript（古来の）も！ 
だけではもう通用しなくなってきてる辛さ。。。

そこで、取っ掛かりが見つけられない初学者の人に、これからどのようにフロントエンドの技術を学んでいけばいいのか、ひとつ道筋を示してみたいと思います。

なお、この記事では、フロントエンドの中でもJavaScriptの話題を中心に書いていきます。

## オススメ5選

まずは、細かいことは抜きにして、最低限以下の技術を学んでみることをオススメします。

1. Node.js
2. npm
    1. yarn
3. ES6（ECMAScript2015）
4. Module
5. Webpack

### なぜこのチョイスか？

- Node.js/npm はフロントエンドの根幹にある技術で、これなしに今のフロントエンド開発は成り立たない
- ES6は 新しいJavaScriptの中核となる仕様で、すぐに廃れることはない
- Moduleは JavaScriptの保守性、再利用性を最大限に高められる機能
- Webpackは 万能なかつハイブリッドなモジュールバンドラーで、フロントエンド開発の規模が大きくなるほど威力を発揮する

といった理由があるからです。

ここに挙げた技術をひとまず習得できれば、今のフロントエンド開発にビビらず立ち向かうことができると思います。
また、次にくる新しい波に乗っていくことができるはず！

### ちなみに、オススメから外したもの

- CSSプリプロセッサ, CSSポストプロセッサー, CSSフレームワーク
  - SassやLess, PostCSS
  - BootstrapやBulma

正直、フロントエンド開発では重要な立ち位置の技術ではありますが、ひとまずJavaScriptでまとめたっかたので外しました。

- タスクランナー
  - GulpやRollup, npm-script

Web環境の構築を自動化してくれる便利ツールではあるものの、これがなければ環境構築ができないわけではないので外しました。

- テストツール
  - JestやMocha, Jasmin

タスクランナー同様、これがなければフロントエンド開発ができないわけではないので外しました。

- JavaScriptフレームワーク
  - ReactやVue, Angular

フレームワークを使う上でも5選に挙げた技術はベースになっているので、それを習得してからでも遅くはないので外しました。

## 1. Node.js

Node.jsはサーバーサイドJavaScriptの実行環境として登場しましたが、今はフロントエンド開発の最も根幹にある技術でもあります。
また、フロントエンドで使われる各種ツールのほとんどはNode.jsで書かれており、それらを駆使して開発を行っていく上では、避けて通れない技術になります。

ところで 
**「Node.jsって環境なの？ それとも言語なの？」**
という疑問にぶち当たったことありませんか？（私はぶち当たりました）

これはある意味どちらも正解です。

Node.js自体はJavaScript開発で使うツールをまとめたもの（JavaでいうところのJDK）で、その中には、インタープリターや後述するnpm（ライブラリ群を管理するツール）が含まれています。

**Node.js配下を展開**

```
$ tree /usr/local/Cellar/node/10.10.0 -L 3
/usr/local/Cellar/node/10.10.0
├── AUTHORS
├── CHANGELOG.md
├── INSTALL_RECEIPT.json
├── LICENSE
├── README.md
├── bin
│   └── node
├── etc
│   └── bash_completion.d
│       └── npm
├── include
│   └── node
│       ├── common.gypi
│       ├── config.gypi
|   〜 省略 〜
│       ├── v8-util.h
│       ├── v8-value-serializer-version.h
│       ├── v8-version-string.h
│       ├── v8-version.h
│       ├── v8.h
│       ├── v8config.h
│       ├── zconf.h
│       └── zlib.h
├── lib
│   └── dtrace
│       └── node.d
├── libexec
│   ├── bin
│   │   ├── npm -> ../lib/node_modules/npm/bin/npm-cli.js
│   │   └── npx -> ../lib/node_modules/npm/bin/npx-cli.js
│   └── lib
│       └── node_modules
└── share
    ├── doc
    │   └── node
    ├── man
    │   └── man1
    └── systemtap
        └── tapset
```

そして、Node.jsはスクリプト言語であって、インタープリターで実行できます。

**Node.jsの構文**

``` hello_world.js
const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World\n');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});
```

**インタープリターで実行**

```
$ node hello_world.js
Server running at http://127.0.0.1:3000/
```

よって広義な意味では環境であり、狭義の意味では言語です。

なぜ混乱するか。
それは、実行環境の名前に拡張子を付けたから？


## 2. npm

npmは、Node.jsで書かれた開発ツール（パッケージ）を管理するためのツールで、Node.jsをインストールすると一緒にくっついてきます。
npmコマンドを介して、パッケージをインストールしたり、アップデートしたり、自作のパッケージを[レジストリ](https://www.npmjs.com/)に公開したりできます。
Rubyの経験者であれば、npmはBundler、パッケージはGem、package.jsonはGemfile、という感じで置き換えてもらえるとわかりやすいかも。

### npmコマンドでできること

```
$ npm -h

Usage: npm <command>

where <command> is one of:
    access, adduser, bin, bugs, c, cache, completion, config,
    ddp, dedupe, deprecate, dist-tag, docs, doctor, edit,
    explore, get, help, help-search, i, init, install,
    install-test, it, link, list, ln, login, logout, ls,
    outdated, owner, pack, ping, prefix, profile, prune,
    publish, rb, rebuild, repo, restart, root, run, run-script,
    s, se, search, set, shrinkwrap, star, stars, start, stop, t,
    team, test, token, tst, un, uninstall, unpublish, unstar,
    up, update, v, version, view, whoami
```

けっこう多いですが、使うものは意外と限られいてる。

### 開発でよく使うであろうコマンド

・パッケージのインストール（グローバルに）

```
$ npm install -g [パッケージ名]
```

・パッケージのインストール（ローカルに）

```
$ npm install [パッケージ名]
```

・パッケージのインストール（公開用オプション付き）

```
$ npm install --save [パッケージ名]
```
※ 後述するpackage.jsonのdependenciesに依存関係が追記される

・パッケージのインストール（開発用オプション付き）

```
$ npm install --save-dev [パッケージ名]
```
※ 後述するpackage.jsonのdevDependenciesに依存関係が追記される

・パッケージのインストール（特定のバージョン）

```
$ npm install [オプション] [パッケージ名@バージョン]
```

・インストール済みのパッケージを表示（依存パッケージは非表示）

```
$ npm ls --depth=0 | grep 絞り込みたいパッケージ名
```

・インストール済みのパッケージを表示（依存パッケージも表示）

```
$ npm ls | grep 絞り込みたいパッケージ名
```

・インストール済みのパッケージが最新かどうかチェック

```
$ npm outdated
```

・タスクの実行

```
$ npm run [パッケージ名]
```

## 2-1. yarn

パッケージマネージャーには、 `npm` の他に [yarn]("https://yarnpkg.com/ja/") というツールがあります。
これは、Facebook、Google、Exponent、Tildeによって開発された、新しいJavaScriptパッケージマネージャーのことで、`npm` と互換性があり、`npm` の以下の問題解決を目的としています。

- インストールが遅い
- インストール結果に一貫性がない
- セキュリティ上の懸念がある

### yarn の特徴

#### 1. インストールが早い

yarnは一度ダウンロードしたパッケージをグローバルにキャッシュとして保持し、次回以降そこからコピーするため高速にインストールできます。 またダウンロード自体も並列で行なうため、初回ダウンロードも npm に比べて速い。

#### 2. インストール結果に一貫性がある

yarnでインストールを行なうと、 `yarn.lock` というファイルが生成されます。
これは依存パッケージのバージョン情報を示しているファイルで、これを参照することで正確にパッケージを管理することができます。
（npmでもVer5からは `package-lock.json` が採用されました）

#### 3. よりセキュアである

Yarnはインストール前にチェックサムで整合性を確認するため、より安全であるといえます。
なによりも、FacebookやGoogleが開発に関わっていることの信頼感や、インストール時の出力がクリーンであることなどなど、多くのメリットを享受できます。



### package.json

npmでインストールしたパッケージは依存関係を含め、`package.json` という、ファイルで管理します。
また、インストールされたパッケージは `node_modules` というディレクトリに配置されます。

``` package.json
{
  "private": "true",
  "name": "myapp",
  "version": "1.0.0",
  "description": "",
  "main": "./lib/main.js",
  "scripts": {
    "server": "webpack-dev-server",
    "build_artifacts": "RELATIVE_PATH=true webpack",
    "build_lib": "webpack --config ./webpack.config.release.js",
    "test": "NODE_ENV=test npm run jest",
    "lint": "npm run eslint src/*"
  },
  "devDependencies": {
    "@types/jest": "^23.1.4",
    "axios": "^0.18.0",
    "babel-core": "^6.26.3",
    "babel-eslint": "8",
    "babel-jest": "^23.0.1",

    〜 省略 〜

    "ts-loader": "^4.4.0",
    "typescript": "^2.9.1",
    "typescript-eslint-parser": "^16.0.0",
    "webpack": "^4.1.1",
    "webpack-cli": "^2.0.12",
    "webpack-dev-server": "^3.1.1",
    "webpack-glob-entry": "^2.1.1",
    "webpack-spritesmith": "^0.4.1"
  },
  "dependencies": {
    "popper.js": "^1.14.4"
  },
  "author": "",
  "license": "ISC"
```

**パラメーターの説明**

|項目|意味|必須|
|---|---|---|
|name|パッケージ名|○|
|version|パッケージのバージョン|○|
|description|パッケージの説明||
|main|パッケージの中で最初に呼ばれるスクリプトファイル||
|scripts|npmコマンドから実行できるタスク||
|dependencies|公開時に必要なパッケージの情報||
|devDependencies|開発時のみ必要なパッケージの情報||
|author|パッケージ作者の情報||
|license|パッケージのライセンス情報||

npmやpackage.jsonを深掘りしたい場合は、
https://docs.npmjs.com/ の `Packages and modules` と `CLI documentation` がとても参考になります。


## 3. ES6（ECMAScript2015）

[ES6（ECMAScript2015）](http://www.ecma-international.org/ecma-262/6.0/index.html) は次世代JavaScriptの標準規格です。
旧来のJavaScriptに新しい機能や文法が追加され、さらに既存機能もアップデートされています。

安全、便利、効率的にプログラムを書くことができるようになっているので、ES6のシンタックスを使って開発するのが今のスタンダードです。（おそらく）
また、モダンなブラウザでは、ES6を旧来のJavaScript（ES5）に変換（トランスパイル）せずとも、そのままのシンタックスで実行できるので、今後ますます主流になっていくはずです。
（[compatibility table](http://kangax.github.io/compat-table/es6/)）

Node.jsもES6に対応済みで、のちに登場するWebpackも、公式のチュートリアルはES6で書かれているので、早めに慣れておくといいかも。

### 追加された機能

- let・constキーワードによる変数宣言
- classキーワードによるクラス宣言
- テンプレート文字列（Template strings）
- 配列ヘルパー（foreach、map、filter、reduce、every、some、reduce、for..of）
- アロー関数（Arrow functions）
- オブジェクトリテラルとデフォルト関数の引数（Object literals, Default function arguments）
- 関数の可変長引数（Rest and Spread operator）
- 分割代入（Destructuring assignment）
- promissとfetch
- モジュール（Modules）
- ジェネレータ関数（Generators）
- mapとset

チュートリアルで参考にしたサイト
https://learn.co/lessons/introduction-to-es6
https://codeburst.io/es6-tutorial-for-beginners-5f3c4e7960be


## 4. Module

モジュールとは、JavaScriptをある役割や機能ごとに分割したそれぞれのファイル単位のことです。

**特徴**

- ひとつのJavaScriptモジュールは、ひとつのJavaScriptファイルに対応する
- モジュールは変数や関数などを外部に提供（export）できる
- 別のモジュールで宣言された変数や関数などを取り込める（import）

**利点**

- 保守性
  - 依存性を増やさずにコードベースを拡張していくことができる
  - モジュールを書き換えるとき、依存性が少ないほど書き換えは容易になります
- 名前空間
  - モジュールごとに分かれたスコープがあるので、命名の競合が起きない
  - たとえば、module1の関数 x( ) はmodule2の関数 x( ) と衝突することはない
- 再利用性
  - 同じモジュールを他のアプリケーションで共有することができる
  - 便利な変数や関数を複数の場所にコピペせずとも、モジュールとして再利用できる

また、モジュールにはいくつかの形式が存在しますが、今後は `ES Module` だけ押さえておけばいいかと。

**モジュール形式**

- CommonJS
  - サーバーサイドをはじめ、Javascriptでアプリ開発をするための標準APIを目指したプロジェクト
  - Node.jsで採用されて認知度が上がった
- AMD（Asynchronous Module Definition）
  - モジュールとその依存関係を定義し、必要に応じて非同期にロードするためのAPIを定義
  - RequireJSで実装された
- ES Modules
  - ECMAScript2015で標準化されたモジュール仕様で、ブラウザが直接JavaScriptモジュールの仕組みを解釈できるようになった

モジュールを理解することで、後述するモジュールバンドラーの理解も早まります。

JavaScript モジュールの全体像を知るのに参考になるサイト
https://postd.cc/the-state-of-javascript-modules/

CommonJSとES Modulesの比較を知るのに参考になるサイト
https://qiita.com/rooooomania/items/4c999d93ae745e9d8657

ES Modulesの構文を知るのに参考になるサイト
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/export
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import
https://sbfl.net/blog/2017/07/26/es-modules-basics/


## 5. Webpack

[Webpack](https://webpack.js.org/guides/) は、ある役割ごと（クラスや関数単位）に分割されたJavaScriptファイル（モジュール）を、依存関係（読み込み順）を解決しつつ、1つのファイルにまとめる（バンドルする）機能を提供するツールで、モジュールバンドラーと呼ばれています。
loader（ローダー）を使うことで、CSSや画像ファイルといった、JavaScript以外のリソースもバンドルしてくれます。

ちなみに、モジュールバンドラーはWebpackの他にも、BrowserifyやRequireJSといったものがありますが、今は圧倒的にWebpackの時代です！

### モジュールバンドラーが登場する前の課題

複数のJavascriptを組み合わせて一つの機能を実現したい場合、
従来の方法だと、HTMLに実行順を考慮した `<script>` タグを列挙しなければいけませんでした。

たとえば、

- main.js（本体）
- lib_parent.js（親ライブラリ）
- lib_child.js（子ライブラリ）

の3つのファイルがあり、本体から親ライブラリと子ライブラリの機能を使いたいとします。（子ライブラリは親ライブラリに依存している前提）

このときHTML側に

```html
<script src="./main.js"></script>
<script src="./lib_parent.js"></script>
<script src="./lib_child.js"></script>
```

と書くと、main.jsは使いたいlib_parent.jsを参照できずエラーになります。

正しくは

```html
<script src="./lib_child.js"></script>
<script src="./lib_parent.js"></script>
<script src="./main.js"></script>
```

ファイル数が少なければ問題ないですが、これが5個6個..10個と増えていくと、依存関係も比例して増えていくので途端に管理が難しくなります。
また、変数名や関数名のコンフリクトが起きる可能性も高くなるので、命名には気を使うことになります。
そもそもHTML側で依存関係を意識しないといけない状態がよろしくない！


### モジュールバンドラーが登場したことで得られるメリット

#### 1. 自動的に依存性を解決する

複数のJavaScriptコードの依存関係を自動で解析し、最終的に1つのファイルとしてバンドルしてくれます。
よって、HTML側で依存関係をほとんど気にすることなく、バンドルされたファイルを読み込みさえすれば問題なく機能します。

反面、1つのファイルにバンドルしてしまうと、ファイルサイズが肥大化し、読み込みに時間が掛かってしまうといったデメリットもありますが、Webpackにはコード分割を行って、複数のバンドルファイルを生成するような機能も持っています。

#### 2. サーバーへのリクエスト回数が減らせる

リクエスト回数が減れば、転送効率がアップします。

#### 3. 大規模な開発がやりやすくなる

コードをクラス単位、関数単位で分割できるようになったため、コードの見通しが良くなり分業が楽になります。
また、モジュール化できることによって再利用性や保守性が高まり開発効率が上がります。

### Webpackの優位点

#### 1. 複数のモジュール形式に対応している

BrowserifyやRequireJSといった他のモジュールバンドラーは特定のモジュール形式しかサポートされていません。
その点、Webpackは、Moduleのところでも紹介した、すべてのモジュール形式を解釈できるので、過去のモジュール資産が活用できます。

#### 2. ローダーやプラグインが豊富で拡張性が高い

Webpack自体は、あくまでJavascriptのモジュールをバンドルするだけのシンプルなツールですが、
ローダーを追加することで、altJSやaltCSS、画像ファイルといったものをコンパイルした上でバンドルすることも可能になります。
またプラグインを利用すれば、コードの圧縮やバンドルしたファイルを実行するためのページ生成なんかも行ってくれたりします。

#### 3. フレームワークで採用されはじめた

Vue, Angular, Reactなどに内部的に利用されている他、Ruby on Rails でもVer5.1から標準採用されています。

以上の点から、最近のJavaScriptによるフロントエンド開発では、ほぼ欠かせないツールになっています。

## おわりに

ここに挙げた技術は、あくまで開発のベースになりうるものであって、その他にも、オススメ5選から外した技術やら概念やら、とにかく覚えることは山ほどあります。
また、エンジニアとしてのスキル以外でも、UI/UX、ビジュアルデザインといった、デザイナーよりの知識が必要とされる場合もあるでしょう。

これらの状況が、辛いと思うか楽しいと思うかはあなた次第・・・