Wheel Builder.org

手組ホイール.org

ホイール設計くんを全面的に再設計

Posted at # development

全面刷新

ホイール設計くんは 10 年前の JavaScript,いわゆる Vanilla JS (ES5 以前)でコーディングしていました。HTML, CSS, JavaScript(ES5)に jQuery などを組み合わせれば,フロントエンドからサーバーサイドまで一応は手広く設計・実装できるしこれでいいやと。

2015 年で私のプログラミング知識は完全にストップしていたのでした…

眠り

Google App Script 🔗

2015 年以前は GAS(Google App Script) 🔗, GAE(Google App Engine) 🔗等の Google サービスや AWS(Amazon Web Service) 🔗など大手のサーバーを借りてサーバーサイドプログラミングするのが全盛で,当時 node.js などすらも使わずに GAS ばかり使ってサーバーサイドを動かしていました。今思うとかなりのローテクでプログラミングしていました。

下手くそな設計だとサーバー側の処理が遅くて読み込みに 15 秒かかったりとかもざら。でも,原因箇所さえつかめれば非同期処理を組み合わせるなどすることで,大抵のアプリケーションは「それなり」には動きます。

JavaScript 便利だなー,なんでもできるなー,今度なんか作るかなー

と思っている間に仕事が多忙になりウェブ開発の一線から離れていました。

覚醒

しかし,先日会社に転がっているソースコードを見てみたら,ウェブ画面のソースコードが一昔前とは一変していることに気づきました。

そう,React 🔗が使われていたのです。(気づくの遅い!)

React は標準で TypeScript を採用しており,JavaScript マスターを自任していた私にとって,以下の構文は衝撃的。ナニコレ???

const function = () => {
    return "Hello";
}

関数の書き方だけでさえ謎なのにさらに,React の構文も意味不明。

type Props = {
    name: string;
    handler: any;
};

const function = (props: Props) => {
    const [value, setValue] = useState("");
    return (
        <div>
            <p>Hello, {props.name}</p>
        </div>
    );
};
export default function;

「Props って何?」 「useState って何?」 「JavaScript の関数の中に HTML がいるって何?」 「Docker 🔗ってどんな開発環境?」

思ったより何も理解できませんでした。見覚えあるのに分からない。懐かしいけど初めてあった人,みたいな。

そのソースコードで実際にやっているのはボタンを押したら装置が動作開始するとかのごく単純な動きです。

一昔前なら,ゴテゴテの機会を動かす画面なんて,Visual C++とか Visual C#で画面を作っていたと思いますが,今どきは装置制御もウェブ画面から行って,スマホやタブレットや PC どこからでも制御・閲覧できるのが普通。

完全に時代に取り残されていました。

JavaScript のカビの生えたイメージ

JavaScript ってネット黎明期にはブラウザクラッシャー(通称ブラクラ)とかみたいないたずらプログラミング言語みたいな印象がありました。所詮ネットスケープ社独自の機能もしょぼいスクリプト言語。

while (true) {
    alert("閉じても無駄!");
}

while (true) {
    window.open();
    alert("無限に新規ウィンドウが開く!");
}

遊びでアラート画面出しまくるとかも簡単にできてしまうしうっとうしいから,ブラウザの設定で JavaScript はオフにしておいたほうが安全だよ,みたいな。どちらかというとカビが生えたというか日陰のプログラミング言語というのが昔の印象です。

私が JavaScript でゴリゴリ実装していた 2010 年代もそこまで立派な言語という印象は持っていませんでした。

Google が jQuery(AJAX = 非同期処理)を駆使して Google Earth みたいな動的なページも作ってておしゃれだけど,Google の一流プログラマーが設計を駆使してやっとのことでリアルタイム性のある描画を実現しているんだなーという印象にとどまっていました。

まだまだネイティブアプリの方がインストールは手間だけどレスポンス含めて圧倒的に高性能で快適という時代。

一方で,気楽にテキストエディタ―とブラウザ一つで開発環境すらなくても開発できる JavaScript は日曜プログラマーからすると気楽につきあえるいいお友達。

当時は棒グラフや円グラフなどを簡単に描画できる Chart.js やGoogle Charts 🔗 などのライブラリーがいろいろ登場していた時期でもあったので,導入しては試してみて,みたいなのを繰り返していました。

Google Charts

ここまでが,プレ 2015 年の JavaScript の時代。

当時はやはり

  • ネイティブアプリが王様
  • JavaScript 含めウェブアプリはニッチ

この印象がガラッと崩れる事件が起きます。

ES6(ES2015)と TypeScript

JavaScript はネットスケープ社の独自言語で,のちにマイクロソフト社のインターネットエクスプローラーでも対応したりするものの,ブラウザ間の互換性も乏しく,ブラウザごとに見栄えが違うなど,お金を取る製品には使えないなと思っていました。デバッグも大変だし…。

でも気が付けば業界団体が設立され,Ecma International 🔗 が ECMAScript という標準規格を作っていたのでした。(知らんかった!)

私が知っていたのは ES5(ES2009)ごろまで。

でも,破壊的に進化したのはその後の 2015 年,ES6(ES2015)が登場してからでした。

const function = () => {
    return "Hello";
}

というアロー関数という表記法も ES6 から追加になっていました。

また,マイクロソフト社が推進する TypeScript 🔗 (JavaScript に型宣言を追加したもの)も標準化され普及しています。

TypeScript は言語的にはほぼ JavaScript の上位互換ですが,実際にはスクリプト言語ではなく,TypeScript のソースコードを tsc(コンパイラー)を使って JavaScript にコンパイルして実行します。

いわば,C 言語とアセンブラの関係に近く,

高級言語コンパイル低級言語
TypeScript=>JavaScript
C 言語=>アセンブラ

みたいな感じに進化していたのでした。

TypeScript は型宣言がはっきりしていることで,エディターでの静的解析を使うことができ,コンパイルする前に代入ミスなどを警告できるようになりました。

JavaScript の”ゆるい”ところが直されて,“お堅い”TypeScript に進化していたわけです。型をいちいち指定するのでコード量は増えますが,整数値に文字列を代入するみたいなしょうもないミスは排除できます。typeof を使ってフェイルプルーフなコードを書くことがめっきり減りました。プログラマーの生産性向上にはもってこい。

調べれば調べるほど,新しい言語仕様(ここでは TypeScript)の方が優れていることだらけで,知識のアップデートしないとヤバいことに気づかされました。

React

TypeScript というベースとなる言語仕様を把握して,やっとのことで会社で使っているReact 🔗というライブラリーにやっとたどり着くわけですが,こちらは言語ではなく,ライブラリーというだけあって,ブラウザ画面に部品を描画するための小道具といった印象。もちろん使わなくても立派なウェブ画面は作れるんですけど,React を使うといろいろ手っ取り早いです。

特に戸惑ったのが JavaScript, HTML との力関係が逆転している点です。

  • 旧来:HTML の中から JavaScript を呼ぶ
  • React:JavaScript で HTML を生成する

React の発想は PHP に近く,スクリプトが画面内の HTML を動的に作っていく感じです。過去にも確かにこういうものは存在していましたが,JavaScript でそれをやるのかーというのは感慨。

この React の「HTML 生成用 JavaScript/TypeScript」ファイル形式を JSX あるいは TSX と呼び,JavaScript を理解しているエンジニア向けには理解しやすい作りになっていたのです。

※最初説明がなければ何で return 文のところに HTML タグがいるの???ってなると思いますが。これが JSX/TSX のお作法です。

const function = () => {
    const [lang, setLang] = useState("ja");
    let lang : string = "ja";
    return (
        <body>
            <div>
                <h1>Sample</h1>
                <p>Hello, {props.name}</p>
                <button
                    onClick={() => setLang(lang === "en" ? "ja" : "en")} />
                <p>今の言語は{lang}です。</p>
            </div>
        </body>
    );
};
export default function;

フェイスブック社が膨大なウェブコンテンツを動的に公開するために React を使っているというのはニュース等で知識として知っていましたが,まともにソースコードや開発環境を調べなかったのは痛恨のミス。

2015 年以降,React 含めて数多くのライブラリーやフレームワークが誕生し,ウェブ開発は効率化・高速化・巨大化しています。

もしあなたが JavaScript しか使っていなかったなら,今すぐ何らかの新しいフレームワークを試してみてください。

ローレベル制御は過去のもの

JavaScript でボタン・テキストボックス入力・選択肢などの動作の変化を受け取ろうとすると 以下のようなイベントハンドラー・イベントリスナーをそれぞれの id ごとに設定する必要がありました。

ボタンが 100 個あったらそれぞれ異なるユニークな id と,その id に紐づいたイベントリスナーが必要。今思うと大変。

addEventListener;
window.onload;
append;
innerHTML;

でも,React 等のフレームワークを使った場合これらを一切使う必要がなくなりました。

ボタンが 100 個あったとしても,『ボタン描画』コンポーネントを 1 個作っておいてやれば,子コンポーネントとして『ボタン描画』コンポーネントを使いまわせるのです。上位の親コンポーネントは「自前で作ったイベントハンドラー関数」を経由して子コンポーネントの変化を受け取り好きなようにコーディングできます。

フェイスブックがわざわざ React というライブラリーを作ったのも,この効率の良さを追求するためでしょう。

個人のホームページならせいぜい数個のボタン,数個のテキストボックスで済みますが,SNS みたいに大量にあるボタンを id で管理するのは限界がある。なので,ウェブ画面に描画する範囲内のみ必要なボタンコンポーネント,Select コンポーネントなどの子コンポーネントを貼り付けていくだけでいいのは理にかなっています。

もちろん,React/TypeScript で実装したソースコードをコンパイルしたら,これらのローテクな JavaScript に変換されるので,知識として知っておき使いこなせるのはメリットが大きいです。

道具は使いよう

装置の組み込み開発やっている人なんかだと, ・起動ルーチン:アセンブラで書く ・起動後の処理:C 言語で書く みたいな棲み分けがあるはずです。今となってはめっきり減りましたが,CPU 起動直後にジャンプしてくるエントリーアドレスから P, D, R, W 等のセクションを設定したり,各ポート・各周辺機能を設定したりするような部分はアセンブラで書くことがよくありました。

各 CPU メーカーのマニュアル通りに 1bit 単位で設定するだけなのでアセンブラでやっても,C 言語で書いても大差ありません。

でも,その後のタスク設計やアプリケーション層の設計となると話は別。 取り扱うデータが増え,ロジックも複雑になっていくとアセンブラみたいな低級言語で取り扱うのはまず無理!!!

プログラマーが CPU のアドレス 1 個ずつを把握して,JMP 命令等で処理するみたいなのは起動ルーチン以外ではまずやりません。規模が大きくなるほど指数関数的にバグを生むからです。

高級言語のいいところは,配列・MAP さらにはスタックやリングバッファなどのデータ構造にデータを詰め込めるところと,それらのデータの具体的な格納場所(メモリの番地だったり,CPU のアドレスだったり)を意識しなくていい点です。コンパイラーとリンカーのおかげです。

それに似た話で,今どきは JavaScript(アセンブラ相当)を直接使いこなす必要は極めて少なくなっている のです。

では,addEventListener()を使わずに,フォームの値を取得したり,Select や Input の値を読み取るにはどうするか?

React の場合は,useState()というフックを使用して,ボタンや Select 等の要素から変数を動的(リアクティブ)に取得できるようになっています。

先ほどの例で言うと,以下のような感じで,クリックした際に関数が呼び出され,その中で lang という変数の値が書き換えられます。 さらに{lang}というかっこでくくってやると,この値を動的にウェブ画面に表示することが可能です。

ちょっと前なら,JavaScript を使って EventListener で変更を検知し,変化した値をもとに innerHTML を書き換えるみたいなローテクな処理が必要でした。もちろん,今でもそのやり方を使ってもいいのですが,JSX/TSX 形式の React を使用すると同じことをもっと簡単に実現可能です。

const function = () => {
    const [lang, setLang] = useState("ja"); // lang変数(言語設定)を状態管理しますよー
    let lang : string = "ja";
    return (
        <body>
            <div>
                <h1>Sample</h1>
                <p>Hello, {props.name}</p>
                <button
                    onClick={() => setLang(lang === "en" ? "ja" : "en")} />
                <p>今の言語は{lang}です。</p >
            </div>
        </body>
    );
};
export default function;

React 以外の選択肢

このホームページではASTRO 🔗React 🔗で作っています。

特にウェブアプリみたいな動的な部分は React で全部作ってますし,blog みたいな静的な部分は ASTRO を使ってマークダウン(.md)で書いています。

そして,React/ASTRO で制作したソースコードをコンパイルし,HTML と JavaScript と CSS ファイルを出力しています。最終的にウェブサーバーにアップロードするのは,生成された HTML と JavaScript と CSS ファイルです。

自前でウェブアプリを一から作り直して思うのは,10 年前よりも現在の方が開発環境が圧倒的に充実し,同じ時間で何倍も効率がよくなったなーってことです。

最初に調べる手間が必要で,取り掛かるまでに 1 か月~ 2 か月かかるわけですが,自分で開発する規模が大きくなるほど React や Astro で作るメリットが増えていきます。コンポーネント単位で設計するので,使いまわししやすいのです。

もし,ここ 10 年のウェブ開発界隈の進化についていけていない方がいたら,ぜひ今からでも React, Astro, Svelte などに取り組んでみてください。

進化が早いデメリットとして,気が付いたら言語仕様が勝手に変わってる!!(使っていた関数ライブラリーが削除されている!!)みたいなことも起こりがちですが,ここ数か月調査した限りでは,たいていのフレームワークは正常進化,まっとうに便利で堅牢な方向に進化しています。

今から取り組むなら

ASTRO 🔗

一押しです。圧倒的に軽くて早く,効率がよい。さらに React や Svelte のコンポーネントを組み込むこともできる優れもの。 テーマファイルも用意してくれている方がいるので,ウェブページのデザインも簡単。

React 🔗

動的なページならとりあえずコレ。 Svelte よりもソースコードが肥大化しやすく,繰り返しおまじないみたいな文言を書く回数が多いと感じるものの,ボタン 1 個単位でコンポーネント化して描画する,細分化したパーツを配置するだけという設計方式は明快で分かりやすいです。

開発者の数もウェブ開発案件の 6-7 割を占めるというだけあって,情報もいくらでも手に入るし,ライブラリー等の完成度も高く,変なバグ・変な挙動に悩むことは少ないでしょう。

C 言語などのほかの言語をやっている人からすると逆に「全部を記述する」React のスタイルは安心感があるはず。自分がコーディングしたこと以外はおせっかいしないというのは,ブラックボックスになりにくいのが最大のメリット。

いろんな機能を追加したければ,サードパーティーのライブラリーを組み込むことが可能。開発者も多いことからライブラリーの完成度,ドキュメントの豊富さはぴか一。

NextJS 🔗

React のフレームワークの一つではあるあるものの,妙に動作が遅く,不要な依存関係も多く使いにくい。ちょっと試してお蔵入り。あえて使う必要はないものだと思います。

Svelte 🔗

React に近い高速・軽量フレームワークの一つです。

こちらは React みたいな JavaScript/TypeScript からすべてのウェブコンポーネントを生成するというスタンスではなく,HTML と JavaScript を 1 つのファイル内でミックスしつつ,コードの記述量を減らす工夫が随所になされています。

ただし,Svelte は 4.0 から 5.0 系列に生まれ変わろうとしている最中で現在進行形で言語仕様がガラッと変わりますので,今の時期に始めるなら Svelte5 をやっておいたほうが確実。

Svelte5 は React のいいところをまねしており,$state(React の useState フック), $effect(React の useEffect フック)などかなり似た仕様になってきました。これら Rune と呼ばれるリアクティブな状態管理の仕組みは個人的には分かりやすくて使いやすいと思っています。

部分的には React よりも少ないコードで動的なウェブアプリを作れるので重宝します。

ただし,Svelte を扱っている人がまだ少ないだけあって,サードパーティーのライブラリーの完成度は React に劣ります。更新も一部の優れたプログラマーの個人技みたいになっていて,更新が滞りがち。Svelte が普及すれば飛躍的にライブラリーの数も品質も上がっていくと思います。これからに機体ですね。