こんにちは同僚。
今日、JavaScriptフレームワークのような多面的で物議を醸すトピックに触れたかった。 過去数か月にわたって、出版社はAngular.jsとKnockout.jsの両方で書籍を出版する見通しについて繰り返し議論してきましたが、昨年Backbone.jsで書籍を
出版しました 。 次の資料は、さまざまなJavaScriptフレームワークの長所と短所を理解するのに役立つことを目的としています。 おそらく、記事を読んだ後、読者はこれらのフレームワークが非常に似ているかどうか、そしてこのレビューで言及されている技術のいくつかをさらに研究したいかどうかについての質問に答えるのが簡単になるでしょう。 これらのフレームワークに関する新しい本が必要かどうか、もしそうなら、どの本についてのあなたの考えを共有するようお願いします(コメントで話す、本へのリンクを提供することをheしないでください)。
MV *カテゴリからJavaScriptフレームワークをすばやく学習するには、フレームワークを一連の可能性として提示できることが重要です。 MV *アプリケーションの主な機能は、ルーティング、データバインディング、テンプレート/ビュー、モデル、およびデータストレージです。 この出版物では、これらの機能について説明し、2つまたは3つのフレームワークでこれらの各機能の実装コードを示します。 まず、各フレームワークが最適化する特定のタスクのソリューションを具体的に分析します。その後、これらすべてのフレームワークの違いよりも類似点が多いことに気づくことを願っています。 ほとんどのフレームワークでは、他のフレームワークで正当化された機能が積極的に取り入れられていることがわかります。
私は人の違いよりも類似点を見たいと思っています-Isabel Allende
コードの各行を解析する必要はありません。 とりあえず、これらのフレームワークがどのようなものに似ているのか、プロジェクトでどのような問題を解決できるのかを評価してみてください。
ルーティング少なくとも、ルーティングはURLをアクションにマッピングしますが、ビュー内の状態間の遷移を制御するために完全な「状態マシン」パターンを実装することになる場合があります。
たとえば、Rails、CodeIgniter、CakePHP、ASP.NET MVC、Spring MVC、JSF、STRUTS、GrailsなどのサーバーMVCフレームワークでルーターを使用したことがある場合、JavaScript MV *ルーターを同様のものと見なすことができます。エンティティですが、JavaScriptを使用してクライアント上で実行し、サーバーコードへの道を開きます。サーバーコードは、PHP、Ruby、Java、C#などで記述できます。
疑問が生じる可能性があります:古いブラウザーでは、これらはすべてどのように機能しますか このメカニズムはデフォルトで機能し、ハッシュタグの後のURLにあるすべての情報をルートとして使用します。 確かに、プッシュ状態のサポートがHTMLで構成されている場合(ほとんどのフレームワークでは、これは1行のコードで行われます)、ルートに一致するハッシュのないURLはクライアントでインターセプトされ、JavaScriptコードも実行されます。
プッシュ状態はデフォルトではサポートされていません。ブックマークを介して直接リクエストされたURLまたは電子メールメッセージで送信されたURLはエンドユーザーのコンピューターで動作するにもかかわらず、検索エンジンはJavaScriptでの動作にあまり適していません つまり、ロボットはJavaScriptコードを実行しない可能性があるため、ページの実際のコンテンツを表示できません。
このような場合に通常提供されるソリューションは、サーバー上でPhantomJSまたは別の軽量ブラウザーを起動し、ページとJavaScriptを強制的に読み込み、生成されたマークアップをJavaScriptとともに返すことです。 このメカニズムを構成するには、一生懸命作業する必要があります。そのため、すべてのブラウザーでサポートされているハッシュタグ付きのURLに対してデフォルトで設定されています。
十分な説明は、コードに行きます。
バックボーンの例Backbone.jsの簡単なルーティングの例を次に示します
var HomeView = Backbone.View.extend({ template: '<h1>Home</h1>', initialize: function () { this.render(); }, render: function () { this.$el.html(this.template); } }); var AboutView = Backbone.View.extend({ template: '<h1>About</h1>', initialize: function () { this.render(); }, render: function () { this.$el.html(this.template); } }); var AppRouter = Backbone.Router.extend({ routes: { '': 'homeRoute', 'home': 'homeRoute', 'about': 'aboutRoute', }, homeRoute: function () { var homeView = new HomeView(); $("#content").html(homeView.el); }, aboutRoute: function () { var aboutView = new AboutView(); $("#content").html(aboutView.el); } }); var appRouter = new AppRouter(); Backbone.history.start();
特に、AppRouterオブジェクトに注意してください。 ルートは機能にマップされます。 関数は、DOMモデルのフラグメントを管理するビューオブジェクトを作成し、URLが変更されるとすぐにページに追加します。 Backbone.history.start()関数は、URLの変更のリッスンを開始するようBackboneに指示します。
AngularJSの例以下は、AngularJSの簡単なルーティングの例です。
var routingExample = angular.module('FunnyAnt.Examples.Routing', []); routingExample.controller('HomeController', function ($scope) {}); routingExample.controller('AboutController', function ($scope) {}); routingExample.config(function ($routeProvider) { $routeProvider. when('/home', { templateUrl: 'embedded.home.html', controller: 'HomeController' }). when('/about', { templateUrl: 'embedded.about.html', controller: 'AboutController' }). otherwise({ redirectTo: '/home' }); });
AngularJSの例は、Backboneに非常に似ていますが、ルートがtemplateUrlおよびコントローラークラスにマップされる点が異なります(このコンテキストでは、コントローラーはBackboneビューに似ています)。
エンバーの例
以下は、Emberの簡単なルーティングの例です。
App.Router.map(function() {
繰り返しますが、この例は以前の例と非常に似ていますが、Ember.jsでは、ルーターの「リソース」オブジェクトの最初のパラメーターはrouteNameであり、2番目はURLです。 最初は、この例のaboutページの場合のように、pathパラメーターがオプションであり、多くの場合合意によって設定できると誰かが示唆するまで、この順序付けは私を混乱させました。 さらに、この単純なルーティングの例を実行するにはテンプレートが必要ですが、記事の最後でのみ焦点を当てます。 今のところ、テンプレートが{{outlet}}要素に配置されていることを知るだけで十分です。 Emberルーターは、テンプレートのネストなど、比較的複雑な機能を使用することに注意してください。ただし、ここでは、他のライブラリと機能的に類似していることを示します。
ルーティングするだけほとんどのフレームワークにはルーティングソリューションがありますが、Knockout.jsはデータバインディングに焦点を当てており、ルーティングのみを目的とするJavaScriptライブラリの1つを使用する最善のソリューションを提供します。 この種の最も一般的な特殊ライブラリは、SummyJSとHistoryJSです。 SummyJSは、HTML5のHistory APIをサポートするブラウザー、およびレガシーブラウザー(IE 8以下)のURLハッシュで動作します。 HistoryJSライブラリは小さくなりますが、HTML5のHistory APIのみをサポートします。
データバインディングバインディングを使用すると、ビューで更新するデータモデルを変更したり、ビューに加えられた変更でモデルを自動的に更新したりできます。これには追加のコードは不要です。 単方向データバインディングは、通常、モデルへの変更がビューに伝播されることを意味します。 双方向のデータバインディングでは、ビューに加えられたすべての変更をモデルにすばやく反映する追加の機会があります。 データバインディングにより、開発者が通常記述する必要のある大量のステレオタイプコードを節約できます。 したがって、プログラマは、このアプリケーションに固有の固有のタスクを解決することに集中できます。
AngularJSの例以下は、AngularJSでの双方向データバインディングの簡単な例です。 入力フィールドに入力すると、ウェルカムメッセージの直後に表示されます。
<h1>Simple Data Binding with AngularJS</h1> <br /> <div ng-app> Name: <input type="text" ng-model="name" /> <br /><br /> Welcome to AngularJS {{name}} </div>
KnockoutJSを使用した例KnockoutJSフレームワークは実際にデータバインディングにのみ焦点を当てており、特にディスプレイ管理用のDurandalJSおよびルーティング処理用のHistory.jsまたはSammy.jsとともに、他のフレームワークおよびライブラリと一緒に最適なソリューションの一部として使用されます。 KnockoutJSでのデータバインディングの例を次に示します。
バックボーンの例Backboneには自動データバインディングはありませんが、手動で行うことができます。 私の経験では、モデルに加えられた変更に応じてビューが更新される単方向データバインディングは非常に便利です。 ビューからモデルへの単方向データバインディングの実際のケースはあまり一般的ではありません(たとえば、ユーザーがテキストエディターで入力したテキストのライブプレビューを実装する場合)。 実際、ビューからモデルへのリンクは有用ですが、多くの場合、ビューを変更する理由はユーザーが入力した情報であり、これらの変更がモデルに加えられる前に他のロジックを実装する時間が必要です。 たとえば、入力の検証やリストのフィルタリングについて話すことができますが、これには現在のフィルターを覚えておく必要もあります。 以下は、両方のバインディングオプションを実装する簡単なコード例です。
var MessageView = Backbone.View.extend({ template: _.template($('#message-template').html()), events: { 'keyup #name': 'updateModel' }, updateModel: function(event) { this.model.set({name:$("#name").val()}); }, initialize: function() { this.listenTo(this.model, "change", this.render); this.render(); }, render: function() { this.$('#message').html(this.template(this.model.toJSON())); return this; } }); var person = new Backbone.Model({name:''}); messageView = new MessageView({el: $('#message-container') ,model: person});
そのため、モデルで発生する変更イベントをリッスンし、ビューのrenderプロパティを呼び出して、モデルがビューを更新するようにします。 同様に、モデルを入力および変更するときにキーアップイベントをリッスンし、jQueryを使用して入力から目的の値を取得し、モデルに設定して、ビューがモデルを更新するようにします。 この例は、データバインディングが機能するために必要なコードの量の印象を与えるのに役立ちます。 また、Backboneでのデータバインディングをサポートする多数のプラグインの存在に言及する必要があります。
エンバーの例Emberのデータバインディングは次のようになります。
App = Ember.Application.create({});
Emberフレームワークでは、使い慣れたハンドルバーオブジェクトを使用してテンプレートを作成しますが、ほとんどのフォームにある入力フィールドへのバインドを提供する「入力アシスタント」も含まれています。 この例では、中括弧{{入力時<角度を置換し、「名前」プロパティに引用符がないため、アシスタントはアタッチする必要があることを認識しています。 Emberフレームワークは、AngularJSと同様に、最小限のコードで双方向バインディングを提供します。
テンプレート/ビューテンプレートはHTMLページ全体に対応できますが、多くの場合、テンプレートは小さなフラグメントであり、将来の動的データのプレースホルダーに既製のバインディングを持つプレースホルダー式が含まれます。 それらにはロジックが含まれていない可能性があります-表現にプログラムロジックを含めるべきではない、または最小限に抑えることができるという哲学があります。 一部のテンプレートは、JavaScriptコードを直接埋め込むことができます。 テンプレートはDOMモデルに基づいており、DOMを使用して動的データをオンザフライで挿入したり、HTMLの文字列解釈に使用したりできます。HTML文字列は動的要素をすばやく置き換えるために使用されます。
テンプレートライブラリ標準化に役立つライブラリの中で、最も人気のあるHandlebar.jsという名前を付ける必要があります。 Handlebar.jsはBackbone.jsでよく使用され、Ember.jsの一部です(Ember.jsで他のテンプレートライブラリを使用できますが、この場合、パフォーマンスが大幅に低下するため、お勧めしません)。 もう1つの人気のあるテンプレートエンジンは、Mustache.jsと呼ばれます。 UnderscoreJSは、Backbone.jsフレームワークに関連付けられた補助ライブラリであり、特にテンプレートライブラリ、および関数型プログラミングのための多くの資料が含まれています。 この分野で注目を集めているのがDust.jsライブラリで、LinkedInが最近アプリケーションをプログラムするために採用しました。 さらに、Handlebar.jsおよびMustache.jsライブラリは、開発者がクライアントでテンプレートを作成するだけでよいことに気づいたときに、jQueryおよびAJAXのみ(JavaScript MVCなし)で記述されたアプリケーションでよく使用されます。 jQueryの一部として、独自のテンプレートライブラリが開発されましたが、現時点では既に望ましくないと認識されているため、使用することはお勧めしません。
アンダースコアの起源BackboneJSの作成者であるJeremy AshkenasがBackboneJSから元のバージョンからUnderscoreJSを抽出したときにすべてが始まりました。これにより、BackboneJSから分離して便利な補助機能を使用できます。
いくつかの例を見てみましょう。
AngularJSの例以下は、AngularJSの簡単なテンプレートの例です。
var templatesExample = angular.module('FunnyAnt.Examples.Templates', []); templatesExample.controller('HomeController', function ($scope) { $scope.greeting = "Welcome to the application."; }); templatesExample.controller('AboutController', function ($scope) { $scope.content = "As a software developer, I've always loved to build things..."; }); templatesExample.config(function ($routeProvider) { $routeProvider. when('/home', { templateUrl: 'embedded.home.html', controller: 'HomeController' }). when('/about', { templateUrl: 'embedded.about.html', controller: 'AboutController' }). otherwise({ redirectTo: '/home' }); });
おそらく、このコードが以前のルーティング例のように見えることに気づいたでしょう。 ここでは、アプリケーションでテンプレートがどのように役立つかを理解できるように、データバインディングのみが追加されています。 テンプレートをメインHTMLファイルのスクリプトタグに含めると、サンプルが透明になり、jsfiddle.netで使用できるようになりますが、テンプレートは、Angular.jsの任意の表現の外部に配置できます。 後者の場合、$ routeProviderの構成段階で、templateUrlプロパティに有効なファイルパスを指定するだけです。
大規模アプリケーション(パフォーマンスが非常に重要)でテンプレートを処理する好ましい方法は、AngularJSテンプレートをコンパイル時にAngular $ templateCacheの後続の登録にリンクすることであり、アセンブリタスクは次のように定式化されることに言及する価値があります。
エンバーの例以下は、Emberでテンプレートを使用する例です。
App = Ember.Application.create({}); App.Router.map(function() {
Emberルートは、表示するモデル、つまりURLではなくモデルをテンプレートに伝えるオブジェクトです。 これは、テンプレートとリソース(URL)の最も単純なコントローラーと見なすことができ、その主なタスクはモデルをロードすることです。 これで十分ではなく、アプリケーションの状態も保存する必要がある場合は、完全なコントローラーが必要です。
EmberおよびAngularJSフレームワークは、それぞれ文字列モデルとDOMモデルという異なる方法でテンプレートを操作します。 ただし、どちらの場合も同じ問題は解決されます。 さらに、開発者によって提供されるインターフェイスとコントラクトは非常に似ています。 詳細をもう少し詳しく見ていくと、両方のフレームワークがデータバインディングとスクリプトタグのハンドルバー構文、またはテンプレートを保存するための個別のファイルを使用していることに注意してください。
バックボーンの例次に、Backboneでテンプレートを操作する簡単な例を見てみましょう。
var HomeView = Backbone.View.extend({ template: _.template($("#home-template").html()), initialize: function () { this.render(); }, render: function () { this.$el.html(this.template({greeting:"Welcome to Backbone!"})); } }); var AboutView = Backbone.View.extend({ template: _.template($("#about-template").html()), initialize: function () { this.render(); }, render: function () { this.$el.html(this.template({content:"As a software developer, I've always loved to build things..."})); } }); var AppRouter = Backbone.Router.extend({ routes: { '': 'homeRoute', 'home': 'homeRoute', 'about': 'aboutRoute', }, homeRoute: function () { var homeView = new HomeView(); $("#content").html(homeView.el); }, aboutRoute: function () { var aboutView = new AboutView(); $("#content").html(aboutView.el); } }); var appRouter = new AppRouter(); Backbone.history.start();
これは修正されたルーティングの例です。 この場合、HTMLはビューオブジェクトのテンプレートプロパティにハードコーディングされていません。 マークアップは、id属性を持つスクリプトタグで囲まれたHTMLページ上にあります(ブラウザーは、タイプが不明なスクリプトタグ(特にテキスト/テンプレート)を無視するため、テンプレート自体は表示されません)。 テンプレート(HTMLフラグメント)を取得するには、jQueryセレクターを使用します。jQueryセレクターを使用して、スクリプトタグのIDで要素を見つけ、そこからinnerHTMLを選択し、このHTMLをプレゼンテーションオブジェクトのテンプレートプロパティに割り当てます(これは単なる文字列です)。
テンプレートが通常のhtml行のように処理されるこのアプローチを使用すると、Backboneアプリケーションの完全に新しいライブラリを既存のライブラリに置き換える方法を簡単に想像できます。 テンプレートプロパティの実装をわずかに変更するだけです。
たとえば、アンダースコアの代わりにHandlebarsテンプレートライブラリを使用するには、次のようにビューのテンプレートプロパティを更新します。
template: Handlebars.compile( $("#home-template").html() ),
...次に、テンプレートで使用されるバインディング構文を更新します。たとえば、
{{greeting}}
アプリケーションで1つのテンプレートライブラリを別のテンプレートライブラリに置き換えるために必要なことはそれだけです。
これは、handlebar.jsライブラリがテンプレートに使用される完全な例です。
var HomeView = Backbone.View.extend({ template: Handlebars.compile( $("#home-template").html() ), initialize: function () { this.render(); }, render: function () { this.$el.html(this.template({greeting:"Welcome to Backbone!"})); } }); var AboutView = Backbone.View.extend({ template: Handlebars.compile( $("#about-template").html() ), initialize: function () { this.render(); }, render: function () { this.$el.html(this.template({content:"As a software developer, I've always loved to build things..."})); } }); var AppRouter = Backbone.Router.extend({ routes: { '': 'homeRoute', 'home': 'homeRoute', 'about': 'aboutRoute', }, homeRoute: function () { var homeView = new HomeView(); $("#content").html(homeView.el); }, aboutRoute: function () { var aboutView = new AboutView(); $("#content").html(aboutView.el); } }); var appRouter = new AppRouter(); Backbone.history.start();
この場合、いくつかの概念を他の概念と明確に区別することは容易ではありません。 テンプレートについて話すと、多くの専門家がテンプレートライブラリのパフォーマンスを深く掘り下げます。これは、上記で説明したデータバインディングに大きく関連しますが、テンプレートの読み込みにも適用されます(さらにJavaScriptでの予備コンパイルなど)。
こうした混乱に加えて、上記のテンプレートは単なるHTMLであり、MVCを扱う多くのサーバー側開発者はHTMLをビューまたはパーシャルに関連付けます。 ただし、Backboneのビューはコードであり、HTMLフラグメントを制御する特別なJavaScriptクラスです。 通常、Backboneビューには、通常ビューオブジェクトの外部にあるテンプレートへの参照を除き、マークアップはほとんどありません。
モデルモデルは、クライアント側のさまざまないわゆるビジネスオブジェクト、ドメインオブジェクト、またはエンティティです。 Backboneの著者であるJeremy Ashkenasの定義によれば、「モデルはJavaScriptアプリケーションのコアであり、インタラクティブデータとそれらをカバーするロジックの重要な部分の両方が含まれています。 変換、検証、計算されたプロパティ、アクセス制御。」 Ashkenasはまた、これらのエンティティとActive Recordのサーバーに含まれるモデルとの間で慎重に注意します。 むしろ、これらのエンティティはクライアント上にあるより狭いコレクションであり、ユーザーインターフェイスを操作するときに役立つ追加のプロパティ(たとえば、カウント(count))を所有しています。
原則として、MV *クライアントフレームワークのモデルの概念は次のとおりです。アプリケーションに関連するデータが配置される中央ノードと、このデータでカプセル化できるすべての種類の動作を作成します。 このようなモデルは、サーバーMVCとjQueryを使用したアーキテクチャに基づいて作成でき、モデルに関連するデータは通常DOMに保存されます。 このようなモデルを作成する目的は、このデータと状態をDOMの外部に取り出し、再利用しやすい共通の場所に配置することです。
バックボーンの例データバインディングが検討された前の例の1つで、モデルに精通しました。 原則として、モデルにはデータが含まれており、DOMの外部に保存されます。また、「変更」などのイベントも生成し、それに応じて多数のビューが反応し、必要に応じてユーザーインターフェイスを更新します。 そのため、真実のソースがありますが、これはユーザーインターフェイスではありません。
var MessageView = Backbone.View.extend({
テンプレート:_.template($( '#message-template')。html())、
イベント:{
「クリック#button」:「updateModel」
}、
updateModel:function(event){
this.model.set({ name: $("#name").val() }); $("#name").html(''); }, initialize: function () { _.bindAll(this, 'render'); this.listenTo(this.model, "change", this.render); }, render: function () { this.$('#message').html(this.template(this.model.toJSON())); return this; } }); var NameView = Backbone.View.extend({ template: _.template($('#name-template').html()), initialize: function () { _.bindAll(this, 'render'); this.listenTo(this.model, "change", this.render); }, render: function () { this.$el.html(this.template(this.model.toJSON())); return this; } }); var Person = Backbone.Model.extend({ defaults: { name: '' } }); var person = new Person(); var messageView = new MessageView({ el: $('#message-container'), model: person }); var nameView = new NameView({ el: $('#name-container'), model: person });
新しいテンプレートと、テンプレートと同じPersonモデルオブジェクトに関連付けられたビューを追加して、上記のデータバインディングの例を少し変更しました。 コードを複雑にしないために上記のPersonモデルを動的に宣言しましたが、今ではBackbone.Model.extend()メソッドへの呼び出しを追加して、再利用可能なモデルのプロトタイプを作成する方法を示しています-古典的な言語のクラスのように。
注:両方のビューは同じPersonモデルオブジェクト(変更イベント)をリッスンし、更新されます。 データソースが1つしかない場合、特定のDOM要素への多数の呼び出しを独自の適切な表現でカプセル化でき、モデルはそれらすべてを処理できます。
上記の例で変更イベントがトリガーされるように、アクセス方法(インストーラーと受信者)がどのように使用されるかに注意してください...この同意はしばしば忘れられます。
AngularJSの例アプリケーションの「状態」に関する真の情報を格納する単一モデルのアイデアはAngularJSに存在しますが、AngularJSではモデルとしてPOJOオブジェクトを使用でき、「内部」で$スコープオブジェクトに付加され、ビューに宣言的に付加されるプロパティにオブザーバーを追加します「ng-model」ディレクティブを使用します。 次に、これらのオブザーバーは同じモデルに接続されている他のアプリケーションコンポーネントに自動的に通知し、これらのDOM要素は更新方法を「認識」します。 一般的に、ここでは多くの「マジック」が行われていますが、AngularJS開発チームは「ホワイトマジック」であると主張しています。
次に、データバインディングを使用したAngularJSの更新例を示します。 これは、ビューのフラグメントがどのように更新されるかを示しています。
<h1>Simple Data Binding with AngularJS</h1> <br /> <div ng-app> Name: <input type="text" ng-model="person.name" /> <br /><br /> Welcome to AngularJS {{person.name}} <br/> Person: {{person.name}} </div>
AngularJS person , ng-model. , JavaScript.
, : , Backbone; , , AngularJS .
AngularJSAngularJS . -, AJAX , $.ajax jQuery, $http. , RESTful-, AngularJS $resource, RESTful- .
$http app.factory('myService', function($http) { return { getFooOldSchool: function(callback) { $http.get('foo.json').success(callback); } } }); app.controller('MainCtrl', function($scope, myService) { myService.getFooOldSchool(function(data) { $scope.foo = data; }); }); $resource var Todo = $resource('/api/1/todo/:id');
BackboneBackbone , RESTful API, Backbone.sync(). , (URL), save.
var UserModel = Backbone.Model.extend({
urlRoot: '/user',
defaults: {
name: '',
email: ''
}
}); var user = new Usermodel();
EmberEmber Ember Data, , / . , ORM ActiveRecord, JavaScript . Ember Core v1.0, , Ember $.ajax jQuery, AngularJS $http.
MV*- JavaScript, , . , , , . , – , , .