Laravelを採用したシステム開発では同時にクライアントサイドのJavaScriptのアーキテクチャも見直しました。

React, Vue.js

まずは、クライアントサイドのフレームワークではReactVue.jsなどが知られていますのでこれを研究しました。

React のチュートリアルを進めていくと、激しくリファクタリングを行う必要があり、開発の負担が重そうに感じました。

Vue.js はあまり複雑ではなく開発しやすそうに思いました。しかしながら、Model と View が分離しているようで分離していないので、シングルモデル - マルチビューが実現しにくいと思いました。

独自フレームワークの作成

そこでレイヤーアーキテクチャーによって Model と View を分離するフレームワークを新たに作成しました。React や Vue.js が実現している1文字入力するたびに反応する Reactive な機能は実現できていませんが、当方の開発範囲ではフィールド毎に反応する機能があればよいと考えています。

いくつか割り切った部分があります。

現在では要件としてChromeに動作を限定できる場合が多く、ECMAScript 6を前提として記述しました。また、jqueryの使用をやめました。

サーバーとのデータ交換は JSON で行うと限定し、FORMによる GET, POST をサポートする機能を実装しませんでした。レンダリング対象となるデータは JSON でクライアントに送信し、クライアント側で DOM を構築するようにしました。

バインディングによる Model と View の連係

Model と View との連係は双方向データバインディングによっています。

次のようなコードを HTML に埋め込むことで、バインディングを指定しています。

<div data-node="order">
  <dl>
    <dt>注文番号</dt>
    <dd data-property="orderNo" data-input-type="static"></dd>
  </dl>
  <dl>
    <dt>顧客名</dt>
    <dd><input type="text" data-property="customerName" data-uneditable-behavior="disabled"/>
    <span class="error-message" data-property-error="customerName"></span></dd>
  </dl>
</div>

上記コードでdata-nodeはプロパティの親となるエンティティや埋め込みバリューを指定し、data-propertyはプロパティを指定しています。

算出プロパティ

Vue.js を調べるうちに 算出プロパティ という機能に興味を持ちました。 この機能は計算式を書いておけば必要なときに計算式が自動的に実行されるものです。

算出プロパティはangularJSで既に実現されていたようです。

以前に当方も独自に同様の機能を作成したことがあるのですが、計算式と合わせて計算のトリガーとなるプロパティ名を設定しなければならないもので、あまり便利とは言えませんでした。 一方 Vue.js の算出プロパティでは計算式さえ設定すればよく、非常に便利なものでした。仕組みを調べると、計算式を評価するたびに依存元を分析・記録するようになっていました。 パフォーマンス的な点が心配になりましたが、そもそも依存元の値に変化がなければ計算式を評価しないので問題は少ないだろうと結論付けています。

早速、同様の機能を当方の Model にも取り入れました。採用してみると大変便利で依存性のことをほとんど意識しなくてよく、宣言的に計算式を書きさえすればよくなりました。また、オブジェクトの境界を越えて依存性をたどってくれる点も便利です。

算出プロパティ実装時に工夫した点はリストのようにメソッド呼び出しによって内部の値を変えるケースにも対応したことです。例えば、明細を集計した金額を表示するといったケースにも対応できます。

次のように記述できます。※コピペで使えるものではありません。

export class Order extends Data.ObservableObject {
    /**
     * 初期化します。
     */
    initialize() {
        const totalAmountExpression = () => {
            return Enumerable.from(this.orderDetails).sum(item => item.amount)
        } //合計金額
        this.defineProperty('totalAmount', DataType.Number, totalAmountExpression) //合計金額
        this.defineInternalChangeProperty('orderDetails',
            new Data.ListChangeDetector(),
            new Data.ObservableList({createItem: () => new OrderDetails(this)})) //受注明細リスト
    }

今回の開発とは関係がありませんが、算出プロパティを C# にも移植し、便利に使っております。

仮想 DOM

React、Vue.js では仮想 DOM によってレンダリングの負担を軽減しています。当方の開発したフレームワークでは仮想 DOM を採用していません。Model が変化すればバインディングによって同時に DOM も変化します。この点でパフォーマンスの心配がありました。

動作させてみると DOM が変更されたらすぐにブラウザーがレンダリングするのではなく、一連のスクリプトが終了してからレンダリングしているようです。そのため、仮想 DOM なしでも許容できるものと考えています。

開発したフレームワークを採用した結果

開発したフレームワークを採用した結果、下記のメリットを感じました。

サイト内関連リンク