見出し画像

小さな渋谷の会社でゼロから作るWebアプリケーション (5) フロントエンド開発環境整備

新年明けましておめでとうございます!

株式会社エー・アンド・ディのCTOの山口です。
仕事が忙しくて、ついに年を越してしまいました……。
(これも仕事ですが)

さて、前回でJavaのフォーマットなどを整えました。

今回は、フロントエンドの開発環境を整えるところまでやってみようと思います。

なお、本マガジンで開発しているプロジェクトのソースコードはこちらで公開しています。
(開発は継続しているため、masterブランチでは本文の内容より先に進んでいる場合があります)

https://github.com/andcorp/morisoba

フロントエンドの開発環境は難しい

フロントエンドの開発環境は基本的に難しいです。難しい理由が色々あります。

* 多くの要素が絡み合う。
        * HTML・CSS・JS・ブラウザ・サーバーへなど関係者が多い。
        * それぞれについて関連するツール・言語が多数ある。
        * UI/UXデザインのことまで考えると本当にやばい世界だ……。
* 中でもJavaScriptが難しい。
        * とても柔軟性の高い言語で、プログラマーに技量を要求する。
        * (最近は改善されているが)ブラウザごとに挙動が異なる。
        * 画面の状態管理や通信などを処理する必要がある。
        * 特に最近は、何でもJavaScriptででき、開発環境が複雑化している。

以前、JavaScriptプログラマーのための必読書を紹介するブログ記事が人気になったことがありましたが、そこでは十数冊もの必読書が挙げられていました。
必読書が十数冊あるプログラミング言語はなかなか無いです……。

そんなわけで、フロントエンド開発は、雰囲気のカジュアルさとは裏腹に難しいです。

複雑で難しいものに対応するには

複雑で難しいものを扱う必要が生じた場合、どうするべきでしょうか?

一番良いのは回避することです。
でも、このマガジンではその選択肢はありません……。
そこで、ここでは直球の取り組み方をします。

それは、いちばんシンプルな状態から始めて、徐々に必要なものを追加し、その度に理解を深めるという方法です。

フロントエンド開発は複雑なので、実は出来合いのインストーラーのようなものが色々用意されています。
たとえば、本マガジンと同様の構成であれば、NuxtJSが候補になります。

ただし、ツールにも以下のような欠点があります。

* 往々にしてインストーラー自体が複雑。
* 基本となる技術を覚えていないと結局うまく使えなかったりする。
* 何かトラブルが発生した時に対応しづらい。

そこで、このマガジンでは、基礎的なツールをちゃんとゼロから使って環境構築を行います。

(それに、ツールを使った記事は他にたくさんあるので、他所様を参考にしてください)

フロントエンド環境使用ツール

このマガジンでは以下のツールを使用します。

* node.js
* yarn
* Gulp
* Webpack4
* TypeScript

上記ツールを使用して開発が進められるよう準備を行うのですが、その前に各ツールの説明をします。

node.jsとは

node.jsとは、簡単に言えばJavaScriptの実行環境です。

これがあると、JavaScriptがブラウザなしで実行できます。
ということは、バックエンドサーバーや普通のアプリケーションの中でも、また独立したプログラムとしても、JavaScriptが動かせます。

つまり、バックエンドからフロントエンドまで、開発環境からブラウザの実行時まで、すべてJavaScriptを共通言語として使用できます。

そのため、フロントエンド開発ではJavaScriptが酷使されています。
CSSを(SCSSなどで)書くことから、Single Page Applicationを作ることまで、フロントエンド開発では何をするにもnode.jsが無いと始まりません。

yarnとは

yarnとは、node.jsのパッケージ管理ツールです。

npmjsというWebサイトで、node.js向けの様々なパッケージ(ライブラリ)が公開されています。
(ちょうど、JavaにおけるMavenのようなものです)
node.jsを使った開発では、ほぼ必ずnpmjsで公開されているパッケージを使用します。

そのパッケージを管理するためのツールとして、npmというツールとpackage.jsonという設定ファイルが主に使用されます。

npmはnode.jsに同梱されているためすぐ使用できるのですが、速度が遅い・機能不足などの問題があります。
そのため、高速な代替ツールとしてyarnの利用も広がっています。

このマガジンでは、そのyarnを使用します。

Gulpとは

Gulpとは、node.js用のタスクランナーです。

フロントエンド開発では、CSSやJavaScriptの変換・最小化など様々なタスクを実行する必要があります。
そういったタスクを実行するためのツールをタスクランナーと呼びます。Gulpはそのひとつです。

実は後述するWebpackのため本来Gulpは必要ないのですが、Windowsで実行するうえで多少トラブルがあるため、Gulpも併用します。

Webpackとは

Webpackとは、Webサイトのモジュールバンドラーです。

Webサイトは、JavaScript・CSS・HTMLなどなど、さまざまな種類のたくさんのファイルから構成されます。
さらに、通信速度の問題から、少しでも効率よくダウンロードされるようにサイズを圧縮したり、同時に必要になるファイルをまとめる場合もあります。

そういったファイルの圧縮・結合を、各種ツールと組み合わせて行うのがWebpackです。

TypeScriptとは

TypeScriptとは、型情報の構文を導入したJavaScriptです。

JavaScriptは関数や引数に型情報を書く必要はありません。
しかしTypeScriptでは、型を明記するための構文を導入しています。
元々のJavaScriptの構文や動作を大きく変えずに型情報を追加しているのが特長です。

ソースコードに型情報が明記されることにより、以下のような利点があります。

* コンパイル時の型チェックでコーディングミスによるバグを防げる。
* VSCodeなどのIDEで、型情報を元に関数等の情報を表示できる。
* 型情報が明記されることで、コードの仕様・意図が伝わりやすくなる。

型情報を書く必要があるため、どうしてもJavaScriptよりコーディング量は増えてしまいますが、大規模な開発を中心に利用が広がっています。

今回は、各種設定ファイルも含めてなるべくTypeScriptでコーディングを行うようにします。

フロントエンドプロジェクトの作成

前置きが長くなってしまいました。ようやくフロントエンド環境の構築に入ります。

まずはフロントエンドのソースコード等を置くGradleプロジェクトを作成します。
……フロントエンドなのにGradleプロジェクト?
前述したように、Gradleは活用するととてつもなくスゴイです。
なんと、node.jsのインストールと実行まで行えます。

Gradleでnode.jsをインストールすると、以下の利点があります。

* 使用バージョンをbuild.gradleに明記できる。
* インストール手順が簡略化できる。
* ビルドプロセスに組み込めるので、自動ビルド・自動テストの時に楽。

frontendプロジェクトのディレクトリ作成

それではフロントエンド用のGradleプロジェクトを作りましょう。

C:\Users\ikemen\morisoba> mkdir frontend

そしてbuild.gradleを作成します。いまはコメントだけです。

// frontend プロジェクト ビルド設定

frontendプロジェクトをmasterのsettings.gradleに追記します。

includeFlat 'backend', 'frontend'

これでGradleに新しくfrontendプロジェクトが追加されました。

frontendのbuild.gradle作成

次に、frontendのbuild.gradleにnode.jsのインストールの設定を追加します。

// 使用するプラグイン
plugins {
    id 'com.moowork.node' version '1.3.1'
    id 'com.moowork.gulp' version '1.3.0'
}

// node.jsバージョン
node {
    version = '12.14.1'
    yarnVersion = '1.21.1'
    download = true
}

// pacakge.json初期生成時の設定。
yarn_init {
    args = ['-y'] // ターミナルからの入力なしにする。
}

// ツール等のインストール
task installTools(dependsOn: [yarn_install]) {
}

// プロジェクトの初期化
task initializeProjects(dependsOn: [installTools]) {
}

これで、VSCodeからinitializeProjectsタスクを起動するとnode.jsとyarnがインストールされるようになります。

> Executing task in folder morisoba: .\gradlew.bat initializeProjects <
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :frontend:yarnSetup
C:\Users\ikemen\morisoba\frontend\.gradle\yarn\yarn-v1.21.1\yarn -> C:\Users\ikemen\morisoba\frontend\.gradle\yarn\yarn-v1.21.1\node_modules\yarn\bin\yarn.j 
C:\Users\ikemen\morisoba\frontend\.gradle\yarn\yarn-v1.21.1\yarnpkg -> C:\Users\ikemen\morisoba\frontend\.gradle\yarn\yarn-v1.21.1\node_modules\yarn\bin\yarn.js
+ yarn@1.21.1
added 1 package in 0.392s

> Task :frontend:yarn_install
yarn install v1.21.1
[1/4] Resolving packages...
[2/4] Fetching packages...
warning sha.js@2.4.11: Invalid bin entry for "sha.js" (in "sha.js").
info fsevents@1.2.9: The platform "win32" is incompatible with this module.
info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation.
[3/4] Linking dependencies...
[4/4] Building fresh packages...
Done in 28.16s.

BUILD SUCCESSFUL in 51s
7 actionable tasks: 6 executed, 1 up-to-date

こんな感じで出れば、インストール成功です。frontendの.gradleディレクトリ内にnode.jsとyarnがダウンロードされています。

.gitignore設定

さて、node.jsのインストールが完了したところで新たにnode_modulesというディレクトリが作られたと思います。

node_modulesはnode.jsが使うJavaScriptのライブラリが格納される場所のため、ソースコード管理等で格納する必要はありません。
(ビルド時などには自動でダウンロードされます)

そこで、node_modulesはソースコード管理から除外します。つまりfrontendディレクトリ直下に以下のような.gitignoreを追加します。

/node_modules

pacakge.json作成

node.jsがインストールできたら、次はnode.jsのパッケージファイル(package.json)を作成します。

node.jsでは、package.jsonという設定ファイルで使用するライブラリーなどパッケージの設定を管理します。
Gradleからyarnのコマンドを使用して、以下のようにpackage.jsonファイルを作成できます。


PS C:\Users\ikemen\morisoba\master> .\gradlew.bat :frontend:yarn_init 
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :frontend:yarn_init
yarn init v1.21.1
warning The yes flag has been set. This will automatically answer yes to all questions, which may have security implications.
success Saved package.json
Done in 0.04s.

BUILD SUCCESSFUL in 5s

これで、frontendディレクトリに以下のようなpackage.jsonファイルができます。

{
 "name": "frontend", // パッケージ名
 "version": "1.0.0", // パッケージのバージョン
 "main": "index.js", // パッケージのメインスクリプト。実行時に最初に起動される。
 "license": "MIT" // ソースコードのライセンス
}

(JSONファイルにはコメントが書けないので、実際は上記ファイルにコメントを記載していません)

このファイルを、開発プロジェクトに合わせて修正します。
内容についてのリファレンスはここにあるので、必要に応じて項目を設定します。

{
 "name": "morisoba", // パッケージ名
 "description": "MORISOBA Web Application Framework", // パッケージの説明
 "version": "0.0.1", // パッケージのバージョン
 "main": "index.js", // パッケージのメインスクリプト。実行時に最初に起動される。

 // パッケージの作者
 "author": {
     "name": "andcorp.co.jp",
     "url": "https://andcorp.co.jp/"
 },

 // パッケージのリポジトリー
 "repository": {
     "type": "git",
     "url": "https://github.com/andcorp/morisoba.git"
 },

 "license": "MIT" // ソースコードのライセンス

 // パッケージが非公開のものか。今回はWebアプリの一部として使用するため非公開にする。
 "private": true
}

TypeScriptのインストール

package.jsonができたら、ここに利用するツールやライブラリーの依存関係を記載していきます。

先述の通り、node.jsではnpmjsというサイトで各種ライブラリーが公開されています。
ここに、様々な言語のコンパイラやビルドツールやブラウザ向けUIライブラリやその他もろもろのあらゆるソースコードが格納されています。

そのソースコードを、package.jsonのdependenciesという設定にバージョンと共に記載するだけで、ダウンロードして利用できるようになります。

まずは、今後中心的なツールとなるTypeScriptのコンパイラや実行のためのツールをインストールします。

{
   "name": "morisoba",
   "description": "MORISOBA Web Application Framework",
   "version": "0.0.1",
   "main": "index.js",

   "author": {
       "name": "andcorp.co.jp",
       "url": "https://andcorp.co.jp/"
   },

   "repository": {
       "type": "git",
       "url": "https://github.com/andcorp/morisoba.git"
   },

   "license": "MIT",
   "private": true,

   // 以下を追加。
   "devDependencies": {
       "typescript": "3.7.4",
       "ts-node": "8.5.4", // Webpackなどの設定ファイルをTypeScriptで書く場合に必要
       "gulp": "4.0.2",
       "@types/gulp": "4.0.6" // TypeScript用の型定義ファイル
   }
}

なお、devDependenciesとは、開発時に使用する依存ライブラリ等を記載する箇所になります。
(実行時に必要になるライブラリはdependenciesに記載することになります。後ほど出てきます)

devDependenciesを書いた状態でinitializeProjectsタスクを実行すれば、必要なツールがダウンロードされます。

tsconfig.json作成

TypeScriptのインストールが完了したら、次にTypeScript用の設定ファイルtsconfig.jsonを作成します。

今回は、最低限のなるべく厳密な(型エラーの発生しやすい)設定とします。

{
   "compilerOptions": {
       "strict": true, // とにかく厳密に型チェックを行う。
       "target": "es5", // コンパイル後のJavaScriptのバージョンはES5とする。
       "esModuleInterop": true // JavaScript ES形式のモジュールと相互運用できるようにする。
   }
}

(コメントはやはり実際のファイルには記載していません)

index.ts作成

TypeScriptの設定ファイルが書けたら、パッケージのエントリポイントとなるTypeScriptファイルを作成します。

いまのところは何もしないスクリプトを配置してお茶を濁します。配置場所はfrontend内のsrc/ts/index.tsです。

/**
* アプリケーションのエントリーポイント.
*/

まだ何もしないのでコメントのみです。

Webpackインストール

次に、フロントエンドのサイト構築の中心となるWebpackをインストールします。

Webpackもやはりpackage.jsonのdevDependenciesに追記します。

{
   // 前略
   "devDependencies": {
       "typescript": "3.7.4",
       "ts-node": "8.5.4",
       "gulp": "4.0.2",
       "@types/gulp": "4.0.6",

       // 以下を追加
       "webpack": "4.41.5",
       "@types/webpack": "4.41.2",
       "ts-loader": "6.2.1" // WebpackでTypeScriptを使うためのローダー
   }
}

追記したら、またinitializeProjectsでインストールを行います。

webpack.config.ts作成

そしてWebpackの設定ファイルwebpack.config.tsを作成します。

Webpackは先述の通りWebサイトのファイルをコンパイル・圧縮・結合するツールのため、ブラウザにダウンロードされる全てのファイルが関わります。
そのため、設定ファイルの内容も複雑になります……。

とりあえずは最小限の内容で作成します。
ほぼ何もしない設定ファイルになりますが、先ほどと違って今度はちゃんと中身を書く必要があります。


/**
* Webpackビルド設定.
*/

// webpackのConfiguration型を取り込む。
import { Configuration, Module, Resolve, RuleSetRule } from "webpack";

/**
* ビルドルール定義.
*/
const rules: RuleSetRule[] = [

   // TypeScriptルール。
   // tsファイル取り込み時、ts-loaderでJavaScriptに変換する。
   {
       test: /\.ts$/,
       use: "ts-loader"
   }
];

/**
* モジュール定義.
*/
const module: Module = { rules };

/**
* モジュール解決方法定義.
*/
const resolve: Resolve = {

   // 省略可能にする拡張子の設定
   // これがあれば、import時等に拡張子の指定が不要になる。
   extensions: [".ts", ".js"]
}

/**
* Webpack設定.
*/
const config: Configuration = {

   // アプリケーションエントリーポイント定義
   // index.tsに紐づくソースコードを元にWebサイトが構築される。
   entry: "./src/ts/index.ts",

   // ソースコード解決方法
   resolve,

   // モジュール設定
   module,
};

// Webpack設定を外部に公開する。
export default config;

gulpfile.ts作成

次に、タスクランナーGulp向けのファイルを作成します。

実はWebpackだけでも本来は開発を進められるのですが、以下のような不具合がWebpackにあります。

現象としては、テスト用のサーバーがゾンビプロセスとして残り続ける問題になります。

Gradleのnode.jsプラグインではGulpを使用することで回避できたため、今回Gulpを使用しています。

というわけで、面倒ですがgulpfile.tsを作成します。


/**
* Gulpタスク定義.
*/

// webpackを使用するためimport
import webpack, { Configuration } from "webpack";

/**
* 終了関数の型定義.
*/
type DoneFunction = (error?: {}) => void;

// ログ等表示設定
const stats = {
   // ログをカラーコード付きで表示する。
   colors: true,
};

/**
* 先ほどのWebpack設定 webpack.config.tsを読み込む.
*
* @return Webpack設定
*/
async function importWebpackConfiguration(): Promise<Configuration> {
   return (await import("./webpack.config")).default;
}

/**
* ビルドタスク.
*
* @param done 終了関数
*/
export async function build(done: DoneFunction): Promise<void> {
   // Webpack設定を読み込む。
   const config = await importWebpackConfiguration();

   // 本番向けビルド設定に変更する。
   config.mode = "production";
   config.devtool = false;

   // Webpackビルド実行
   webpack(config).run((err, s) => {
       // ビルド結果をログ出力する。
       console.log(s.toString(stats));

       // 処理完了。エラーがあれば報告する。
       done(err ? err : undefined);
   });
}

/**
* デフォルトタスク.
*/
export default build;

ようやくビルド

ここまでの作業でようやくWebサイトのビルドが行えます。
といっても、まだ何もしないファイルが出来るだけです。


PS C:\Users\ikemen\morisoba\master> .\gradlew.bat :frontend:gulp_build
To honour the JVM settings for this build a new JVM will be forked. Please consider using the daemon: https://docs.gradle.org/5.5.1/userguide/gradle_daemon.html.
Daemon will be stopped at the end of the build stopping after processing
Configuration on demand is an incubating feature.

> Task :frontend:gulp_build
[15:11:22] Requiring external module ts-node/register
[15:11:24] Using gulpfile ~\github\morisoba\frontend\gulpfile.ts
[15:11:24] Starting 'build'...
[15:11:24] Finished 'build' after 448 ms
(node:6792) [DEP0097] DeprecationWarning: Using a domain property in MakeCallback is deprecated. Use the async_context variant of MakeCallback or the AsyncResource class instead.   
Hash: 68ade7a7935b17821c4aING [6s]
Version: webpack 4.41.5
Time: 1096ms
Built at: 2020-01-09 15:11:25
 Asset       Size  Chunks             Chunk Names
main.js  969 bytes       0  [emitted]  main
Entrypoint main = main.js
[0] ./src/ts/index.ts 112 bytes {0} [built]

BUILD SUCCESSFUL in 9s
2 actionable tasks: 1 executed, 1 up-to-date

frontendのdist/main.jsにJavaScriptファイルができました!

もちろんまだ何も出来ませんが、とりあえずフロントエンド開発用のツールが連携して動いたところまでは確認できました。

まとめ

設定ファイル多すぎですね……。

もちろんNuxtJSなどのツールもあるわけですが、本格的な開発を行っていると、結局いずれは全使用ツールを調べることになります。
そのため、このマガジンでは最初から使用ツールを明示していく形にしました。

次回はHTML生成・Webサイトのテスト表示まで行おうと思います。

免責事項・ライセンス

画像1

 この 作品 は クリエイティブ・コモンズ 表示 - 改変禁止 4.0 国際 ライセンスの下に提供されています。

 弊社の当コンテンツに掲載されている情報の正確性・安全性などについての保証はなく、弊社は何らの責任を負うものではありません。
 弊社の当コンテンツに掲載された内容によって生じた損害等の一切の責任を負いかねますので、ご了承ください。

Copyright © 2020 ANDCORP.

この記事が気に入ったらサポートをしてみませんか?