AureliaとAngular2—コードの比較

公開: 2022-03-11

古き良きJavaScriptAngular1の子孫であるAngularとAureliaは激しい競争相手であり、ほぼ同時に開発およびリリースされ、同様の哲学を持っていますが、いくつかの重要な点で異なります。 この記事では、機能とコードの違いを並べて比較します。

簡単に言えば、AureliaはDurandalとCaliburnの作成者として知られるRobEisenbergによって作成されました。 彼はGoogleでAngular2チームに取り組んでいましたが、最新のフレームワークがどのように見えるべきかについての彼の見解が彼らの見解と異なっていたときに、2014年に去りました。

類似点は、より技術的なレベルでも維持されます。テンプレートとそれに関連付けられたコンポーネント(またはカスタム要素)は、AngularアプリとAureliaアプリの両方のコアであり、どちらもルートコンポーネント(つまりアプリ)が必要です。 さらに、AngularとAureliaはどちらも、コンポーネントの構成にデコレータを多用しています。 各コンポーネントには、フックできる固定のライフサイクルがあります。

では、AureliaとAngular 2の違いは何ですか?

Rob Eisenbergによると、主な違いはコードにあります。Aureliaは邪魔になりません。 Aureliaアプリを開発するとき(構成後)、ES6またはTypeScriptで記述します。テンプレートは、特にAngularと比較した場合、完全に正常なHTMLのように見えます。 Aureliaは設定より規約であり、95%の場合、デフォルトの規約(テンプレートの名前付け、要素の名前付けなど)を使用しても問題ありませんが、Angularでは基本的にすべての構成を提供する必要があります。

Aureliaは、HTMLタグに関して大文字と小文字を区別しないという理由だけで、より標準に準拠していると見なされますが、Angular2は大文字と小文字を区別します。 これは、Angular 2がブラウザーのHTMLパーサーに依存できないため、独自に作成したことを意味します。

SPAフレームワークを選択する際に考慮すべきもう1つの要素は、それらを取り巻くコミュニティ(エコシステム)です。 AngularとAureliaはどちらもすべての基本(ルーター、テンプレートエンジン、検証など)を備えており、ネイティブモーダルを取得したり、サードパーティのライブラリを使用したりするのは簡単ですが、Angularがより大きなコミュニティとより大きな開発を行っているのは当然です。チーム。

さらに、両方のフレームワークはオープンソースですが、Angularは主にGoogleによって開発されており、商用化を目的としていません。コアチームを採用しているDurandal、Inc.は、コンサルティングとトレーニングを通じてEmber.jsの収益化モデルに従っています。

AureliaとAngular:コードの比較

各フレームワークの背後にある哲学を強調する最も注目すべき機能のいくつかを見てみましょう。

AngularとAureliaのシードプロジェクトのクローンを作成すると、ES6 Aureliaアプリ(Jspm / System.js、Webpack、RequireJSをES6またはTypeScriptと組み合わせて使用​​できます)とTypeScript Angularアプリ(WebPack)がそれぞれ用意されます。

レッツ・ロール。

データバインディング

作業例を並べて比較する前に、AureliaとAngular 2の構文上の違い、つまりコントローラーからビューに値をバインドする主要な機能を確認する必要があります。 Angular 1は、すべてに「ダーティチェック」を使用しました。これは、スコープの変更をスキャンする方法です。 これは、当然のことながら、多くのパフォーマンスの問題を引き起こしました。 Angular2もAureliaもその道をたどりませんでした。 代わりに、イベントバインディングを使用します。

Angular2でのデータバインディング

Angularでは、次のように、データを角かっこでバインドし、括弧を使用してイベントをバインドします。

 <element [property]="value"></a> <element (someEvent)="eventHandler($event)"></a>

双方向バインディング(アプリケーションデータの変更をビューに反映させたい場合、またはその逆の場合)は、角かっこと括弧の両方を組み合わせたものです。 したがって、双方向バウンド入力の場合、これは問題なく機能します。

 <input type="text" [(ngModel)]="text"> {{text}}

つまり、括弧はイベントを表し、角括弧は入力にプッシュされる値を表します。

Angularチームは、バインディングの方向を分離する素晴らしい仕事をしました:DOMへ、DOMから、そして双方向。 クラスとスタイルのバインドに関連するシンタックスシュガーもたくさんあります。 たとえば、一方向バインディングの例として、次のスニペットについて考えてみます。

 <div [class.red-container]="isRed"></div> <div [style.width.px]="elementWidth"></div>

しかし、双方向データをコンポーネントにバインドしたい場合はどうでしょうか。 次の基本的な入力設定を検討してください。

 <!-- parent component --> <input type="text" [(ngModel)]="text"> {{ text }} <my-component [(text)]="text"></my-component> import {Component, Input} from '@angular/core'; @Component(/* ... */) export class MyComponent { @Input() text : string; } <!-- child component --> <input [(ngModel)]="text"> Text in child: {{ text }}

ngModelを使用するには、モジュールが@angular/formsからFormsModuleをインポートする必要があることに注意してください。 今、私たちは何か面白いものを持っています。 親入力の値を更新すると、すべての場所で値が変更されますが、子の入力を変更すると、その子にのみ影響します。 親の値を更新する場合は、親に通知するイベントが必要です。 このイベントの命名規則は、次のようにproperty name + 'Change'です。

 import {Component, Input, Output, EventEmitter} from '@angular/core'; @Component(/* ... */) export class MyComponent { @Input() text : string; @Output() textChange = new EventEmitter(); triggerUpdate() { this.textChange.emit(this.text); } }

双方向バインディングは、 ngModelChangeイベントにバインドした直後に正しく機能し始めます。

 <!-- child component --> <input [(ngModel)]="text" (ngModelChange)="triggerUpdate($event)">

バインドされた値が変更されても無視するようにフレームワークに効果的に指示する場合、1回限りのバインドについてはどうでしょうか。

Angular 1では、{{::value}}を使用して1回バインドしました。 Angular 2では、ワンタイムバインディングが複雑になります。ドキュメントには、コンポーネントの構成でchangeDetection: ChangeDetectionStrategy.OnPush属性を使用できると記載されていますが、これにより、すべてのバインディングが1回限りになります。

データバインディング:Aurelia Way

Angular 2とは対照的に、Aureliaのデータとイベントのバインディングは非常に単純です。 Angularのproperty="${value}"のように補間を使用するか、次のバインディングタイプのいずれかを使用できます。

 property.one-time="value" property.one-way="value" property.two-way="value"

名前は一目瞭然です。 さらに、 property.bind="value"があります。これは、バインディングが一方向か双方向かを自己検出するシンタックスシュガーです。 検討:

 <!-- parent--> <template bindable="text"> <input type="text" value.bind="text"/> <child text.two-way="text"></child> </template> <!-- child custom element --> <template bindable="text"> <input type="text" value.bind="text"/> </template>

上記のスニペットでは、 @bindable@Inputの両方が構成可能であるため、バインドされているプロパティの名前などを簡単に変更できます。

イベントはどうですか? Aureliaのイベントにバインドするには、 .trigger.delegateを使用します。 たとえば、子コンポーネントにイベントを発生させるには、次のようにします。

 // child.js this.element.dispatchEvent(new CustomEvent('change', { detail: someDetails }));

次に、親でそれをリッスンします。

 <child change.trigger="myChangeHandler($event)"></child> <!-- or --> <child change.delegate="myChangeHandler($event)"></child>

これら2つの違いは、 .triggerがその特定の要素にイベントハンドラーを作成するのに対し、 .delegatedocumentにリスナーを追加することです。 これによりリソースが節約されますが、バブル以外のイベントでは明らかに機能しません。

AureliaとAngularの基本的な例

バインディングについて説明したので、スケーラブルベクターグラフィック(SVG)をレンダリングする基本的なコンポーネントを作成しましょう。 それは素晴らしいものになるので、私たちはそれをawesome-svgと呼びます。 この演習では、AureliaとAngular 2の基本的な機能と哲学の両方を説明します。この記事のAureliaコード例は、GitHubで入手できます。

AureliaでのSVGRectanglesの例

まず、JavaScriptファイルを作成しましょう。

 // awesome-svg.js import {bindable} from 'aurelia-framework'; export class AwesomeSvgCustomElement { @bindable title; @bindable colors = []; }

次にHTMLについて説明します。

Aureliaでは、注釈@ template、 @inlineView @template 、または@noViewを使用してテンプレートを指定(またはインラインテンプレートを使用)できますが、箱から出して、 .jsと同じ名前の.htmlファイルを検索します。ファイル。 カスタム要素の名前についても同じことが言えます。 @customElement('awesome-svg')で設定できますが、そうでない場合、Aureliaはタイトルをダッシュ​​ケースに変換して一致するものを探します。

特に指定しなかったため、要素はawesome-svgと呼ばれ、Aureliaは同じディレクトリでjsファイル(つまりawesome-svg.html )と同じ名前のテンプレートを検索します。

 <!-- awesome-svg.html --> <template> <h1>${title}</h1> <svg> <rect repeat.for="color of colors" fill.bind="color" x.bind="$index * 100" y="0" width="50" height="50"></rect> </svg> </template>

<template>タグに注意してください。 すべてのテンプレートは<template>タグでラップする必要があります。 また、ES6の場合と同じようにand the string interpolation ${title}`を使用することも注目に値します。

コンポーネントを使用するには、 <require from="path/to/awesome-svg"></require>を使用してテンプレートにインポートするか、アプリ全体で使用する場合は、フレームワークのconfigure関数でリソースをグローバル化する必要があります。 aurelia.use.globalResources('path/to/awesome-svg'); 、これはawesome-svgコンポーネントを一度だけインポートします。

[これらのいずれも行わない場合、 <awesome-svg></awesome-svg>は他のHTMLタグと同じように扱われ、エラーは発生しません。]

コンポーネントは次のように表示できます。

 <awesome-svg colors.bind="['#ff0000', '#00ff00', '#0000ff']"></awesome-svg>

これにより、3つの長方形のセットがレンダリングされます。

AureliaでのSVGRectanglesの例

Angular2でのSVGRectanglesの例

次に、GitHubでも入手可能なAngular2で同じ例を実行してみましょう。 Angular 2では、テンプレートと要素名の両方を指定する必要があります。

 // awesome-svg.component.ts import {Component, Input} from '@angular/core'; @Component({ selector: 'awesome-svg', templateUrl: './awesome-svg.component.html' }) export class AwesomeSvgComponent { @Input() title : string; @Input() colors : string[] = [] }

ビューは物事が少し複雑になるところです。 まず、Angularは不明なHTMLタグをブラウザと同じようにサイレントに処理します。つまり、 my-own-tagの行に沿った何かが不明な要素であるというエラーが発生します。 バインドするすべてのプロパティに対して同じことを行うため、コードのどこかにタイプミスがあると、アプリがクラッシュするため、大きな注目を集めることになります。 いいですね。 はい、アプリを壊した場合はすぐに気付くでしょうし、いいえ、これは悪い形だからです。

このスニペットを検討してください。これは、バインディング構文の観点からは完全に問題ありません。

 <svg> <rect [fill]="color"></rect> </svg>

正常に読み取られても、「「:svg:rect」の既知のプロパティではないため、「fill」にバインドできません」などのエラーが発生します。 これを修正するには、代わりに構文[attr.fill]="color"を使用する必要があります。 また、 <svg/>: <svg:rect>内の子要素で名前空間を指定して、これをHTMLとして扱わないようにする必要があることをAngularに通知する必要があることにも注意してください。 スニペットを展開してみましょう:

 <!-- awesome-svg.component.html--> <h1>{{ title }}</h1> <svg> <rect *ngFor="let color of colors; let i = index" [attr.fill]="color" [attr.x]="i * 100" y="0" width="50" height="50" ></rect> </svg>

そこに行きます。 次に、モジュール構成にインポートします。

 @NgModule({ declarations: [ AwesomeSvgComponent ] //... })

これで、次のように、コンポーネントをこのモジュール全体で使用できます。

 <awesome-svg [colors]="['#ff0000', '#00ff00', '#0000ff']" title="Rectangles"></awesome-svg> 

AngularJS2でのSVGRectanglesの例

カスタム要素

ここで、長方形コードを独自のロジックを持つカスタムコンポーネントにしたいとします。

カスタム要素:Angular 2 Way

Angular 2は、定義されたセレクターに一致するものによってコンポーネントをレンダリングするため、次のようにカスタムコンポーネントを定義するのは非常に簡単です。

 @Component({ selector: 'g[custom-rect]', ... })

上記のスニペットは、カスタム要素を任意の<g custom-rect></div>タグにレンダリングします。これは非常に便利です。

カスタム要素:Aurelia Way

Aureliaでは、テンプレートのみのカスタム要素を作成できます。

 <template bindable="colors, title"> <h1>${title}</h1> <svg> <rect repeat.for="color of colors" fill.bind="color" x.bind="$index * 100" y="0" width="50" height="50"></rect> </svg> </template>

カスタム要素には、ファイル名に基づいた名前が付けられます。 他のコンポーネントに名前を付けることとの唯一の違いは、configureまたは<require>タグを介してインポートする場合、最後に.htmlを配置する必要があることです。 たとえば、 <require from="awesome-svg.html"></require>です。

Aureliaにもカスタム属性がありますが、Angular 2と同じ目的はありません。たとえば、Aureliaでは、カスタムrect要素に@containerlessアノテーションを使用できます。 @containerlessは、コントローラーと<compose>を使用せずに、カスタムテンプレートで使用することもできます。これにより、基本的にDOMにデータがレンダリングされます。

@containerlessアノテーションを含む次のコードについて考えてみます。

 <svg> <custom-rect containerless></custom-rect> </svg>

出力にはカスタム要素タグ( custom-rect )は含まれませんが、代わりに次のようになります。

 <svg> <rect ...></rect> </svg>

サービス

次の例に示すように、サービスに関しては、AureliaとAngularは非常に似ています。 NumberOperatorに依存するNumberGeneratorが必要だとします。

ミズクラゲのサービス

Aureliaで2つのサービスを定義する方法は次のとおりです。

 import {inject} from 'aurelia-framework'; import {NumberGenerator} from './number-generator' export class NumberGenerator { getNumber(){ return 42; } } @inject(NumberGenerator) export class NumberOperator { constructor(numberGenerator){ this.numberGenerator = numberGenerator; this.counter = 0; } getNumber(){ return this.numberGenerator.getNumber() + this.counter++; } }

ここで、コンポーネントについて、同じ方法で注入します。

 import {inject} from 'aurelia-framework'; import {NumberOperator} from './_services/number-operator'; @inject(NumberOperator) export class SomeCustomElement { constructor(numberOperator){ this.numberOperator = numberOperator; //this.numberOperator.getNumber(); } }

ご覧のとおり、依存性注入を使用すると、どのクラスも完全に拡張可能なサービスになる可能性があるため、独自のリゾルバーを作成することもできます。

ミズクラゲの工場

必要なのがファクトリまたは新しいインスタンスである場合( FactoryNewInstanceは、箱から出して提供される2つの一般的なリゾルバです)、次の操作を実行できます。

 import { Factory, NewInstance } from 'aurelia-framework'; @inject(SomeService) export class Stuff { constructor(someService, config){ this.someService = someService; } } @inject(Factory.of(Stuff), NewInstance.of(AnotherService)) export class SomethingUsingStuff { constructor(stuffFactory, anotherService){ this.stuff = stuffFactory(config); this.anotherServiceNewInstance = anotherService; } }

Angularサービス

これがAngular2の同じサービスセットです。

 import { Injectable } from '@angular/core'; import { NumberGenerator } from './number-generator'; @Injectable() export class NumberGenerator { getNumber(){ return 42; } } @Injectable() export class NumberOperator { counter : number = 0; constructor(@Inject(NumberGenerator) private numberGenerator) { } getNumber(){ return this.numberGenerator.getNumber() + this.counter++; } }

@Injectableアノテーションが必要であり、実際にサービスを注入するには、次のように、コンポーネント構成またはモジュール構成全体のプロバイダーのリストでサービスを指定する必要があります。

 @Component({ //... providers: [NumberOperator, NumberGenerator] })

または、お勧めしませんが、 bootstrap(AppComponent, [NumberGenerator, NumberOperator])呼び出しで指定することもできます。

注入方法に関係なく、 NumberOperatorNumberGeneratorの両方を指定する必要があることに注意してください。

結果のコンポーネントは次のようになります。

 @Component({ //... providers: [NumberOperator, NumberGenerator], }) export class SomeComponent { constructor(@Inject(NumberOperator) public service){ //service.getNumber(); } }
Angular2の工場

Angular 2では、名前の衝突を防ぐためにエイリアシングサービスにも使用されるprovideアノテーションを使用してファクトリを作成できます。 ファクトリの作成は次のようになります。

 let stuffFactory = (someService: SomeService) => { return new Stuff(someService); } @Component({ //... providers: [provide(Stuff, {useFactory: stuffFactory, deps: [SomeService]})] })

トランスクルージョン

Angular 1には、トランスクルージョンを使用して、あるテンプレートから別のテンプレートにコンテンツ、つまり「スロット」を含める機能がありました。 その子孫が何を提供しなければならないか見てみましょう。

Angular2によるコンテンツプロジェクション

Angular 2では、トランスクルージョンは「コンテンツプロジェクション」と呼ばれ、 ng-transcludeと同じように機能します。 つまり、Angular 1の用語で言えば、トランスクルージョンされたコンテンツは親スコープを使用します。 構成セレクターに基づいて、トランスクルージョンされたコンテンツタグと一致します。 検討:

 @Component({ selector: 'child', template: `Transcluded: <ng-content></ng-content>` }) export class MyComponent {}

次に、 <child-component>Hello from Translusion Component</child-component>でコンポーネントを使用できます。これにより、子コンポーネントでレンダリングされた正確なトランスクルージョンされたYesが取得されます。

マルチスロットトランスクルージョンの場合、Angular 2には、 @Component構成の場合と同じ方法で使用できるセレクターがあります。

 <!-- child.component.html --> <h4>Slot 1:</h4> <ng-content select=".class-selector"></ng-content> <h4>Slot 2:</h4> <ng-content select="[attr-selector]"></ng-content>
 <!-- parent.component.html --> <child> <span class="class-selector">Hello from Translusion Component</span> <p class="class-selector">Hello from Translusion Component again</p> <span attr-selector>Hello from Translusion Component one more time</span> </child> 

Angular2のスロット

カスタムタグでselectを使用できますが、タグはAngular2に認識されている必要があることに注意してください。

Aureliaのスロット

Aureliaが可能な限りWeb標準に従うと言ったのを覚えていますか? Aureliaでは、トランスクルージョンはスロットと呼ばれ、Web ComponentsShadowDOMの単なるポリフィルです。 Shadow DOMはまだスロット用に作成されていませんが、W3C仕様に準拠しています。

 <!-- child --> <template> Slot: <slot></slot> </template> <!-- parent --> <template> <child>${textValue}</child> </template>

Aureliaは標準に準拠するように設計されていましたが、Angular2は準拠していませんでした。 その結果、フォールバックコンテンツを使用するなど、Aureliaのスロットでより素晴らしいことができます(Angular 2でフォールバックコンテンツを使用しようとすると、 <ng-content> element cannot have contentなります)。 検討:

 <!-- child --> <template> Slot A: <slot name="slot-a"></slot> <br /> Slot B: <slot name="slot-b"></slot> Slot C: <slot name="slot-c">Fallback Content</slot> </template> <!-- parent --> <template> <child> <div slot="slot-a">A value</div> <div slot="slot-b">B value</div> </child> </template>

Angular 2と同じように、Aureliaは名前の一致に基づいてスロットのすべてのオカレンスをレンダリングします。

ミズクラゲのスロット

また、AureliaとAngularの両方で、テンプレートパーツをコンパイルし、コンポーネントを動的にレンダリングできることにも注意してください(Aurelia view-model<compose>を使用するか、Angular 2のComponentResolverを使用します)。

ShadowDOM

AureliaとAngularはどちらもShadowDOMをサポートしています。

Aureliaでは、 @useShadowDOMデコレータを使用するだけで、準備が整います。

 import {useShadowDOM} from 'aurelia-framework'; @useShadowDOM() export class YetAnotherCustomElement {}

Angularでは、 ViewEncapsulation.Nativeでも同じことができます。

 import { Component, ViewEncapsulation } from '@angular/core'; @Component({ //... encapsulation: ViewEncapsulation.Native, }) export class YetAnotherComponent {}

ブラウザがShadowDOMをサポートしているかどうかを確認することを忘れないでください。

サーバー側のレンダリング

2017年であり、サーバー側のレンダリングは非常に流行しています。 Angular Universalを使用してバックエンドでAngular2を既にレンダリングできます。また、Aureliaは、チームの新年の決議に記載されているように、2017年にこれを使用する予定です。 実際、Aureliaのリポジトリには実行可能なデモがあります。

それに加えて、Aureliaには1年以上にわたってプログレッシブエンハンスメント機能があります。これは、非標準のHTML構文のためにAngular2にはない機能です。

サイズ、パフォーマンス、および次の予定

全体像は示されていませんが、デフォルトの構成と最適化された実装を備えたDBMonsterベンチマークは、優れた比較図を示しています。AureliaとAngularは、1秒あたり約100回の再レンダリングという同様の結果を示しています(MacBook Proでテスト)。一方、Angular1はその結果の約半分を示しました。 AureliaとAngularはどちらもAngular1を約5倍上回っており、どちらもReactより40%進んでいます。 AureliaもAngular2も仮想DOMの実装はありません。

サイズの問題については、AngularはAureliaの約2倍の太さですが、Googleのスタッフが取り組んでいます。Angularのロードマップには、開発者のエクスペリエンスを向上させながら、Angular 4をより小さく、より軽量にする計画が含まれています。 Angular 3はありません。実際、メジャーリリースは6か月ごとに計画されているため、Angularについて話すときはバージョン番号を削除する必要があります。 Angularがアルファ版から現在のバージョンにたどり着いたパスを見ると、属性の名前がビルドからビルドに変更されるなど、常に一貫しているとは限らないことがわかります。Angularチームは、変更を壊すのは簡単だと約束しています。移行します。

2017年の時点で、AureliaチームはAurelia UXをリリースし、より多くの統合とツールを提供し、サーバー側のレンダリングを実装することを計画しています(これは非常に長い間ロードマップに含まれています)。

Angular 2 vs. Aurelia:味の問題

AngularとAureliaはどちらも優れており、どちらを選択するかは個人の好みと優先順位の問題です。 よりスリムなソリューションが必要な場合はAureliaが最適ですが、コミュニティサポートが必要な場合は、Angularが勝者です。 「このフレームワークで私は…できますか?」という問題ではありません。 答えは単に「はい」だからです。 これらは、Web標準への完全に異なるアプローチとともに、異なる哲学とスタイルに従いながら、ほぼ同じ機能を提供します。