Aurelia vs. Angular 2 — การเปรียบเทียบรหัส
เผยแพร่แล้ว: 2022-03-11Angular และ 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 ในเชิงมุม 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>
องค์ประกอบที่กำหนดเอง
สมมติว่าตอนนี้เราต้องการให้โค้ดสี่เหลี่ยมผืนผ้าของเราเป็นส่วนประกอบที่กำหนดเองด้วยตรรกะของตัวเอง
องค์ประกอบที่กำหนดเอง: 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>
คุณสามารถใช้ 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 และ 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 คือผู้ชนะของคุณ ไม่ใช่คำถามว่า "กรอบงานนี้อนุญาตให้ฉัน…หรือไม่" เพราะคำตอบก็คือ "ใช่" มีฟังก์ชันการทำงานที่เหมือนกันโดยประมาณในขณะที่ปฏิบัติตามปรัชญาและรูปแบบต่างๆ ควบคู่ไปกับแนวทางมาตรฐานเว็บที่ต่างไปจากเดิมอย่างสิ้นเชิง