Aurelia vs. Angular 2 — การเปรียบเทียบรหัส

เผยแพร่แล้ว: 2022-03-11

Angular และ Aurelia ซึ่งเป็นทายาทของ JavaScript Angular 1 รุ่นเก่าที่ดี เป็นคู่แข่งกันที่ดุเดือด พัฒนาและเผยแพร่ในเวลาใกล้เคียงกันและมีปรัชญาที่คล้ายคลึงกัน แต่แตกต่างกันในประเด็นสำคัญหลายประการ ในบทความนี้ เราจะทำการเปรียบเทียบความแตกต่างในด้านคุณลักษณะและโค้ดแบบเคียงข้างกัน

เรื่องสั้นโดยย่อ Aurelia ถูกสร้างขึ้นโดย Rob Eisenberg ซึ่งเป็นที่รู้จักในนามผู้สร้าง Durandal และ Caliburn เขาทำงานในทีม Angular 2 ที่ Google แต่ลาออกในปี 2014 เมื่อเห็นว่ากรอบงานสมัยใหม่จะดูแตกต่างจากพวกเขาอย่างไร

ความคล้ายคลึงกันยังคงอยู่ในระดับเทคนิคที่มากขึ้นเช่นกัน: เทมเพลตและส่วนประกอบ (หรือองค์ประกอบที่กำหนดเอง) ที่เกี่ยวข้องกันนั้นเป็นแกนหลักของแอปทั้ง Angular และ Aurelia และทั้งคู่ต้องการให้คุณมีองค์ประกอบรูท (เช่น แอป) นอกจากนี้ ทั้ง Angular และ Aurelia ยังใช้มัณฑนากรสำหรับการกำหนดค่าส่วนประกอบอย่างหนัก แต่ละองค์ประกอบมีวงจรชีวิตตายตัวที่เราสามารถเชื่อมโยงได้

ความแตกต่างระหว่าง Aurelia และ Angular 2 คืออะไร?

ความแตกต่างที่สำคัญตามที่ Rob Eisenberg บอกคืออยู่ในรหัส: Aurelia ไม่สร้างความรำคาญ เมื่อพัฒนาแอป Aurelia (หลังการกำหนดค่า) คุณเขียนด้วย ES6 หรือ TypeScript และเทมเพลตจะดูเหมือน HTML ที่มีเหตุผลอย่างยิ่ง โดยเฉพาะอย่างยิ่งเมื่อเปรียบเทียบกับ Angular Aurelia เป็นแบบแผนมากกว่าการกำหนดค่า และ 95% ของเวลาที่คุณทำได้ดีโดยใช้หลักการเริ่มต้น (เช่น การตั้งชื่อเทมเพลต การตั้งชื่อองค์ประกอบ ฯลฯ) ในขณะที่ Angular ต้องการให้คุณกำหนดค่าสำหรับทุกอย่างโดยพื้นฐานแล้ว

นอกจากนี้ Aurelia ยังถือว่าเป็นไปตามมาตรฐานอีกด้วย ถ้าเพียงเพราะไม่คำนึงถึงตัวพิมพ์เล็กและตัวพิมพ์ใหญ่เมื่อพูดถึงแท็ก HTML ในขณะที่ Angular 2 เป็นแบบ ซึ่งหมายความว่า Angular 2 ไม่สามารถพึ่งพาตัวแยกวิเคราะห์ HTML ของเบราว์เซอร์ได้ ดังนั้นจึงสร้างเอง

อีกปัจจัยที่ต้องพิจารณาเมื่อเลือกระหว่างกรอบงาน SPA คือ ชุมชน- ระบบนิเวศ-รอบตัว ทั้ง Angular และ Aurelia มีพื้นฐานทั้งหมด (เราเตอร์ เครื่องมือเทมเพลต การตรวจสอบความถูกต้อง ฯลฯ) และง่ายต่อการรับโมดอลดั้งเดิมหรือใช้ไลบรารีของบุคคลที่สาม แต่ไม่น่าแปลกใจเลยที่ Angular จะมีชุมชนที่ใหญ่กว่าและการพัฒนาที่ใหญ่กว่า ทีม.

นอกจากนี้ แม้ว่าเฟรมเวิร์กทั้งสองเป็นโอเพ่นซอร์ส แต่ Angular ได้รับการพัฒนาโดย Google เป็นหลัก และไม่ได้มีจุดประสงค์เพื่อการค้าในขณะที่ Durandal, Inc. ซึ่งว่าจ้างทีมงานหลัก กำลังติดตามรูปแบบการสร้างรายได้ของ Ember.js ผ่านการให้คำปรึกษาและการฝึกอบรม

Aurelia vs. Angular: การเปรียบเทียบรหัส

มาดูคุณลักษณะที่โดดเด่นที่สุดบางส่วนซึ่งเน้นย้ำปรัชญาเบื้องหลังแต่ละกรอบงาน

หลังจากการโคลนโปรเจ็กต์เมล็ดพันธุ์สำหรับ Angular และ Aurelia เรามีแอป ES6 Aurelia (คุณสามารถใช้ Jspm/System.js, Webpack และ RequireJS ร่วมกับ ES6 หรือ TypeScript) และแอป TypeScript Angular (WebPack) ตามลำดับ

ม้วนกันเลย

การผูกข้อมูล

ก่อนที่เราจะเปรียบเทียบตัวอย่างการทำงานแบบเคียงข้างกัน เราต้องดูที่ความแตกต่างทางวากยสัมพันธ์ระหว่าง Aurelia และ Angular 2 กล่าวคือในฟังก์ชันหลักของการโยงค่าจากคอนโทรลเลอร์ไปยังมุมมอง Angular 1 ใช้ "การตรวจสอบสกปรก" สำหรับทุกอย่าง ซึ่งเป็นวิธีการที่สแกนขอบเขตเพื่อหาการเปลี่ยนแปลง สิ่งนี้ทำให้เกิดปัญหาด้านประสิทธิภาพหลายประการ ซึ่งเข้าใจได้ ทั้ง Angular 2 และ Aurelia ไม่ปฏิบัติตามเส้นทางนั้น แต่จะใช้การผูกเหตุการณ์แทน

การผูกข้อมูลในเชิงมุม 2

ใน 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 โมดูลของคุณต้องนำเข้า FormsModule จาก @angular/forms ตอนนี้เรามีสิ่งที่น่าสนใจ การอัปเดตค่าในอินพุตพาเรนต์จะเปลี่ยนค่าทุกที่ แต่การแก้ไขอินพุตของเด็กจะมีผลกับเด็กคนนั้นเท่านั้น หากเราต้องการให้อัปเดตค่าพาเรนต์ เราจำเป็นต้องมีเหตุการณ์ที่แจ้งให้ผู้ปกครองทราบ หลักการตั้งชื่อสำหรับเหตุการณ์นี้คือ 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)">

แล้วการโยงแบบครั้งเดียวล่ะ เมื่อคุณบอกกรอบงานอย่างมีประสิทธิภาพให้ละเว้นค่าที่ถูกผูกไว้ แม้ว่าจะเปลี่ยนไปก็ตาม

ใน Angular 1 เราใช้ {{::value}} เพื่อผูกครั้งเดียว ใน Angular 2 การโยงแบบครั้งเดียวจะซับซ้อน: เอกสารระบุว่าคุณสามารถใช้ changeDetection: ChangeDetectionStrategy.OnPush ในการกำหนดค่าของคอมโพเนนต์ได้ แต่นั่นจะทำให้การโยง ทั้งหมด ของคุณเป็นแบบครั้งเดียว

การผูกข้อมูล: The Aurelia Way

ตรงกันข้ามกับ Angular 2 การเชื่อมโยงข้อมูลและเหตุการณ์ใน Aurelia นั้นง่ายมาก คุณสามารถใช้การแก้ไข เช่นเดียวกับคุณสมบัติ Angular's property="${value}" หรือใช้การโยงประเภทใดประเภทหนึ่งต่อไปนี้:

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

ชื่อสามารถอธิบายตนเองได้ นอกจากนี้ยังมี property.bind="value" ซึ่งเป็น syntax-sugar ที่ตรวจจับตัวเองว่าการผูกควรเป็นแบบทางเดียวหรือสองทาง พิจารณา:

 <!-- 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>

ความแตกต่างระหว่างสองสิ่งนี้คือ .trigger สร้างตัวจัดการเหตุการณ์ในองค์ประกอบนั้นในขณะที่ .delegate เพิ่มผู้ฟังใน document วิธีนี้ช่วยประหยัดทรัพยากร แต่เห็นได้ชัดว่าใช้ไม่ได้กับเหตุการณ์ที่ไม่เดือด

ตัวอย่างพื้นฐานของ Aurelia กับ Angular

ตอนนี้เราได้ครอบคลุมการผูกแล้ว มาสร้างองค์ประกอบพื้นฐานที่แสดงผลกราฟิกแบบเวกเตอร์ที่ปรับขนาดได้ (SVG) มันยอดเยี่ยมมาก ดังนั้นเราจะเรียกมันว่า awesome-svg แบบฝึกหัดนี้จะอธิบายทั้งการทำงานพื้นฐานและปรัชญาสำหรับ Aurelia และ Angular 2 ตัวอย่างโค้ด Aurelia ของบทความนี้มีอยู่ใน GitHub

ตัวอย่างสี่เหลี่ยมผืนผ้า SVG ใน Aurelia

มาสร้างไฟล์ JavaScript กันก่อน:

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

ตอนนี้สำหรับ HTML

ใน Aurelia คุณสามารถระบุเทมเพลต (หรือใช้แบบอินไลน์) ด้วยคำอธิบายประกอบ @template , @inlineView หรือแม้แต่ @noView แต่จะค้นหาไฟล์ .html ที่มีชื่อเดียวกับ . .js นอกกรอบ ไฟล์. เช่นเดียวกับชื่อขององค์ประกอบที่กำหนดเอง คุณสามารถตั้งค่าด้วย @customElement('awesome-svg') ได้ แต่ถ้าคุณทำไม่ได้ Aurelia จะเปลี่ยนชื่อเป็น dash-case และค้นหารายการที่ตรงกัน

เนื่องจากเราไม่ได้ระบุเป็นอย่างอื่น องค์ประกอบจะถูกเรียกว่า 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> สิ่งที่ควรสังเกตอีกอย่างก็คือคุณใช้ ` for … of and the string interpolation ${title}` เช่นเดียวกับที่คุณทำใน ES6

ในการใช้องค์ประกอบ เราควรนำเข้าองค์ประกอบในเทมเพลตที่มี <require from="path/to/awesome-svg"></require> หรือหากใช้ในแอป ให้รวมทรัพยากรในฟังก์ชันกำหนดค่าของกรอบงานให้เป็นสากล ด้วย aurelia.use.globalResources('path/to/awesome-svg'); ซึ่งจะนำเข้าส่วนประกอบ awesome-svg ทันทีและสำหรับทั้งหมด

[หมายเหตุ หากคุณไม่ทำทั้งสองอย่าง <awesome-svg></awesome-svg> จะได้รับการปฏิบัติเหมือนกับแท็ก HTML อื่นๆ โดยไม่มีข้อผิดพลาด]

คุณสามารถแสดงส่วนประกอบด้วย:

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

นี่ทำให้ชุดของ 3 สี่เหลี่ยม:

ตัวอย่างสี่เหลี่ยมผืนผ้า SVG ใน Aurelia

ตัวอย่างสี่เหลี่ยม SVG ในเชิงมุม 2

ตอนนี้ ลองทำตัวอย่างเดียวกันใน Angular 2 ซึ่งมีอยู่ใน GitHub ด้วย 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'” ในการแก้ไขปัญหานี้ คุณต้องใช้ไวยากรณ์ [attr.fill]="color" แทน นอกจากนี้ โปรดทราบว่าจำเป็นต้องระบุเนมสเปซในองค์ประกอบย่อยภายใน <svg/>: <svg:rect> เพื่อให้ Angular รู้ว่าสิ่งนี้ไม่ควรถือเป็น HTML มาขยายข้อมูลโค้ดของเรา:

 <!-- 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> 

ตัวอย่างสี่เหลี่ยม SVG ใน AngularJS 2

องค์ประกอบที่กำหนดเอง

สมมติว่าตอนนี้เราต้องการให้โค้ดสี่เหลี่ยมผืนผ้าของเราเป็นส่วนประกอบที่กำหนดเองด้วยตรรกะของตัวเอง

องค์ประกอบที่กำหนดเอง: The Angular 2 Way

เนื่องจาก Angular 2 แสดงส่วนประกอบตามสิ่งที่ตรงกับตัวเลือกที่กำหนดไว้ มันจึงง่ายมากที่จะกำหนดองค์ประกอบที่กำหนดเอง เช่น:

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

ข้อมูลโค้ดด้านบนจะแสดงองค์ประกอบที่กำหนดเองให้กับแท็ก <g custom-rect></div> ซึ่งสะดวกมาก

องค์ประกอบที่กำหนดเอง: The 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>

องค์ประกอบที่กำหนดเองจะถูกตั้งชื่อตามชื่อไฟล์ ความแตกต่างเพียงอย่างเดียวจากการตั้งชื่อส่วนประกอบอื่นๆ คือ เมื่อนำเข้า ไม่ว่าจะในการกำหนดค่าหรือผ่านแท็ก <require> คุณควรใส่ .html ต่อท้าย ตัวอย่างเช่น <require from="awesome-svg.html"></require>

Aurelia มีแอตทริบิวต์ที่กำหนดเองเช่นกัน แต่ไม่ได้มีจุดประสงค์เดียวกับใน Angular 2 ตัวอย่างเช่น ใน Aurelia คุณสามารถใช้คำอธิบายประกอบ @containerless บนองค์ประกอบ rect ที่กำหนดเองได้ @containerless ยังสามารถใช้กับเทมเพลตที่กำหนดเองได้โดยไม่ต้องใช้คอนโทรลเลอร์และ <compose> ซึ่งโดยทั่วไปจะแสดงผลเนื้อหาใน DOM

พิจารณาโค้ดต่อไปนี้ที่มีคำอธิบายประกอบ @containerless :

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

ผลลัพธ์จะไม่มีแท็กองค์ประกอบที่กำหนดเอง ( custom-rect ) แต่เราจะได้รับ:

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

บริการ

ในส่วนที่เกี่ยวกับบริการ Aurelia และ Angular มีความคล้ายคลึงกันมาก ดังที่คุณเห็นในตัวอย่างต่อไปนี้ สมมติว่าเราต้องการ NumberOperator ซึ่งขึ้นอยู่กับ NumberGenerator

บริการใน Aurelia

ต่อไปนี้เป็นวิธีกำหนดบริการทั้งสองของเราใน Aurelia:

 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(); } }

อย่างที่คุณเห็น ด้วยการฉีดการพึ่งพา คลาสใดๆ ก็สามารถเป็นบริการที่ขยายได้อย่างเต็มที่ ดังนั้นคุณจึงสามารถเขียนตัวแก้ไขของคุณเองได้

โรงงานใน Aurelia

หากสิ่งที่คุณต้องการคือโรงงานหรืออินสแตนซ์ใหม่ ( Factory และ NewInstance เป็นเพียงตัวแก้ไขยอดนิยมสองสามตัวที่มีให้ตั้งแต่แกะกล่อง) คุณสามารถทำสิ่งต่อไปนี้ได้:

 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 2:

 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])

โปรดทราบว่าคุณต้องระบุทั้ง NumberOperator และ NumberGenerator ไม่ว่าคุณจะฉีดด้วยวิธีใด

องค์ประกอบที่ได้จะมีลักษณะดังนี้:

 @Component({ //... providers: [NumberOperator, NumberGenerator], }) export class SomeComponent { constructor(@Inject(NumberOperator) public service){ //service.getNumber(); } }
โรงงานในเชิงมุม2

ใน Angular 2 คุณสามารถสร้างโรงงานที่ provide คำอธิบายประกอบซึ่งใช้สำหรับบริการนามแฝงเพื่อป้องกันการชนกันของชื่อ การสร้างโรงงานอาจมีลักษณะดังนี้:

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

การหลุดพ้น

Angular 1 มีความสามารถในการรวมเนื้อหา "ช่อง" จากเทมเพลตหนึ่งไปยังอีกเทมเพลตหนึ่งโดยใช้การยกเว้น เรามาดูกันว่าทายาทมีอะไรบ้าง

การฉายเนื้อหาด้วย Angular 2

ใน Angular 2 การ transclusion เรียกว่า "การฉายเนื้อหา" และทำงานในลักษณะเดียวกับที่ 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 HTML ที่แยกจากกันในองค์ประกอบย่อย

สำหรับการ transclusion หลายช่อง 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 ในแท็กที่กำหนดเองได้ แต่อย่าลืมว่า Angular 2 ต้องรู้จักแท็ก

สล็อตกับ Aurelia

จำได้ไหมว่าฉันพูดว่า Aurelia ปฏิบัติตามมาตรฐานเว็บทุกครั้งที่ทำได้? ใน Aurelia การ transclusion เรียกว่า slot และเป็นเพียง polyfill สำหรับ Web Components Shadow DOM Shadow DOM ยังไม่ได้สร้างสำหรับ slots แต่ เป็นไปตามข้อกำหนดของ W3C

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

Aurelia ได้รับการออกแบบมาให้เป็นไปตามมาตรฐาน และ Angular 2 ไม่ใช่ เป็นผลให้เราสามารถทำสิ่งที่ยอดเยี่ยมมากขึ้นด้วยสล็อตของ 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

เป็นที่น่าสังเกตว่าทั้งใน Aurelia และ Angular คุณสามารถรวบรวมชิ้นส่วนเทมเพลตและแสดงส่วนประกอบแบบไดนามิก (โดยใช้ <compose> กับ view-model ใน Aurelia หรือ ComponentResolver ใน Angular 2)

Shadow DOM

ทั้ง Aurelia และ Angular รองรับ Shadow DOM

ใน 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 {}

อย่าลืมตรวจสอบว่าเบราว์เซอร์ของคุณรองรับ Shadow DOM หรือไม่

การแสดงผลฝั่งเซิร์ฟเวอร์

มันคือปี 2017 และการเรนเดอร์ฝั่งเซิร์ฟเวอร์นั้นทันสมัยมาก คุณสามารถเรนเดอร์ Angular 2 ในส่วนแบ็คเอนด์ด้วย Angular Universal ได้แล้ว และ Aurelia จะมีสิ่งนี้ในปี 2560 ตามที่ระบุไว้ในมติปีใหม่ของทีม อันที่จริง มีการสาธิตที่รันได้ใน repo ของ Aurelia

นอกจากนั้น Aurelia ยังมีความสามารถในการเพิ่มประสิทธิภาพที่ก้าวหน้ามาเป็นเวลากว่าหนึ่งปี ซึ่งสิ่งที่ Angular 2 ไม่สามารถทำได้เนื่องจากไวยากรณ์ HTML ที่ไม่ได้มาตรฐาน

ขนาด ประสิทธิภาพ และสิ่งที่จะเกิดขึ้นต่อไป

แม้ว่าจะไม่แสดงภาพรวมทั้งหมด แต่การวัดประสิทธิภาพ DBMonster ด้วยการกำหนดค่าเริ่มต้นและการใช้งานที่ปรับให้เหมาะสม ให้ภาพเปรียบเทียบที่ดี: Aurelia และ Angular แสดงผลลัพธ์ที่คล้ายกันประมาณ 100 การแสดงผลซ้ำต่อวินาที (ตามที่ทดสอบบน MacBook Pro) ในขณะที่ Angular 1 แสดงบางสิ่งประมาณครึ่งหนึ่งของผลลัพธ์นั้น ทั้ง Aurelia และ Angular ทำได้ดีกว่า Angular 1 ประมาณห้าเท่า และทั้งคู่นั้นนำหน้า React 40% ทั้ง Aurelia และ Angular 2 ไม่มีการนำ Virtual DOM ไปใช้งาน

ในเรื่องของขนาด Angular นั้นอ้วนเป็นสองเท่าของ Aurelia แต่พวกที่ Google กำลังดำเนินการอยู่: แผนงานของ Angular รวมถึงการเปิดตัว Angular 4 โดยมีแผนที่จะทำให้มันเล็กลงและเบาขึ้นในขณะที่ปรับปรุงประสบการณ์ของนักพัฒนา ไม่มี Angular 3 และจริงๆ แล้ว หมายเลขเวอร์ชันควรลดลงเมื่อพูดถึง Angular เนื่องจากมีการวางแผนการเผยแพร่หลักทุกๆ 6 เดือน หากคุณดูเส้นทางที่ Angular นำจากอัลฟ่ามาสู่เวอร์ชันปัจจุบัน คุณจะเห็นว่ามันไม่สอดคล้องกับสิ่งต่าง ๆ เช่น การเปลี่ยนชื่อแอตทริบิวต์จากบิลด์เป็นบิลด์ ฯลฯ ทีมงานของ Angular สัญญาว่าการเปลี่ยนแปลงจะเป็นเรื่องง่าย เพื่อโยกย้าย

ในปี 2560 ทีมงาน Aurelia วางแผนที่จะเปิดตัว Aurelia UX จัดหาการผสานรวมและเครื่องมือเพิ่มเติม และใช้การเรนเดอร์ฝั่งเซิร์ฟเวอร์ (ซึ่งอยู่ในแผนงานมาเป็นเวลานานมาก)

Angular 2 vs. Aurelia: เรื่องของรสนิยม

ทั้ง Angular และ Aurelia นั้นดี และการเลือกซึ่งกันและกันเป็นเรื่องของรสนิยมส่วนตัวและลำดับความสำคัญ หากคุณต้องการโซลูชันที่เพรียวบางกว่านี้ Aurelia เป็นตัวเลือกที่ดีที่สุดของคุณ แต่ถ้าคุณต้องการการสนับสนุนจากชุมชน Angular คือผู้ชนะของคุณ ไม่ใช่คำถามว่า "กรอบงานนี้อนุญาตให้ฉัน…หรือไม่" เพราะคำตอบก็คือ "ใช่" มีฟังก์ชันการทำงานที่เหมือนกันโดยประมาณในขณะที่ปฏิบัติตามปรัชญาและรูปแบบต่างๆ ควบคู่ไปกับแนวทางมาตรฐานเว็บที่ต่างไปจากเดิมอย่างสิ้นเชิง