納得するための、なぜフロントエンド開発でwebpackなどを用いた複雑なJavaScript開発環境を作るのか


現代的な Web フロントエンド開発には非常に多くのキーワードが付属します。node.js、npm、webpack から始まり、Eslint、Prettier、React、Cypress、TypeScript…などなど情報量は圧倒的です。

時間と共に新しいキーワードは増え続け、情報だけ追いかけるほどフロントエンド開発が魔境に感じ始めます。とてもじゃないけれど手を出してはいけないと萎縮してしまうかもしれません。フロントエンドエンジニア、一体こいつらは何をしているんだ!と。

あるいは 昔ながらの開発を愛するエンジニアは、ツールばかり構築しているフロントエンドエンジニアを情報に流されて現実が見えていない哀れなエンジニアに感じてしまうかもしれません。

この記事は、フロントエンド開発にどうして色々なツールを使うのか、どうしてこうも複雑なのか、一体何をしているのかをツールの使い方ではないアプローチで説明することを目的としています。フロントエンド開発という括りですが JavaScript に大きく焦点を当てた説明になっています。個人的に、開発環境として納得が最も必要なのは JavaScript だと考えているためです。

効率よく開発がしたい

分割するまとまりや大きさは様々ですが、一般的にはプログラムを書く時は複数ファイルにコードを分割します。例えば機能毎に分けて書いておき、その機能が必要な箇所ではそのファイルを読み込んで使います。同じ処理をコピペせずに使い回すことで、後々効率よく修正も可能です。

分割したファイルの読み込みはプログラム言語によって様々です。JavaScript の場合は、動作させる html ファイルに複数の script ファイルを読み込みます。

<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>

読み込む順番や数の問題が出てくることは容易に想像できます。必然的に開発の段階から少量のファイル内に様々な機能を詰め込んでの開発となるでしょう。

この状況の改善に webpack を使うことができます。JavaScript の変数や関数を moudle として export し、他のファイルで必要な module を import して使用します。

// sample.js
export const sampleVariable = "sample";
export function sampleFunction(text) {
  console.log(`引数は ${text}`);
}
// index.js
import { sampleVariable, sampleFunction } from "./sample.js";

sampleFunction(sampleVariable); // 引数は sample

npm や yarn といったパッケージマネージャーを使用すれば外部ライブラリも module として自身のプロジェクトに import できます。JavaScript ファイルの読み込み順を気にする必要はありません。

import $ from "jquery";

$("button.continue").html("Next Step...");

上記のように開発したファイルはブラウザでそのまま使うことはできません。webpack で build して、import/export を上手く処理してもらいます。そして、生成されたブラウザ用の JavaScript ファイルを使います。

<script src="index.min.js"></script>

開発時は複数ファイルに分割、実際にブラウザで使用するのはブラウザ用に生成したファイルとなります。生成されたファイルは普通の JavaScript です、もちろん SPA ではありません

このような開発は React、Vue、Angular といった強力な view のライブラリと共に語られるケースが多く、従来の jQuery を用いた開発と比較されがちです。

しかし、どれもが JavaScript ですから、React か、jQuery か、あるいはライブラリなしの JavaScript かは関係がありません。webpack を使うことで、最終的なファイル読み込み数や順番を考える必要がなくなり、開発時はコードの品質に集中できます。簡単にいうと、webpack を使用することで効率良く開発できます

JavaScript に求められるもの

どのような開発手法であれ、ブラウザで動かす JavaScript は次の条件を最低限満たしていないといけません。

  • アプリケーションが対象とするブラウザで動作すること

どれだけモダンで、格好良く開発できるかは絶対に必要な要素ではありません、アプリケーションは正しく、想定したように動かなければいけません。chrome で問題なく動いたとしても、そのアプリケーションが IE で使われるなら IE で正しく動かないと意味がありません。

正しく動作するものを作れるところがスタート地点です。そして

  • 効率良く保守性が高い開発を行えること
  • よりパフォーマンスの高いアプリケーションを作成できること

こういった要素が開発環境に加わるとより良くなります。React で作ったアプリケーションはクールで機能追加が容易でテストしやすい、開発者自身も知的好奇心を刺激されるかもしれません。しかし、それが必要な場所で動かないなら評価対象にすらならないことは覚えておく必要があります。

ユーザーが使うブラウザは多種多様

プログラム言語の実行環境はプログラム言語により様々です、1つの例として PHP をあげます。PHP で良く使われているのは PHP5 系 か PHP7 系です。サーバーに入っている PHP が 5 系 なら、PHP7 で新規追加された機能を使うことはできず、PHP7 の機能を使いたいなら PHP を動かしているサーバー側が PHP7 に対応する必要があります。ユーザーの環境は関係ありません。

一方、JavaScript はユーザーのブラウザ上で動きます。なので、どの JavaScript の機能が使用できるかはユーザーが使うブラウザに依存します。ここで問題になるのが、ユーザーが使用するブラウザはバラバラということです。

同じ chrome でもバージョンの違いはあります。mac の safari と iPhone の safari では動作が違います。IE を使用するユーザーもいます。

JavaScript の進歩と ECMAScript について

JavaScript には ECMAScript という仕様があり、これに沿って JavaScipt の機能が決まります。JavaScript は ECMAScript 2015(ES6) で大きく機能が追加され、ECMAScript 自体の策定方法も変更されました。ES5 時点の JavaScript 仕様が一旦の区切りです。そうとうなレガシーブラウザでない限りは、ユーザーが使用するブラウザでまず問題なく動作するのが ES5 時点の仕様です。

ECMAScript は 2015 から毎年更新され ECMAScript2016,2017…2019 と策定されています。年々 JavaScript に新しい機能が追加されています。しかし、全てのブラウザがこれらの策定に追随し、アップデートできているかといえばできていません。ブラウザにより使える機能、使えない機能があります。

ユーザーが使用するブラウザの JavaScript 実装状況を正確に把握するのは大変です。しかし、それができなければ動作保証のために安全をとり JavaScript は ES5 の文法まで!となってしまいます。

繰り返しですが重要なのは対象とするユーザーが使えるアプリケーションであることです。モダンな JavaScript であるかは使う側からは関係ありません。しかし、機能制限がエンジニア側に与えるストレスは大きく、モチベーションの低下はアプリケーションの品質にも現れてしまうものです。

開発手法は進化する

プログラム言語は同じでもその使い方には流行があります。社会の状態にあわせたより良く開発する方法を多くの人が模索、提案し、その影響を受けてプログラム言語や開発環境も進化するからです。また、プログラムを動かす環境も技術の進化と共に変化します。2019 年では PC よりスマホが重視されています。2020 年や 2021 年は時計サイズの対応、あるいは声の対応が重要視される可能性もあります。どうなるかは分かりませんが、柔軟に対応できる開発であるに越したことはありません。

少なくとも、何かしらの意図がない限り、現代のプログラム開発では関連のないコードを 1 つのファイルに混ぜて書くようなスタイルは推奨されません。例えば顧客に関する機能を修正したい時、同じファイルで店舗情報や販売、商品登録などのロジックがグローバルに展開されていたらどうでしょうか。あるいは、機能が共通化されずにコピペされていたとしたら、自分の担当でないなら時間を理由にそっと目をそらすことでしょう。

本来なら読む必要のないコードを読むのは苦痛で、そんなものを作るのもつらいです。ロジックの切り離しが難しければリファクタリングするのも勇気が必要で、与えられた開発時間の短さに比例してコピペが乱立する原因になります。制作されたアプリケーションは、動作する、以上の価値を出せないかもしれません。

JavaScript は EcmaScript2015(ES6)を皮切りにプログラム言語として強力に進化し続けています。ブロックスコープを持つ変数宣言の let、const、module 機能の import と export、class や配列、オブジェクトの扱い。しかし、上述の通りユーザーが使用するブラウザは全ての機能を実装している保証はありません。確実に動く ES5 だけを使い、将来の負債覚悟で開発を行うのか、モダンブラウザに割り切って ECMAScript2015 以上を使うのか。

動作保証するブラウザが決まっているなら ES5 しか選ぶことができない状況はあるでしょう。しかし、そういった状況を、ツールを使用することで解決できるなら新たな選択肢となります。ECMAScript2015 以上で開発しても、 ES5 と同じレベルでブラウザ動作が保証できるならどうでしょうか?最高です。

トランスパイラで ES5 に変換する

ECMAScript2015 以上の仕様で書かれた JavaScript を、設定にあわせて変換してくれるトランスパイラというものが存在します。有名なものが babel です。また、日々注目度を増している TypeScript もその 1 つです。

トランスパイラは様々なツールと連携が可能で、2019 年時点では webpack と共に使われることが多いです。トランスパイラを使うことで、開発は ECMAScript2015 以上、あるいはライブラリ独自の構文を使い効率良く行い、実際にブラウザで読み込ませる JavaScript はブラウザに対応した形に変換された JavaScript ファイルとなります。

もちろん、トランスパイラはそれ単独で全てを解決できるわけではありません。そもそも ES5 にない概念もあり、後述する import/export の問題もあります。特に IE で問題にぶつかりがちで、一部機能には polyfil を使った対応が必要になります。また、一般的でないブラウザや専用にカスタマイズされすぎている環境などはやはりトランスパイラといえど怖いものがあります。

これさえ使っておけば全てが解決する!というものではなく、トランパイラを使用した上で必要な環境に応じた修正を行います。こう書くと、学習コストを払っても対応が難しいなら ES5 + jQuery なのではと保守的に考えてしまうかもしれません。ただ、ES5 + jQuery でも端末依存やブラウザ不具合には遭遇します。どのような開発環境であれ不具合や特定環境への対応は必要な要素です。

import/export するための Webpack の必要性

トランスパイラの bebel と共に、モジュールバンドラーの webpack が使われている構成を良く見かけるはずです。モジュールバンドラーを使うと、各プロジェクトで作成した module と外部ライブラリの module をとても簡単に import/export して使えるようになります。

module の import/export は webpack を使用していると意識せずに簡単に使えますが、webpack なしだと途端に困難になります。webpack 公式の why webpack ではその理由について説明されています。

Why webpack

わたしは import/export の状況について追いきれていないため、浅い説明になりますが下記にモジュールバンドラーの必要性をまとめます。CommonJS などの用語はひとまず置いておきます。

サーバー側とブラウザ側

ブラウザ側では JavaScript の読み込みは script タグで行います。複数読み込みが必要な時は複数の script タグを書きます。

<script src="./sample.js"></script>
<script src="./sample2.js"></script>

しかし、サーバーサイドの node.js の場合、実行環境がブラウザではないので別のファイル読み込み方法が必要になりました。それが webpack.config.js などで良く見かけるだろう、module.exports と require です。

function sample() {
  console.log("sample");
}
module.exports = { sample };

const { sample } = require("./sample.js");

module の export と require の機能を使い、それらをパッケージとして NPM で管理する方式がサーバーサイドの JavaScript の世界ではとられています。これはとても便利なのでブラウザ側でも使いたいのですが、ブラウザ側には module.exports と require の仕組みがありません。

ブラウザ側の JavaScript でも ECMAScript2015 から ES module という仕組みがあります。しかし、その使い方と公開方法は node.js とは異なります。


// html
<script type="module" src="./index.js"></script>

// index.js
import {sample3} from './sample3.js'
sample3();

// sample3.js
export function sample3 { console.log("sample3") }

ES Module は上記のように html の script タグの定義が必要なのでトランスパイラでどうこうできる問題ではなく、IE が対応していません。また、NPM で公開されているパッケージの全てが ES Module に対応しているわけではありません。

こういった JavaScript とブラウザのジレンマを解決してくれるのがモジュールバンドラーです。つまり、node.js で使われているルールで export している module を、node_modules フォルダから import してくれる。自身が書いた module を import/export 可能にしてくれる。それらを最終的に 1 つのファイルにまとめて、多くのブラウザに対応してくれる。

ブラウザや使用したいライブラリが ES Module に対応していなくても、import/export を使って開発ができます。将来的には必要なくなるという意見はあるものの、現状は必要です。

他にも強力な機能がたくさん

webpack は module の import/export を簡単にするだけではなく、plugin で色々な機能を追加することもできます。更新したらブラウザを自動的にリロードしてくれたり、JavaScript だけでなく CSS の変換、JavaScript ファイル内で扱う画像ファイルの変換などたくさんあります。

使用している人が多い分、ドキュメントやベストプラクティスが豊富なのも利点です。

これらの機能を体感するには Create React App や Vue-cli といったモダンな開発環境に触れてみることをお勧めします。適切に設定された webpack + 各種ツールを使用した開発がどれだけ強力を体感でき、きっと楽しくなります。

目的を間違えないこと

フロントエンド開発で webpack やトランスパイラといった様々なツールを使うことは当たり前、という雰囲気はウェブ上の記事では年々強くなっていると感じます。わたしもそう思いますし、フロントエンド開発環境の進化をどんどん取り入れたいとは考えています。

とはいえ、当たり前だから使うはとっかかりとして悪くないものの、意味もわからずにツールだけ増やし続けるのは良くありません。とりあえず使ってみる柔軟性は絶対に必要ですが、採用するバランス感覚と勉強時間は絶対に必要です。

フロントエンドは強力なツールが増え続けている結果、取捨選択が難しく振り回される状況にもなり易いです。意識の中心に置いておきたいのは、そもそもの目的はツールを使うことではないことです。2019 年 7 月の時点では webpack は主流ですし、TypeScript はとても便利で、React は最高です。しかし、これから先もずっとそうとは思えません。

特定のものに固執するのではなく、このプロジェクトではこれがもっとも開発効率とアプリケーションの品質を上げ、長い目でみて保守と改善がしやすいから使う。特殊な状況だと、昔ながらの確立した手法の方が有効なケースもあるはずで、その状況ですらツールの話しばかりしてしまったら一度立ち止まりましょう。

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

慣れてくると難しさが薄れてきます。とても良いことです。しかし、フロントエンドは開発時に非エンジニアが触れる機会も多い領域ということを忘れてはいけません。HTML コーダーやデザイナーが触ることもあるでしょう。

個人開発でもない限りはチームの理解が必要です。ウェブのみんながやってるから!は説得理由として弱すぎます。また、よしやろう!となっても学習コストは想像以上にあります。node.js どうこうの前に、ターミナルを触ったことがない人もいるわけです。

開発スタイルの変更にはストレスも付きまといます。また、信頼関係も必要です。極端な例ですが実力も良く分からない新人にこんなことを言われるとしたらどうでしょうか。

あなたの開発方法古くて非効率ですね。ぼくがセットアップしてあげるのでちょっと貸してください

素晴らしい開発環境はある日突然降ってくるものではなく、突然そうなるものでもないと思います。ちょっと目につく場所で少しずつ改善したりアピールして、もし周囲が興味を持ってくれたら全力で後押ししてみる。複雑そうにみえて簡単ですよと言わんばかりに設定ファイル書いてみたりする。

時に強行にプロジェクトに入れてみたりなど試行錯誤が必要です。つまり、 webpack などツールの使い方をある程度覚えたが職場に中々浸透しないと感じたら、次はコミュニケーションや処世術を学ぶべきなのかもしれません。そういった面もあわせて、フロントエンドというか、IT エンジニアの難しさを感じることがあります。

参考