1.数ヶ月ぶりにビルドしたらエラーになる
開発中は問題がないのですが、数カ月ぶりにWebpackなりをつかってビルドしてみるとエラーがでる。
フロントエンド開発者なら一度は経験したことがあるのではないでしょうか?
いろんな原因があると思いますが、以下のような条件で起こることが多いのではないでしょうか?
・複数のプロジェクトを開発する端末に直接配置している。
・nodeenvなどのバージョン切り替えツールをいれずグローバルでnodeを使っている。
・ライブラリをプロジェクト内で限定せずグローバルでつかっている。
要は、手元のMacやWindows、Ubuntuなど開発端末上に直接、複数のプロジェクトを配置して開発しつつ、nodeを共有したり、安易にバージョンアップとかしているとビルドができないなどのエラーが発生したりします。
2.nodeをつかったフロントエンドのビルドの脆さ
(1)特定の団体がビルドツールをメンテしてくれる言語
ビルドというのは、Java、Go、Rustなど型付きの言語ではよくしますが、ビルドツール自体が単一のもので特定の開発団体がそれら全体をメンテしてくれています。
もちろん、これらのツールであっても、バージョンが違えばビルドは通らなくなることがあります。ただ、後方互換があって、多少バージョンがあがっても動くこともままあります。
(2)ビルドという行為が不要な言語
PHPやRubyのようなスクリプト言語の場合、ビルドという行為がそもそもありません。ただ、ランタイムのバージョン違いで動かないということは、node同様に起こります。
(3)Webのフロントエンドのビルドツールは「ごった煮」
昨今では、Webのフロントエンドは、HTMLをつくることすら「ビルド」というものを通さないと作れないことがあります。まして、Reactなどのコンポーネントツール、SassによるCSSの作成などを行っていれば、ビルドは必須になります。
この際利用されるのがnodeであり、「ごった煮」になったnpmライブラリ群です。
Webのフロントでいうビルドツールは、JavaやGoのように特定の団体が単一のことに対してつくったビルドツールとは違います。
いろいろな団体や個人がいろいろな目的のために作ったものを、Webpackやgulpのようなタスクランナーでまとめて実行することになります。
つまり、ビルドに関して総合して誰かが担保してくれないのが、Webフロントのビルドです。
ユーザーが責任をもって管理せざるをえないのです。
(4)nodeのバージョンアップは激しく仕様が変わりやすい
node自体の更新が激しいため、nodeのバージョンが合わなくて動かなくなることも多々あります。
[以下の文言は投稿時に、こちらのURLで記載がありましたが、2022.10.9時点ではページが無くなっています。)
Node.js のメジャーバージョンは 6 ヶ月間 現行 リリースの状態になり、ライブラリの作者はそれらのサポートを追加する時間を与えられます。 6 ヶ月後、奇数番号のリリース (9, 11 など) はサポートされなくなり、
以下は、上記のコメントの代わりにリリースサイクルに関する記事です。
当然、他の言語でもバージョンアップはありますが、nodeは他のものよりその間隔が短く、サポート期間も短いと思います。
nodeで作られたライブラリはともすればバージョンと合わないとよく警告がでたり、そもそも動かなくなることも多いです。
3.Webフロントのビルド環境は固める(Dockerのススメ)
(1)nodeenvよりDockerの方がよいと思う理由
Webのサーバサイド開発は、Dockerを使うことが増えています。
なので、Webのフロントエンドもついでに、DockerでOSやnodeの実行環境を固めてしまえば、とりあえずは冒頭で述べたような問題は回避できると思います。
nodeをつかったWebのビルドの仕組みはとにかく、バージョンアップの影響を受けやすいため、サーバーサイド以上に環境を固めてしまうことが必要だと考えます。
もちろん、nodeenvをつかったバージョン固定もいいですが、うっかりミスということを防ぐには、Dockerの方がよい気もします。
ついでに、ビルドサーバーの起動なども一緒に作ってしまえば、コンテナ上げればすぐにビルド環境が実行されるので便利です。
最近は、Github ActionsなどCI/CDを外部サービスに依存することがあります。
この際に、できるだけ同じ環境でビルドした方が、デプロイ時のトラブルを未然に防ぐことができます。
あと、チーム開発の場合、dockerなら開発環境を揃えやすいです。OS自体の環境に左右されることが少ないです。慣れてる人は、問題なくOSでの開発環境を整えられるかもしれませんが、チームメイトすべてがそうであるかはわかりませんし、時間が経ち、チームメイトが変わったときを考えるとOSに直に環境構築するよりも、コンテナを使った方が無難な気がします。
最近、AWSでもfargateなどのコンテナをつかった環境構築が主流になりつつありますが、どこでもいつでも同じ環境をつくることはとても重要だと思います。
"俺だけ、今だけ"の視点だけでは、チーム開発の効率化は難しいです。
私は、alpineなどを使って軽量なコンテナのDockerファイルを一つつくってそれを使いまわしています。
Githubからソースをcloneしてビルドして、npmのタスクランナーを起動するみたいなよくすることを書いて、使いまわしています。
Dockerを使わなくてもnodeのバージョンを固定化する方法もありますが、コンテナを使うと固定化だけでなく、環境構築自体もしてくれる所にメリットがある気がします。
とはいえ、ポイントは環境やライブラリを固めることなので、個々人やチームで最善だと思うやり方でよいと思います。Dockerはその一つの手段です。
(2)Docker for Macが遅い場合
これはよく言われることですね。WindowsやLinuxだと全くそんなことはないというか、値段が安いのでスペックを上げてしまってればよいという問題もあるかもしれません。
Docker for Macは、Macのファイルシステムとの相性がよくないので遅いようです。(そもそも、DockerはLinuxをベースとするものなので、UnixベースのMacとは相性がよくないんでしょうか。)
なので、WindowsがWSLを使っているように、仮想のミドルウェア(Lima)をいれて、解決するという手段もMacではあるようです。
4.(余談)フロントエンド開発における脱node依存
(1)Hotwireという仕組み
個人的に、最近はRailsのTurbo、そして、LaravelのLivewireのようにnodeに依存しないフロントエンド開発を進めています。
これらを実現する技術はHotwireという名前で呼ばれています。
TurboやLivewireはサーバサイドでコンポーネントのコードを書きます。Javascriptのコードをプログラマーはあまりかきません。ライブラリ側で自動的に、適宜、Javascriptのコードは作ってくれています。
とはいえ、大半はイベント周りのコードで、コンポーネント自体のHTMLなどはサーバサイドで作られています。
ちなみに、GoでもTurboを利用したHotwireの試みがあります。
RustのフレームワークでTurboを使うという試みもありますね。
Rustのフレームワーク、RocketにHotwireを組み込む実験プロジェクト。
(2)Hotwireを使うメリット
Hotwireを使うことで以下のメリットが考えられます。
・フロントとサーバで同じ言語で開発できる(管理や学習コストの低下)
・フロントとサーバのリソースを集約できる。(サーバ側で一括管理)
・Webのフロントのビルドツールへの依存度を下げられる。(構築コストを下げ、ビルドトラブルを回避)
フロントとサーバの言語を統一するという点では、node.jsをサーバサイドのランタイムとして使うのも一つだと思います。
一見、昔のサーバサイドの開発に戻っているように見えますが、完全にそうとはいえません。
Ajaxの仕組みをつかったり、コンポーネント設計ができます。昔のサーバサイドにはこうした発想や設計はなかったと思います。
JavaScriptをつかったフロントエンド開発が生んだ設計思想を踏まえた仕組みが、Hotwireなのだと思います。
大切なことは、JavaScriptやTypeScriptを使うことではなく、いかにユーザーや開発者が楽な設計をするかということであり、Hotwireはユーザーと開発者双方の利便性を追求しようとする一つの技術なのだと思います。