În acest articol, vom vorbi despre modul în care putem utiliza instrumentele de construcție CLI angular pentru a crea un plugin angular precompilat AOT, care poate partaja codul comun cu alte pluginuri și chiar funcționa cu Angular universal. Aceasta este o soluție neoficială, dar funcționează bine pentru cazul nostru.

aplicații

AngularInDepth se îndepărtează de Medium. Articolele mai recente sunt găzduite pe noua platformă inDepth.dev . Vă mulțumim că faceți parte din mișcarea profundă!

Iată o reprezentare simplă a ceea ce construim:

Aici avem o pagină simplă în care obținem configurarea pluginurilor dintr-un fișier plugins-config.json. Apoi încărcăm leneș pluginul AOT compilat (plugin1.js) care are o dependență de shared.js. Biblioteca partajată conține o componentă Tabs și Tabs din fabrici. Ceva mai târziu încărcăm al doilea plugin (plugin2.js) care reutilizează codul din biblioteca shared.js încărcată anterior.

Iată depozitul Github angular-plugin-architecture dacă doriți să aruncați o privire asupra codului sursă.

Folosim Angular CLI 7.3.6 în demo

Să nu așteptăm Ivy, ci să ne ocupăm de ViewEngine curent astăzi

  • De ce unghiular?
  • Scopul
  • Ce este un plugin?
  • De ce este atât de greu să creezi un plugin cu Angular?
  • Cerințe
  • Alte soluții
  • Către o soluție
  • Un singur pachet
  • Externe
  • Exporturi dinamice
  • Construiți un plugin
  • Bibliotecă unghiulară externă și partajată
  • Încărcați pluginul (client, server)
  • Cum se redă un plugin?
  • Cum se izolează aplicația principală de erori într-un plugin?

Echipa și comunitatea Angular continuă creșterea rapidă a ecosistemului său.

Angular ne ajută să ne structurăm codul într-un mod consecvent, astfel încât fiecare dezvoltator nou să poată fi ușor implicat într-un proiect.

Ne place faptul că urmărim cele mai bune practici web cu Angular, gândiți-vă doar la dactilografiat, observabile, redare de pe server, lucrători web, încărcare diferențială, aplicație web progresivă (PWA), încărcare leneșă etc. Toate acestea ne ajută să adoptăm aceste caracteristici într-un mod rapid.

Există, de asemenea, mai multe caracteristici pe care ni le oferă Angular, cum ar fi sistemul de injecție de dependență încorporat, formele reactive, schemele și așa mai departe.

De aceea, de obicei, alegem Angular atunci când construim o aplicație de întreprindere.

Într-o zi, clientul nostru ne-a cerut să adăugăm o nouă funcție la aplicația sa Angular Universal existentă. El dorea să aibă un sistem de management al conținutului (CMS) conectabil. Scopul a fost de a adăuga posibilitatea de a extinde funcționalitatea aplicației curente, astfel încât un dezvoltator terță parte să poată dezvolta cu ușurință un nou modul și să-l încarce. Apoi, aplicația Angular ar trebui să o ridice fără a fi necesară recompilarea întregii aplicații și redistribuire.

Pur și simplu, trebuie să dezvoltăm un sistem de pluginuri.

Sistemele de pluginuri permit extinderea unei aplicații fără modificarea codului de bază al aplicației.

Sună simplu, dar scrierea unui plugin cu Angular este întotdeauna o provocare.

Unul dintre colegii mei, care a lucrat cu AngularJS cu mult timp în urmă, a spus că a văzut o aplicație Angular 2 scrisă în es5 simplu (hmm . poate că a găsit fișierul meu sau vechiul meu repo). Așa că a sugerat crearea unui modul Angular în es5, introducerea acestuia într-un folder și ca aplicația principală să o facă să funcționeze cumva.

Nu mă înțelegeți greșit, dar sunt cu Angular, deoarece 2 alfa și Angular 2 (sau doar Angular) este un lucru complet nou.

Desigur, principala capcană aici este compilarea Ahead Of Time (AOT). Principalul avantaj al utilizării AOT este o performanță mai bună pentru aplicația dvs.

Am văzut multe exemple care folosesc JitCompiler pentru construirea unei arhitecturi conectabile. Nu așa vrem să mergem. Trebuie să ne păstrăm rapid aplicația și să nu includem codul compilatorului în pachetul principal. De aceea nu ar trebui să folosim es5, deoarece numai codul TypeScript poate fi precompilat AOT. De asemenea, implementarea actuală a Angular AOT se bazează pe domeniul tranzitiv @NgModule și necesită ca toate să fie compilate împreună. Toate acestea îngreunează lucrurile.

O altă capcană este că trebuie să partajăm codul între pluginuri pentru a evita duplicarea codului. Ce fel de duplicare putem lua în considerare? Distingem două tipuri de cod care pot fi duplicate:

  • Codul pe care îl scriem sau codul pe care îl luăm din node_modules
  • Codul produs de AOT, gândiți-vă la fabricile de componente și module (component.ngfactory.js și module.ngfactory.js). De fapt, aceasta este o cantitate imensă de cod.

Pentru a evita aceste duplicări de coduri, trebuie să ne ocupăm de modul în care ViewEngine generează o fabrică.

Dacă nu știți, ViewEngine este motorul curent de redare angulară

Problema este că codul generat de compilatorul Angular poate indica ViewFactory dintr-o altă bucată de cod generat. De exemplu, iată cum este asociată definiția elementului cu ViewDefinitionFactory (cod sursă Github)

Deci, acest lucru are ca rezultat obținerea de duplicate ale tuturor fabricilor din biblioteca partajată.

Deci, când discutam despre sistemul de pluginuri Angular, ar trebui să avem în vedere următoarele:

  • AOT
  • Evitați codul duplicat (pachete precum @ angular/core, rxjs, tslib)
  • Utilizați o bibliotecă partajată în toate pluginurile. Dar, NU EXPEDIAȚI fabrici generate din acea bibliotecă partajată din fiecare plugin. Mai degrabă, refolosiți codul bibliotecii și fabricile.
  • Pentru importul modulelor externe trebuie doar să știm un lucru: calea fișierului lor de pachete.
  • Codul nostru ar trebui să recunoască modulul și să plaseze pluginul în pagină.
  • Suportă redarea pe partea de server
  • Încărcați modulul numai atunci când este necesar
  • Acceptați același nivel de optimizare pe care ni-l oferă Angular CLI

Toate aceste considerații ne-au condus la propria noastră soluție.

Există diferite abordări, dar nu au părțile cruciale: suport pentru AOT, cod optimizat și cod non-duplicat.

Iar una dintre soluțiile apropiate nevoilor noastre este: https://github.com/iwnow/angular-plugin-example

Folosește pachetul pentru a produce pachetul de pluginuri în format umd.

Cu toate acestea, iată dezavantajele pe care le văd cu această abordare:

  • ❌ nu folosește tehnicile de optimizare oferite de Angular CLI: nu elimină decoratorii angulari și nu rulează buildOptimizer.
  • ❌ duplică fabricile dacă folosim componente partajate în fiecare plugin.

Din fericire, Angular este foarte extensibil prin scripturi personalizate.

De la Angular 6, există posibilitatea de a vă conecta la procesul de compilare folosind constructori. Ne permite să adăugăm o configurație webpack personalizată, incluzând toate avantajele pe care vi le puteți imagina dintr-o configurație webpack vanilie.

Așa că m-am gândit că ar putea fi o idee bună să scriu un constructor personalizat pentru a construi pluginuri.

CLI angular acceptă generarea de biblioteci. Dar ng-packagr, care este folosit pentru a construi acea bibliotecă, generează multe artefacte de care nu avem nevoie. (Da, știu că urmează Angular Package Format (APF)). Și acele artefacte pot fi consumate numai de alte aplicații angulare.

Și aici m-am gândit că aș putea folosi o aplicație Angular pentru a-mi construi pluginurile. Prin aplicație, mă refer la o aplicație care este generată prin utilizarea unei comenzi CLI precum ng generate application. Această abordare este benefică, deoarece ne oferă același nivel de optimizare pe care îl face Angular CLI. De exemplu, nu vom primi decoratori specifici unghiului după AOT.