完璧なJavaScriptフレームワークを求めて Part 2

前編はこちらです。

テンプレート

私たちはテンプレートエンジンを多用しますが、それはデータとHTMLマークアップを区別する必要があるからです。昨今のフレームワークのテンプレート処理の方法について、最もポピュラーな手法をいくつか以下に挙げます。

テンプレートが<script>で定義されるケース

<script type="text/x-handlebars">
    Hello, <strong> </strong>!
</script>

テンプレートがHTML内に存在しており、よく使われる手法です。見た目がナチュラルで、HTMLが自然にタグを内包しているので理にかなっています。ブラウザが<script>要素のコンテンツをレンダリングしないため、ページレイアウトが崩れません。

テンプレートがAjaxを用いてロードされるケース

Backbone.View.extend({
    'template': 'my-view-template',
    'render': function() {
        $.get('/templates/' + this.template + '.html', function(template) {
            var html = $(template).tmpl();
        });
    }
});

コードは外部HTMLファイルに記述され、<script>タグを追加しなくても済むようになっています。しかしHTTPリクエストの回数が増えてしまうので、(少なくともHTTP2がもっと広く使われるようになるまでは)あまり適切とは言えない場合もあるでしょう。

このテンプレートはページのマークアップの一部であり、フレームワークがDOMツリーからテンプレートを読み出します。そして既に生成されたHTMLに依存します。追加のHTTPリクエストや、新規のファイル作成、また<script>要素の追加は必要ありません。

テンプレートがJavaScriptの一部であるケース

var HelloMessage = React.createClass({
    render: function() {
        // Note: the following line is invalid JavaScript.
        return <div>Hello {this.props.name}</div>;
    }
});

Reactによって導入されたこの手法では、JavaScriptの無効なパートを有効なコードに変換する独自のパーサを利用しています。

テンプレートがHTMLではない場合

HTMLを直接使わないフレームワークもあります。この場合はJSONやYAMLの形式でテンプレートを使います。

テンプレートについての最終的な考察

今後の展望はどうなるのでしょうか。将来のフレームワークでは、私たちはデータのこととマークアップのことだけを考えれば済むようになり、その他のことは一切考えなくてもよくなるべきでしょう。HTMLストリングのロードや、特別な関数にデータを渡すなどということに、わずらわされたくないですよね。変数に値を当てはめ、DOMを更新できればいいのです。人気のあるツーウェイ(TwoWay)データバインディングは、単なる一機能ではなく必須のコア機能であるべきです。

実際のところ、AngularJSは望ましい振る舞いに近いものだと思います。ページのコンテンツからテンプレートを読み込み、素晴らしいデータバインディングを実装しています。しかし、まだ理想形とは言えません。ブラウザがHTMLをレンダリングする時、たまに画面がちらつくという現象がありますが、この時AngularJSのブートメカニズムはまだスタートしていません。また、AngularJSはダーティチェックを使って変更の調査をしますが、この手法は時として非常に高くつきます。いつかObject.observeが全てのブラウザ上でサポートされ、データバインディングが向上するといいなと思います。

開発者は全て、遅かれ早かれ、動的テンプレートの問題にぶつかります。確実に、アプリケーションの一部はブートストラッッピングの後に登場することになりますが、フレームワークはそれに簡単に対処できなくてはなりません。Ajaxリクエストを考えるのではなく、プロセスを同期させるように見せるAPIと連動すべきなのです。

モジュラリティ

私は必要に応じて機能をオン、オフするというアイデアが気に入っています。使用しない機能がコードベースにあるのはナンセンスです。フレームワークは、必要なモジュールだけを含めたバージョンを生成するビルダを備えるべきでしょう。例えばYUIにはコンフィギュレータがあり、必要なモジュールを選択して、すぐに縮小されたJavaScriptを使用可能な状態にできるのです。

現在既に、コアと呼ばれる機能を備えているフレームワークがあり、多くのプラグイン(またはモジュール)を追加で使うことができます。でも、もっと改善できるはずで、必要な機能を選択するプロセスでファイルをダウンロードする必要はないでしょう。ページから手動で選択するのではなく、フレームワークのコードの一部であるべきです。

ちゃんと設定機能を確保したら、あとは拡張性を備えれば完璧な環境と言えるでしょう。そして自分たちでモジュールを書き、他の開発者と共有できるようにした方がいいでしょう。つまり、モジュールを作成しやすい環境を整備すべきだと思います。適切な開発者環境の存在を抜きにしては、強固なコミュニティを作り上げることはできません。

パブリックAPI

現在大部分のフレームワークが提供しているのは、アプリケーションのコア機能のAPIです。しかしベンダがユーザの必要性を認めた部分については、そのパーツへのアクセスが可能になっています。ここがプログラミングの腕の見せどころになり得るわけです。私たちは何かを成し遂げたいと思っても、そのための適正な道具を持っていません。そこで、少し見苦しい手を使ったワークアラウンドでフレームワークに手を加えるのです。以下の例をご覧ください。

var Framework = function() {
    var router = new Router();
    var factory = new ControllerFactory();
    return {
        'addRoute': function(path) {
            var rData = router.resolve(path);
            var controller = factory.get(rData.controllerType);
            router.register(path, controller.handler);
            return controller;
        }
    }
};
var AboutCtrl = Framework.addRoute('/about');

ビルトインルータを装備したフレームワークがありますね。パスを定義すると、コントローラが自動的に初期化されます。ユーザが正しいURLへ行くと、ルータはコントローラのhandlerメソッドを開始します。それはいいのですが、ではURLマッチングのレスポンスとして単なるJavaScriptの関数を実行したい時はどうでしょう? 何か理由があって、新たなコントローラを作成したくない場合です。今のAPIでは、それは不可能です。そこで、例えば以下のような設計が使えるのではないでしょうか。

var Framework = function() {
    var router = new Router();
    var factory = new ControllerFactory();
    return {
        'createController': function(path) {
            var rData = router.resolve(path);
            return factory.get(rData.controllerType);
        }
        'addRoute': function(path, handler) {
            router.register(path, handler);
        }
    }
}
var AboutCtrl = Framework.createController({ 'type': 'about' });
Framework.addRoute('/about', AboutCtrl.handler);

ルータは、表面上は見えません。しかしこれで私たちは、コントローラの作成と経路の登録という2つのプロセスを制御できるようになります。もちろん、ここに提案した設計は私たちの個々のユースケースにマッチします。手動でコントローラを作成しなければならないと思うと、かなり難しいと感じてしまうかもしれませんね。APIを設計する時に念頭に置くべきは、単一責任の原則です。そして1つのことを正しく行うという意識を持つことが大切です。最近、機能を分散化しているフレームワークが増えているように思います。複雑なメソッドをより小さなパーツに切り分けているのです。これはよい兆候でしょう。将来的にこの傾向がさらに進めばいいと私は思っています。

テスト容易性

皆さんにコードのテストをするよう説得する必要はないでしょうね。重要なのは、テストを書くことだけでなく、テストしやすいコードを書くことです。これは時に非常に難しく、時間がかかることもあります。何かのテストが抜けると、そのコードのサイズに関わらず、そこからアプリケーションのバグが生じやすくなります。クライアント側のJavaScriptの場合は特にそうです。複数のブラウザ、複数のオペレーティングシステム、新たなスペック、新たな機能、そしてポリフィルなど、テスト駆動開発を用いるべき要因は山ほどあります。

テストを行うことで得られるものは他にもあります。フレームワーク(アプリケーション)が現在きちんと機能することを保証するだけではありません。明日もそれ以降も、確実に機能することを確認しているのです。コードベースに新たに追加した機能があれば、そのためのテストを書きますね。重要なのは、その新しいテストをパスするだけでなく、以前のテストもパスすることです。これでやっと、新たなコードが何も壊していないことが保証できるのです。  
テストのために規格化されたツールやメソッドが、あればいいのにと思います。全てのフレームワークにおいて、1つのツールとテストだけを使うことができたらいいのですけどね。何らかの方法でテストを開発プロセスに統合できれば、更に良いでしょう。Travisのようなサービスは更に注目に値します。というのも、変更を行うプログラマのためだけでなく、他のコントリビュータの指針としても働くからです。

私はまだPHPを使っていますが、例えばWordPressといったフレームワークを取り扱わなければなりませんでした。また、多くの人たちから、どのようにアプリのテストをするのか、どんなテストフレームワークを使っているのか、どのようにテストを行っているのか、ユニットテストも行っているのかと聞かれます。正直に告白すると、ユニットテストは行っていません。ユニットを持ち合わせていないからです。同じことはいくつかのJavaScriptのフレームワークにも当てはまります。ユニットを持たないので、フレームワークのある部分をテストすることは非常に難しいのです。開発者は、同時に、この方向性を考えるべきです。そうです、コードがスマートかつエレガントで有効というだけでなく、テストの容易性も必要なのです。

ドキュメンテーション

私はどんなプロジェクトも良いドキュメンテーションなしでは遅かれ早かれダメになると思っています。毎週のように、多くのフレームワークやライブラリが世に送り出されていますが、開発者が最初に見るのがドキュメンテーションです。あるツールが何をするのか、どういった特徴を持つのかをリサーチすることに時間をかけたいと思う人はいません。主な機能の羅列だけでは、不十分なのです。とりわけ、大きなフレームワークにとっては特に大事です。

私は分かりやすいドキュメンテーションを3つのパートに分けました。

  • 機能の紹介のセクション ドキュメンテーションはユーザに指針を指し示すもので、正しく説明するものでなければいけません。フレームワークがどんなに素晴らしく優れていようと関係なく、優れた説明であることが求められるのです。ビデオクリップを観ることを好む人もいれば、記事を読むことを好む人もいます。両方のケースで、ベンダは非常に基本的なことから、フレームワークの最先端に至るまで開発者を導かなければいけません。
  • APIドキュメント これは通常含まれています。フレームワークの全パブリックメソッド、パラメータ、何を返すか、そして使用例などについてのリストです。
  • どのように動くかのセクション 通常、このセクションは欠けています。誰かがフレームワークの構造を説明しようとするなら素晴らしいことでしょう。コア機能や関係性を説明するシンプルな図解なども役立つでしょう。これでコードの透明性が増します。カスタムモディフィケーションを作りたい開発者の手助けにもなることでしょう。

まとめ

もちろん、将来を予測することは難しいことです。しかし希望を持つことはできます。そしてJavaScriptフレームワークに何を期待し、何が必要なのかを話し合うことが重要です。もしフィードバックや、シェアしたい考えがあったら、このハッシュタグ#jsframeworksを使ってつぶやいてください。