AngularJS: `шаблон` срещу` templateUrl`

През последните няколко месеца разглеждам различни начини да подобря изпълнението на гигантския SPA, върху който работя в Domo. Постигнахме сериозен напредък, но с милион реда кодове в един SPA, някои от промените не винаги са лесни. Един от членовете на нашия екип направи откритието, за да помогне за добавяне на мързеливо зареждане в проектите на AngularJS и ние много инвестирахме в това. Няколко различни членове на екипа (Джейсън и Тим) се впускат да ни помогнат да измерим времето, необходимо на нашето приложение, за да се инициализираме напълно. Използвахме и webpack, за да опростим съставянето, както и да променим някои от моделите, които използваме. Когато комбинирахме уебпакет с ocLazyload, открихме сериозна печалба за проектите на AngularJS.

Тази минала седмица поех задачата да променя всички декларации за шаблон на компонент / директива и да ги променя от templateUrl в шаблон. Вместо да преместваме ръчно всички шаблони от отделните им .html файлове в съответните им JS файлове, решихме да използваме уеб-пакет за зареждане и да изискваме шаблоните като вградени низове. За да ви обясня по-добре ... нека ви покажа какво искам да кажа. По-долу е примерен компонент AngularJS:

Както можете да видите, в първия пример има компонент, който използва templateUrl за зареждане на неговия шаблон. Това е в най-добрия случай проблематично, IMO. Това означава, че ще трябва или да разгърнете файла foo / bar / myComponent.html за производство, така че вашето приложение за производство да може да зареди фрагмента на шаблона чрез второ искане на мрежа, за да го получите, ИЛИ това означава, че ще трябва да добавите изграждане стъпка, която ще намери всички екземпляри на templateUrl и ще внесе тези шаблони в AngularJS templateCache. И двете от тези решения имат проблеми.

Проблемите с първия са очевидни: ако всичките ви шаблони в производството изискват отделна мрежова заявка, за да ги получите, тогава за да заредите всеки един изглед, ще са нужни N мрежови заявки за получаване на всички изгледи, където N е броят на компонентите / директиви / ngВключва във вашия изглед.

Проблемите с второто са, че стъпките за изграждане, докато са супер удобни, ще заредят всичките ви шаблони в основния ви пакет за уебпакети. Това означава, че дори когато възнамерявате да мързелувате компонент или цял раздел от компоненти, техните шаблони все още ще бъдат заредени с основния ви пакет. Така че не можете да се възползвате напълно от предимствата, които получавате от мързеливо зареждане.

Като се имат предвид многото стотици и стотици шаблони, които имаме в нашия проект, нито един от тях не е осъществим. Трябваше ни нещо друго. Трябваше ни нещо, което да ни позволи да заредим нашите шаблони ефективно, без отделни мрежови заявки за всеки от тях, като същевременно ни позволява да разтоварваме напълно същите тези шаблони. Затова решихме да разгледаме използването на webpack loader, който ще ни позволи да изискваме нашите шаблони в нашите компоненти като вградени низове на HTML / Angular шаблони.

Ползите

Използвайки webpack html-loader за зареждане на всички .html файлове, ние открихме, че успяваме ефективно да заредим нашите шаблони, като същевременно ни позволи да се възползваме напълно от мързеливото зареждане. Когато използвате синтаксиса на шаблон: изисквате ('foo / bar / my.html'), webpack замества вашето заявление за изискване с функция, която се извиква и връща с низ за шаблона. Тъй като шаблонът вече е предоставен като html низ, ако мързелувате компонента, шаблонът също ще бъде мързелив. Точно това ни трябва. Ние обаче открихме няколко други предимства, откриването на които предизвика този пост.

  • По-бързо инициализиране на компоненти - Когато използвате вграден низ като шаблон, компонентът може да се инициализира синхронно. Използвайки templateUrl, AngularJS ще поиска шаблона от templateCache. Тъй като templateCache може вече да има шаблона в кеша си или може да се наложи да отидете в цялата мрежа, за да го получите, искането на шаблон от кеша е процес, който се случва асинхронно. Дори ако шаблонът вече е в кеша, templateCache ще върне вече кеширания шаблон чрез повикване, базирано на обещание. Това означава, че компонентът не може да се инициализира в едно и също събитие. Заявката към templateCache винаги ще бъде поставена в следващия цикъл на събитията, дори и в най-добрия сценарий. Това означава, че компонентът може да започне да се инициализира, да поиска шаблон и след това да завърши инициализирането в следващия цикъл на събитията. Но когато използвате вграден низ, компонентът вече има готов шаблон, така че може да започне и завърши инициализацията му в същия цикъл на събитията. Това може да не изглежда значително, но имаше няколко неочаквани резултата, които трябваше да компенсираме. - Компонентите се инициализират по-бързо - което звучи страхотно, AIR? Е, страхотно е. Това обаче означава, че някои от вашите компоненти, на които винаги са били дефинирани входните им стойности, когато инициализацията им може да се счупи, причинява същите тези стойности да не са там все още. Имахме няколко прекъсвания на компонента, поради неопределените стойности на свързване на входа. Трябваше да променим тези компоненти, за да използваме $ watch или $ onChanges, за да открием актуализацията на входните стойности. - Тестовете на единици ще се изпълняват по различен начин - Тъй като писмените тестове се променят, когато правите синхронен тест спрямо асинхронен тест, тестът за тези компоненти определено може да се промени. Например, в Mocha, ако вашият тест е асинхронизиран, вие инжектирате готовия метод в теста си и го извиквате, след като тестът е направен. Установихме, че тестовете сега се изпълняват синхронно, което означава, че вече не е необходимо да се направи инжектирането. Освен това е неудобно да признаем това, но имахме тестове, които бяха написани синхронно, въпреки че шаблоните се асинхронизират, тези тестове НИКОГА НЕ СЕ ПРЕВЪРШИХ успешно. И така, когато извърших промените за вграждане на шаблоните, тестовете започнаха успешно да се стартират и вместо да преминат, те се провалиха !!!! Отначало мислех, че съм нарушил всички тези тестове. Едва след 5 часа пътешествие разбрах, че тези тестове никога не са минавали. Така че всъщност сега имаме по-голямо покритие за тестове сега, когато използваме вградени шаблони.
  • html-loader използва html minifier - Този малък факт моментално намали размера на нашите шаблони с 19% в цялото приложение. Това е толкова забележително и наистина е нещо, което би трябвало да правим отдавна. Той също така анализира шаблоните и ни помогна да намерим няколко десетки шаблона, които имат невалиден html в тях. Неща като: клас "бла", където = = липсваше. Или атрибут = {{something}}, в който липсват цитатите около него. След като ги оправих, изграждането работи отново.
  • ng-include все още бяха счупени - Докато шаблоните на компонентите вече работеха, ng-include вече бяха счупени. Трябваше да измислим нещо за тях. Затова създадохме малък персонализиран товарач, който ще внесе шаблона в templateCache. Вътрешните ни практики ни казват да не използваме ng-include, но все още имаме много код на възраст над 3 години, който ги съдържа. Така че, вместо да рефакторирам всичко това в този ангажимент, използвах този нов товарач и отидох във всяка секция на приложението, която има ng-include и зареди шаблона за този раздел, както показах по-долу. Това означава, че в този нов процес се грижат и ng-include.

Използван JSCodeShift

Напълно препоръчвам да използвате уебпакет за приложенията на AngularJS и да използвате html-loader, за да приведете шаблоните си в реално време, което означава, че ще трябва да промените вашите шаблониUUrl на екземпляри на шаблони. Тъй като всички те изглеждат много различно, реших, че това е много добър случай на използване за JSCodeShift, проект от Facebook, който ви позволява да обходите AST и да замените програмно всички инстанции за вас. Можете да го мислите като Find & Replace On Steroids, Injected w / More Steroids. Наистина беше просто да напишете скрипта, който намери и актуализира всички тези употреби на templateUrl: 'some / url / to.html с шаблон: изисквам (). Успях да променя 90% от използванията програмно (около 700 файла) и трябваше да довърша последните 70 на ръка. Можех да напиша кода, за да завърша тези други 70, но реших, че мога да ги правя по-лесно на ръка, отколкото като се опитвам да ги кодирам поотделно. Бърза бележка, AST Explorer е абсолютно задължително условие, когато използвате JSCodeShift. Без него не бих могъл да постигна някакъв напредък.

заключение

Вземете вашите приложения AngularJS върху изграждане на уебпакет и поставете време, за да ги накарате да използвате html-loader за зареждане на вашите шаблони. Използвайте шаблон вместо templateUrl и ако вече не сте, спрете да използвате ng-include. И тогава, lazyload, lazyload, lazyload! Понякога хората правят разлика между забавено зареждане и мързеливо зареждане. Имам предвид както забавено зареждане, така и мързеливо зареждане, когато казвам „lazyload“. Това е най-добрата ви промяна, като намалите времето до Първозначителна боя и намалите времето до разполагане на приложение, с което потребителят може да взаимодейства. Късмет. Обратно на главите си!