AngularJS: `template` vs.` templateUrl`

V posledních několika měsících jsem zkoumal různé způsoby, jak zlepšit běhový výkon obřího SPA, na kterém pracuji v Domo. Udělali jsme nějaký vážný pokrok, ale s miliónem řádků kódu v jedné SPA nebyly některé změny vždy snadné. Jeden z členů našeho týmu provedl objev, aby pomohl přidat lazyloading do projektů AngularJS, a my jsme do toho značně investovali. Několik různých členů týmu (Jason a Tim) se pustilo do toho, aby nám pomohlo změřit čas potřebný k úplné inicializaci naší aplikace. Použili jsme také webpack k zefektivnění sestavení a ke změně některých vzorců, které používáme. Když jsme kombinovali webpack s ocLazyloadem, našli jsme pro AngularJS vážnou výhru.

Tento minulý týden jsem převzal úkol změnit všechna prohlášení o šablonách komponent / směrnice a změnit je z šablonyUrl na šablonu. Místo ručního přesunutí všech šablon z jejich samostatných souborů .html do příslušných souborů JS jsme se rozhodli použít zavaděč webpacků a vyžadovat šablony jako vložené řetězce. Abychom to lépe vysvětlili ... ukážu vám, co tím myslím. Ukázková komponenta AngularJS je následující:

Jak vidíte, v prvním příkladu je komponenta, která k načtení šablony používá šablonuUrl. To je v nejlepším případě problematické, IMO. To znamená, že budete muset nasadit soubor foo / bar / myComponent.html do výroby, aby vaše produkční aplikace mohla načíst fragment šablony pomocí druhého požadavku na síť, nebo to znamená, že budete muset přidat sestavení krok, který najde všechny instance templateUrl a přivede tyto šablony do AngularJS templateCache. Obě tato řešení mají problémy.

Problémy s prvními jsou zřejmé: pokud všechny vaše šablony ve výrobě vyžadují samostatnou síťovou žádost, aby je získaly, pak by načtení jakéhokoli jediného pohledu vyžadovalo N síťových požadavků, aby získaly všechny pohledy, kde N je počet komponent / direktivy / ngIncludes in your view.

Problémy s druhým jsou, že kroky sestavení, i když jsou velmi užitečné, načtou všechny vaše šablony do vašeho hlavního balíčku webpack. To znamená, že i když máte v úmyslu lazyload komponentu nebo celou část komponent, jejich šablony budou stále načteny s vaším hlavním balíčkem. Nemůžete tedy plně využít výhod plynoucích z lazyloadingu.

Vzhledem k mnoha stovkám a stovkám šablon, které máme v našem projektu, ani jedna z nich nebyla proveditelná. Potřebovali jsme něco jiného. Potřebovali jsme něco, co by nám umožnilo načíst naše šablony efektivně, bez samostatných požadavků na síť pro každou z nich, a zároveň nám umožnit plně lazyloadovat stejné šablony. Proto jsme se rozhodli podívat se na použití webpack loader, který by nám umožnil požadovat naše šablony do našich komponentů jako inline řetězce HTML / úhlových šablon.

Výhody

Použitím webového balíku html-loader k načtení všech souborů .html jsme zjistili, že se nám podařilo efektivně načíst naše šablony a zároveň nám umožnit plně využít výhod lazyloadingu. Při použití syntaxe template: požadavku ('foo / bar / my.html') nahradí webpack váš příkaz požadavku funkcí, která se nazývá a vrací řetězcem šablony. Vzhledem k tomu, že šablona je nyní poskytována jako řetězec html, pokud lazyload komponentu, bude také šablony lazyloaded. To je přesně to, co jsme potřebovali. Objevili jsme však několik dalších výhod, jejichž objev vedl tento příspěvek.

  • Rychlejší inicializace komponenty - Pokud použijete jako šablonu vložený řetězec, může se komponenta inicializovat synchronně. Při použití templateUrl si AngularJS vyžádá šablonu z templateCache. Vzhledem k tomu, že templateCache již může mít šablonu ve své mezipaměti, nebo může být nutné ji získat přes síť, aby bylo možné ji získat, vyžádání šablony z mezipaměti je proces, ke kterému dochází asynchronně. I když je šablona již v mezipaměti, templateCache vrátí již uloženou šablonu pomocí volání založeného na slibu. To znamená, že součást nelze inicializovat ve stejné smyčce událostí. Požadavek na templateCache bude vždy umístěn na další smyčku událostí, a to i v nejlepším případě. To znamená, že součást může začít inicializovat, požádat o její šablonu a dokončit inicializaci v další smyčce událostí. Když ale použijete vložený řetězec, součást již má připravenou šablonu, takže může zahájit a ukončit inicializaci ve stejné smyčce událostí. To se nemusí zdát významné, ale mělo to několik neočekávaných výsledků, které jsme museli kompenzovat. - Komponenty se inicializují rychleji - které zní skvěle, AIR? No, je to úžasné. To však znamená, že některé z vašich komponent, které vždy měly definované vstupní hodnoty, když se může jejich inicializace rozbít, mohou způsobit, že stejné hodnoty tam ještě nemusí být. Měli jsme několik zlomků komponent kvůli nedefinovaným hodnotám vstupních vazeb. Abychom zjistili aktualizaci vstupních hodnot, museli jsme tyto komponenty změnit tak, aby používaly $ watch nebo $ onChanges. - Testy jednotek budou probíhat odlišně - Protože se psaní testů změní, když provádíte synchronní test vs asynchronní test, test pro tyto komponenty se určitě může změnit. Například v Mocha, pokud je váš test asynchronní, do testu vložíte hotovou metodu a po dokončení testu ji zavoláte. Zjistili jsme, že testy nyní probíhaly synchronně, což znamenalo, že již není třeba provádět injekci. Dále, a je trapné to přiznat, ale měli jsme testy, které byly psány synchronně, ale s šablonami asynchronními, tyto testy NIKDY NIKDY nebyly úspěšně dokončeny. Takže když jsem provedl změny, abych vložil šablony, tyto testy se začaly úspěšně spouštět a namísto absolvování byly FAILING !!!! Nejprve jsem si myslel, že jsem všechny ty testy přerušil. Teprve po 5 hodinách pokousání kolem jsem si uvědomil, že tyto testy nikdy neproběhly. Takže nyní, když používáme inline šablony, máme nyní větší pokrytí testem.
  • html-loader používá html minifier - tato malá skutečnost okamžitě snížila velikost našich šablon o 19% v celé aplikaci. To je tak vynikající a je to opravdu něco, co jsme měli dělat dlouho. Analyzuje také šablony a pomohl nám najít několik desítek šablon, které obsahovaly neplatné html. Věci jako: třída "bla", kde = chyběl. Nebo atribut = {{něco}}, kterému chybí uvozovky kolem toho všeho. Jakmile jsem je opravil, sestavení fungovalo znovu.
  • ng-include byly stále rozbité - Zatímco šablony komponent nyní fungovaly, ng-include byly nyní rozbité. Museli jsme pro ně něco vymyslet. Proto jsme vytvořili malý vlastní zavaděč, který přinese šablonu do templateCache. Naše interní postupy nám říkají, abychom nepoužívali ng-include, ale stále máme mnoho 3letého kódu, který je obsahuje. Takže namísto toho, aby to všechno v tomto odevzdání vyvstalo, použil jsem tento nový zavaděč a šel jsem do každé části aplikace, která má ng-include a načítala šablonu pro tuto sekci, jak jsem viděl níže. To znamená, že v tomto novém procesu se také starají o ng-include.

Použitý JSCodeShift

Plně doporučuji používat webpack pro AngularJS aplikace a používat html-loader, aby vaše šablony byly inline, což znamená, že budete muset změnit instance templateUrl na instance šablony. Protože všechny vypadají velmi odlišně, rozhodl jsem se, že se jedná o velmi dobrý případ použití JSCodeShift, projektu z Facebooku, který vám umožní procházet AST a programově nahradit všechny instance za vás. Můžete si to představit jako Najít a nahradit na steroidech, Vstřikováno w / Více Steroidů. Bylo opravdu snadné napsat skript, který našel a aktualizoval všechna tato použití templateUrl: ‘some / url / to.html s template: vyžadují (). Programově jsem mohl změnit 90% použití (asi 700 souborů) a posledních 70 jsem musel dokončit ručně. Mohl jsem napsat ten kód, abych dokončil těch dalších 70, ale usoudil jsem, že bych je mohl udělat jednodušší rukou, než když bych se je snažil kódovat každý zvlášť. Rychlá poznámka, AST Explorer je absolutní nutností při používání JSCodeShift. Bez něj bych nebyl schopen dosáhnout žádného pokroku.

Závěr

Stáhněte si své aplikace AngularJS na sestavení webového balíčku a dejte si čas, abyste je přivedli k používání šablon html. Použijte šablonu místo templateUrl, a pokud jste tak již neučinili, přestaňte používat ng-include. A pak lazyload, lazyload, lazyload! Někdy lidé rozlišují mezi zpožděným a líným nakládáním. Mám na mysli jak zpožděné načítání, tak líné načítání, když řeknu „líné zatížení“. Je to vaše nejlepší změna při zkrácení času na První smysluplnou malbu a zkrácení času na aplikaci, se kterou může uživatel interagovat. Hodně štěstí. Zpět na hlavu!