محرك تقديم الزاوي 6 واللبلاب

الصورة مساء الخير أيها الزملاء. نحن ندرس ما إذا كان سيتم تحديث الكتاب بواسطة Jacob Fine و Anton Moiseev " Angular و TypeScript. إنشاء موقع ويب للمحترفين ". تظهر طبعة جديدة في خريف هذا العام وتتضمن مادة على الزاوي 5 و 6.

فكرنا في البداية في نشر مواد حول محرك Ivy ، والتي من المحتمل أن تكون أكثر ابتكارات مثيرة للاهتمام في Angular 6 ، ولكن بعد ذلك توقفنا عن نشر أكثر نظرة عامة من Cedric Exbright (تم إصدار الأصل في مايو).

في Angular 6 ، كان هناك الكثير من الابتكارات الجادة ، علاوة على ذلك ، أهمها لا يمكنك تسمية الميزات: هذا هو Ivy ، وهو محرك عرض جديد. نظرًا لأن المحرك لا يزال تجريبيًا ، فسنتحدث عنه في نهاية هذه المقالة ، وسنبدأ بميزات جديدة أخرى وتغييرات ثورية.

مزودي الأشجار المهتزة

الآن هناك طريقة جديدة موصى بها لتسجيل الموفر مباشرة في الديكور @Injectable() ، وذلك باستخدام السمة الجديدة providedIn . تأخذ 'root' كقيمة أي وحدة نمطية في تطبيقك. عند استخدام 'root' سيتم تسجيل الكائن الذي تم تنفيذه في التطبيق باعتباره وحيدًا ، ولن تحتاج إلى إضافته إلى المزودين في وحدة الجذر. وبالمثل ، عند استخدام providedIn: UsersModule يتم تسجيل الكائن الذي تم UsersModule كموفر UsersModule ، ولا تتم إضافته إلى موفري الوحدة النمطية.

 @Injectable({ providedIn: 'root' }) export class UserService { } 

تم تقديم هذه الطريقة الجديدة لإزالة التعليمات البرمجية غير الوظيفية بشكل أفضل في التطبيق (اهتزاز الشجرة). في الوقت الحاضر ، الوضع هو أن الخدمة التي تتم إضافتها إلى موفري الوحدة ستنتهي في المجموعة النهائية ، حتى إذا لم يتم استخدامها في التطبيق - والسماح بذلك أمر محزن قليلاً. إذا كنت تستخدم التحميل البطيء ، يمكنك الوقوع في عدة مصائد في وقت واحد ، أو تجد نفسك في موقف حيث سيتم إدخال الخدمة في مجموعة خاطئة.

من غير المرجح أن يحدث مثل هذا الموقف في التطبيقات في كثير من الأحيان (إذا كتبت خدمة ، ثم استخدمها) ، لكن وحدات الجهات الخارجية تقدم أحيانًا خدمات لا نحتاجها - ونتيجة لذلك ، لدينا مجموعة كاملة من JavaScript عديمة الفائدة.

لذلك ، ستكون هذه الميزة مفيدة بشكل خاص لمطوري المكتبات ، ولكن يُنصح الآن بتسجيل الكائنات التي تم تنفيذها بهذه الطريقة - وهذا ينطبق أيضًا على مطوري التطبيقات. حتى أن CLI الجديدة تستخدم الآن السقالات providedIn: 'root' بشكل افتراضي عند العمل مع الخدمات.

على نفس المنوال ، يمكنك الآن الإعلان عن ميزة InjectionToken وتسجيلها مباشرة مع providedIn وإضافة factory هنا:

 export const baseUrl = new InjectionToken<string>('baseUrl', { providedIn: 'root', factory: () => 'http://localhost:8080/' }); 

يرجى ملاحظة: هذا يبسط أيضًا اختبار الوحدة. لأغراض مثل هذا الاختبار ، يتم استخدامها لتسجيل الخدمة مع موفري وحدة الاختبار. إليك ما فعلناه من قبل:

 beforeEach(() => TestBed.configureTestingModule({ providers: [UserService] })); 

الآن ، إذا كانت UserService تستخدم في providedIn: 'root' :

 beforeEach(() => TestBed.configureTestingModule({})); 

فقط لا تقلق: جميع الخدمات المسجلة في providedIn لم يتم تحميلها في الاختبار ، ولكن يتم نسخها بتكاسل ، فقط في الحالات التي تكون فيها هناك حاجة إليها حقًا.

Rxjs 6

يستخدم Angular 6 الآن RxJS 6 داخليًا ، لذلك تحتاج إلى تحديث التطبيق مع وضع ذلك في الاعتبار.

و ... RxJS 6 يغير نهج الاستيراد!

في RxJS 5 ، يمكنك كتابة:

 import { Observable } from 'rxjs/Observable'; import 'rxjs/add/observable/of'; import 'rxjs/add/operator/map'; const squares$: Observable<number> = Observable.of(1, 2) .map(n => n * n); 

في RxJS 5.5 ، ظهرت كشوف الأنابيب:

 import { Observable } from 'rxjs/Observable'; import { of } from 'rxjs/observable/of'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

وفي RxJS 6.0 ، تغيرت الواردات:

 import { Observable, of } from 'rxjs'; import { map } from 'rxjs/operators'; const squares$: Observable<number> = of(1, 2).pipe( map(n => n * n) ); 

لذا ، سيتعين عليك يومًا ما تغيير عمليات الاستيراد داخل التطبيق بالكامل. أكتب "مرة واحدة" وليس "الآن" ، لأن مكتبة rxJs المتوافقة تم إصدارها في RxJS ، مما يسمح بتنزيل RxJS إلى الإصدار 6.0 ، حتى إذا كانت الإصدارات القديمة لا تزال مستخدمة في تطبيقك بالكامل أو في إحدى المكتبات المستخدمة بناء الجملة.

لقد كتب فريق Angular مستندًا كاملاً حول هذا الموضوع ، ومن الضروري تمامًا قراءته قبل الانتقال إلى Angular 6.0.

يرجى ملاحظة: هنا مجموعة قواعد tslint رائعة جدًا تسمى rxjs-tslint . هناك 4 قواعد فقط ، وإذا قمت بإضافتها إلى المشروع ، فسيقوم النظام تلقائيًا بترحيل جميع عمليات الاستيراد ورمز RxJS ، ويتم ذلك عن طريق أبسط tslint --fix ! بعد كل شيء ، إذا كنت لا تزال لا تعرف ، في tslint هناك خيار fix تلقائيًا على إصلاح جميع الأخطاء التي يجدها! يمكن استخدامه بشكل أكثر بساطة: تثبيت rxjs-tslint وتشغيل rxjs-5-to-6-migrate -p src/tsconfig.app.json . لقد جربت rxjs-tslint في أحد مشاريعنا وعملت بشكل جيد (قم بتشغيله مرتين على الأقل لطي جميع الواردات). تحقق من قراءة هذا المشروع لمزيد من التفاصيل: github.com/ReactiveX/rxjs-tslint .

إذا كنت مهتمًا بمعرفة المزيد حول RxJS 6.0 ، فإنني أوصي بتقرير Ben Lesch التالي حول ng-conf.

i18n

أهم احتمال يتعلق بـ i18n هو القدرة على إنشاء "i18n في وقت التشغيل" ، دون الحاجة إلى إنشاء التطبيق بشكل منفصل لكل نقطة محلية. هذه الميزة غير متاحة حتى الآن (هناك نماذج أولية فقط) ، وستكون هناك حاجة إلى محرك Ivy لتشغيلها (المزيد عن ذلك أدناه).

تم بالفعل إجراء تغيير آخر متعلق بـ i18n وهو متاح. تم تحسين قناة العملات بالطريقة الأكثر كفاءة: الآن تقوم بتقريب جميع العملات ليس إلى رقمين ، كما كان من قبل ، ولكن إلى العدد المطلوب من الأرقام (على سبيل المثال ، إلى 3 في حالة الدينار البحريني أو إلى 0 للبيزو التشيلي).

إذا لزم الأمر ، يمكن استرداد هذه القيمة بشكل برمجي باستخدام وظيفة i18n getNumberOfCurrencyDigits الجديدة.

ظهرت أيضًا وظائف تنسيق ملائمة أخرى ، مثل formatDate و formatCurrency و formatPercent و formatNumber في formatNumber formatPercent .

بشكل ملائم ، إذا كنت ترغب في تطبيق نفس التحويلات التي تتم في القنوات ، ولكن قم بذلك من كود TypeScript.

الرسوم المتحركة

في Angular 6.0 ، تكون الرسوم المتحركة ممكنة بالفعل بدون polyfill web-animations-js ، إلا إذا كنت تستخدم AnimationBuilder . يمكن للتطبيق الخاص بك الفوز ببعض البايتات الثمينة! في حالة عدم دعم المستعرض لواجهة برمجة التطبيقات element.animate ، يتراجع Angular 6.0 إلى استخدام الإطارات الأساسية CSS.

العناصر الزاوية

Angular Elements هو مشروع يسمح لك بلف مكونات Angular كمكونات ويب وتضمينها في تطبيق لا يستخدم Angular. في البداية ، كان هذا المشروع موجودًا فقط في "Angular Lab" (أي أنه لا يزال تجريبيًا). مع الإصدار 6 ، يأتي في المقدمة قليلاً ويتم تضمينه رسميًا في الإطار. هذا موضوع كبير يستحق مقالة منفصلة.

ElementRef <T>

إذا كنت تريد أن تأخذ ارتباط عنصر في القالب الخاص بك ، يمكنك استخدام @ViewChild أو @ViewChildren ، أو حتى تنفيذ ElementRef مباشرة. العيب في هذه الحالة هو: في Angular 5.0 أو أقل ، سيحصل ElementRef المحدد على النوع any لخاصية nativeElement .

في Angular 6.0 يمكنك كتابة ElementRef أكثر صرامة إذا كنت ترغب في:

 @ViewChild('loginInput') loginInput: ElementRef<HTMLInputElement>; ngAfterViewInit() { // nativeElement  `HTMLInputElement` this.loginInput.nativeElement.focus(); } 

ما هو معترف به على أنه غير مرغوب فيه وما يتغير بشكل أساسي

لنتحدث عما يجب عليك أخذه في الاعتبار عند الشروع في الهجرة!

preserveWhitespaces : false افتراضي

في قسم "المشاكل التي قد تحدث أثناء الترقية" ، نلاحظ أن preserveWhitespaces أصبح الآن false بشكل افتراضي. ظهر هذا الخيار في Angular 4.4 ، وإذا كنت تتساءل عما يمكن توقعه في نفس الوقت - إليك منشور كامل حول هذا الموضوع. المفسد: كل شيء يمكن أن يفعله ، أو يمكن أن يكسر قوالبك تمامًا.

ngModel والأشكال التفاعلية

في السابق ، كان من الممكن توفير نفس حقل النموذج مع ngModel و formControl ، ولكن اليوم تعتبر هذه الممارسة غير مرغوب فيها ولن يتم دعمها بعد الآن في Angular 7.0.

هناك القليل من الارتباك هنا ، وقد عملت الآلية بأكملها ، ربما ليس كما توقعت ( ngModel - كان هذا توجيهًا غير مألوف لك منذ فترة طويلة ، ولكن إدخال / إخراج توجيه formControl ، والذي يؤدي نفس المهمة تقريبًا ، ولكن ليس متطابقًا).

حتى الآن ، إذا طبقنا الرمز:

 <input [(ngModel)]="user.name" [formControl]="nameCtrl"> 

ثم نحصل على تحذير.

يمكنك تكوين التطبيق لعرض تحذير always ( once ) ، once (مرة واحدة) أو never (مطلقًا). الافتراضي هو always .

 imports: [ ReactiveFormsModule.withConfig({ warnOnNgModelWithFormControl: 'never' }); ] 

بطريقة أو بأخرى ، للتحضير للانتقال إلى Angular 7 ، تحتاج إلى تكييف الشفرة لاستخدام إما النماذج الموجهة للقالب أو النماذج التفاعلية.

مشروع اللبلاب: محرك عرض جديد (جديد) في Angular

سووو ... هذا هو الإصدار الرئيسي الرابع (2 ، 4 ، 5 ، 6) ، ويتم إعادة كتابة محرك التقديم للمرة الثالثة!

تذكر: تقوم Angular بتجميع القوالب الخاصة بك في كود TypeScript مكافئ. ثم يتم تجميع TypeScript هذا مع TypeScript الذي كتبته في JavaScript ، وتكون النتيجة تحت تصرف المستخدم. وقبلنا بالفعل الإصدار الثالث من محرك العرض هذا في Angular (الأول كان في الإصدار الأولي لـ Angular 2.0 ، والثاني في Angular 4.0).

في هذا الإصدار الجديد من محرك التقديم ، لا يتغير نهج كتابة القوالب ، ومع ذلك ، فإنه يحسن عددًا من المؤشرات ، على وجه الخصوص:

  • بناء الوقت
  • حجم الطلب

كل هذا لا يزال تجريبيًا بعمق ، ويتم تشغيل محرك عرض Ivy الجديد بواسطة مربع اختيار ، والذي يجب عليك وضعه في خيارات المترجم (في ملف tsconfig.json ) إذا كنت ترغب في تجربته.

 "angularCompilerOptions": { "enableIvy": true } 

يرجى ملاحظة أن هذه الآلية ربما لا تكون موثوقة للغاية ، لذلك لا تستخدمها في الإنتاج حتى الآن. ربما لا يزال لا يعمل. ولكن في المستقبل القريب سيتم قبوله كخيار افتراضي ، لذلك يجب أن تجربه مرة واحدة ، ومعرفة ما إذا كان يعمل في تطبيقك ، وما الذي تستفيد منه.

دعونا نناقش بمزيد من التفصيل كيف يختلف Ivy عن محرك العرض القديم.

الرمز الذي تم إنشاؤه بواسطة المحرك القديم

دعنا PonyComponent على مثال صغير: PonyComponent مكون PonyComponent يأخذ نموذج PonyModel (مع معلمات name color ) ويعرض صورة المهر (اعتمادًا على البدلة) ، بالإضافة إلى اسم المهر.

يبدو هذا:

 @Component({ selector: 'ns-pony', template: `<div> <ns-image [src]="getPonyImageUrl()"></ns-image> <div></div> </div>` }) export class PonyComponent { @Input() ponyModel: PonyModel; getPonyImageUrl() { return `images/${this.ponyModel.color}.png`; } } 

ngfactory محرك العرض المقدم في Angular 4 فئة تسمى ngfactory لكل قالب. احتوى الفصل عادة (كود مبسط):

 export function View_PonyComponent_0() { return viewDef(0, [ elementDef(0, 0, null, null, 4, "div"), elementDef(1, 0, null, null, 1, "ns-image", View_ImageComponent_0), directiveDef(2, 49152, null, 0, i2.ImageComponent, { src: [0, "src"] }), elementDef(3, 0, null, null, 1, "div"), elementDef(4, null, ["", ""]) ], function (check, view) { var component = view.component; var currVal_0 = component.getPonyImageUrl(); check(view, 2, 0, currVal_0); }, function (check, view) { var component = view.component; var currVal_1 = component.ponyModel.name; check(view, 4, 0, currVal_1); }); } 

من الصعب قراءتها ، ولكن الأجزاء الرئيسية من هذا الرمز موصوفة كما يلي:

  • هيكل DOM الذي تم إنشاؤه ، والذي يحتوي على تعريفات العناصر ( figure ، img ، figcaption ) ، سماتها وتعاريف العقد النصية. يتم تمثيل كل عنصر من عناصر بنية DOM في صفيف تعريفات العرض بواسطة الفهرس الخاص به.
  • تغيير وظائف الكشف ؛ يتحقق الرمز الموجود فيها لمعرفة ما إذا كانت التعبيرات المستخدمة في القالب تؤدي إلى نفس القيم كما كانت من قبل. هنا ، يتم getPonyImageUrl نتيجة طريقة getPonyImageUrl ، وإذا تغيرت ، يتم تحديث قيمة الإدخال لمكون الصورة. وينطبق الشيء نفسه على لقب المهر: إذا تغير ، سيتم تحديث عقدة النص التي تحتوي على هذا اللقب.

ولدت كود اللبلاب

إذا عملنا مع Angular 6 ، وتم enableIvy علامة enableIvy على true ، فلن يتم إنشاء ngfactory منفصل في نفس المثال ؛ سيتم تضمين المعلومات مباشرةً في الحقل الثابت للمكون نفسه (رمز مبسط):

 export class PonyComponent { static ngComponentDef = defineComponent({ type: PonyComponent, selector: [['ns-pony']], factory: () => new PonyComponent(), template: (renderFlag, component) { if (renderFlag & RenderFlags.Create) { elementStart(0, 'figure'); elementStart(1, 'ns-image'); elementEnd(); elementStart(2, 'div'); text(3); elementEnd(); elementEnd(); } if (renderFlag & RenderFlags.Update) { property(1, 'src', component.getPonyImageUrl()); text(3, interpolate('', component.ponyModel.name, '')); } }, inputs: { ponyModel: 'ponyModel' }, directives: () => [ImageComponent]; }); // ...   } 

الآن كل شيء موجود في هذا المجال الثابت. تحتوي سمة template على ما يعادل ngfactory المألوف ، مع بنية مختلفة قليلاً. سيتم تشغيل وظيفة template ، كما كان من قبل ، عند أي تغيير ، ولكن الآن لديها وضعين:

  • وضع الإنشاء: المكون يتم إنشاؤه للتو ، ويحتوي على عقد DOM الثابتة التي يجب إنشاؤها
  • يتم تنفيذ باقي الوظيفة مع كل تغيير (إذا لزم الأمر ، يتم تحديث مصدر الصورة وعقدة النص).

ماذا تغير ذلك؟

الآن يتم @Injectable جميع الديكورات مباشرة في فصولهم (نفس الشيء بالنسبة لـ @Injectable و @Pipe و @Directive ) ، @Directive ، ما عليك سوى معرفة الديكور الحالي. أطلق فريق Angular على هذه الظاهرة اسم "مبدأ المنطقة": لإعادة تجميع المكون ، لا تحتاج إلى إعادة تحليل التطبيق.

يتم تقليل الرمز الذي تم إنشاؤه بشكل طفيف ، ولكن الأهم من ذلك ، أنه من الممكن التخلص من عدد من التبعيات ، وبالتالي تسريع إعادة التجميع إذا تغير أحد أجزاء التطبيق. بالإضافة إلى ذلك ، مع جامعي الحديث ، على سبيل المثال ، Webpack ، يتبين أن كل شيء أجمل بكثير: يتم قطع الرمز غير الفعال بأمان ، تلك الأجزاء من الإطار التي لا تستخدمها. على سبيل المثال ، إذا لم يكن لديك قنوات في التطبيق ، فلن يتم تضمين الإطار المطلوب لتفسيرها في المجموعة النهائية.

لقد اعتدنا على جعل الكود الزاوي ثقيلًا. في بعض الأحيان لا يكون الأمر مخيفًا ، لكن Hello World تزن 37 كيلوبايت بعد التصغير والضغط كثيرًا. عندما تكون Ivy مسؤولة عن إنشاء الرمز ، يتم قطع الرمز غير الفعال بشكل أكثر فاعلية. الآن Hello World بعد ضغط التصغير إلى 7.3 كيلو بايت ، وبعد الضغط - فقط إلى 2.7 كيلو بايت ، وهذا فرق كبير. تطبيق TodoMVC بعد الضغط - 12.2 كيلو بايت فقط. هذه بيانات من فريق Angular ، ولم يتمكن الآخرون من العمل معنا ، نظرًا لأن Ivy للعمل كما هو موضح هنا ، لا تزال بحاجة إلى تصحيحه يدويًا.

لمزيد من التفاصيل ، راجع هذا الحديث مع ng-conf.

التوافق مع المكتبات الموجودة

قد تكون مهتمًا بما يلي: ماذا سيحدث للمكتبات التي تم نشرها بالفعل بالتنسيق القديم إذا تم استخدام Ivy في مشروعك؟ لا تقلق: سيقوم المحرك بعمل نسخة متوافقة مع Ivy من تبعيات مشروعك ، حتى لو تم تجميعها بدون Ivy. لن أفضح الداخل الآن ، ولكن يجب أن تكون جميع التفاصيل شفافة.

الميزات الجديدة

دعونا نفكر في الفرص الجديدة التي سنحصل عليها عند العمل مع محرك العرض هذا.

الخصائص الخاصة في القوالب

يضيف محرك جديد ميزة جديدة أو تغيير محتمل.
يرتبط هذا الموقف ارتباطًا مباشرًا بحقيقة أن وظيفة القالب مضمنة في المجال الثابت للمكون: الآن يمكننا استخدام الخصائص الخاصة لمكوناتنا في القوالب. كان هذا مستحيلًا في السابق ، حيث اضطررنا إلى إبقاء جميع حقول وأساليب المكون المستخدم في القالب علنية ، وقد وقعوا في فئة منفصلة ( ngfactory ). عند الوصول إلى خاصية خاصة من فئة أخرى ، سيفشل تجميع TypeScript. الآن هو في الماضي: نظرًا لأن وظيفة القالب في حقل ثابت ، يمكنها الوصول إلى الخصائص الخاصة للمكون.

لقد رأيت تعليقًا من أعضاء فريق Angular حول حقيقة أنه لا يُنصح باستخدام الخصائص الخاصة في النماذج ، على الرغم من أن هذا ممكن الآن - نظرًا لأنه قد يكون محظورًا مرة أخرى في المستقبل ... وبالتالي ، فمن الأفضل أن تستمر في استخدام الحقول العامة فقط في النماذج! على أي حال ، أصبحت اختبارات وحدة الكتابة أسهل الآن ، لأن الاختبار يمكن أن يتحقق من حالة المكون دون حتى إنشاء DOM والتحقق منه.

i18n في وقت التشغيل

يرجى ملاحظة: يفتح محرك العرض الجديد أخيرًا فرصة طال انتظارها لنا ويعطي "i18n في وقت التشغيل". في وقت كتابة هذا التقرير ، كانت لا تزال غير مستعدة تمامًا ، لكننا رأينا العديد من الالتزامات في وقت واحد ، وهذه علامة جيدة!
الشيء الرائع هو أنك لست مضطرًا إلى تغيير تطبيقك كثيرًا إذا كنت تعمل بالفعل مع i18n. ولكن الآن لا تحتاج إلى إعادة إنشاء التطبيق لكل لغة تخطط لدعمها - ما عليك سوى تحميل JSON مع ترجمات لكل لغة ، وستتولى Angular الباقي!

مكتبات AoT

في الوقت الحالي ، يجب أن تنشر مكتبة تم إصدارها في NPM ملف metadata.json ولا يمكنها نشر كود AoT لمكوناته. هذا أمر محزن ، حيث يتم تمرير التكاليف المرتبطة بمثل هذا التجميع إلى تطبيقنا. مع Ivy ، ليست هناك حاجة لملف البيانات الوصفية ، وسوف يتمكن مؤلفو المكتبة الآن من نشر كود AoT الخاص بهم مباشرة إلى NPM!

مسارات مكدسة محسنة

الآن يجب أن تعطي الشفرة التي تم إنشاؤها آثار مكدسات محسنة ، إذا واجهت مشكلة في القوالب الخاصة بك - ينتج عن ذلك خطأ واضح يشير إلى سطر القالب الذي حدث فيه. يمكنك أيضًا تعيين نقاط توقف في القوالب وتتبع ما يحدث حقًا في Angular.

NgModule سيختفي NgModule ؟

لا يزال هذا احتمالًا بعيدًا ، ولكن ربما في المستقبل سيكون من الممكن الاستغناء عن NgModules. العلامات الأولى لمثل هذه التغييرات هي مزودو الأشجار المهتزون ، ومن المنطقي أن نفترض أن Ivy لديها جميع الكتل الأساسية الضرورية لأولئك الذين هم على استعداد للتخلي عن NgModules تدريجيًا (أو ، على الأقل ، جعلها أقل استجابة). صحيح ، كل هذا لا يزال في المستقبل ، سنكون صبورين.

لن يكون هناك العديد من الميزات الجديدة في هذا الإصدار ، لكن Ivy بالتأكيد مثيرة للاهتمام للمستقبل. جربها - أتساءل كيف ستحبها!

Source: https://habr.com/ru/post/ar419389/


All Articles