Донякъде разбирам основата на Firefox за изчакване как ще се зареди Javascript, преди да внедря html импортиране, след като изпитах този проблем от първа ръка.

Опровержение

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

Заден план

И така, за да дам малко предистория, създадох инжектор на скриптове на вулканизиран полимерен уеб компонент. Компонентът Polymer web, който създадох, има зависимости от polymer, iron-ajax, iron-flex-layout и някои хартиени елементи. Сега причината, поради която направих това, е, че исках да имам лесен начин за инжектиране на полимерни елементи във всякакви обикновени стари html сайтове, без да използвам обичайния начин за импортиране и проверка на полифили, подобно на начина, по който бихте инжектирали Google Analytics във вашата страница (скриптът за инжектиране всъщност прави импортирането и проверката на polyfills вместо вас, като го зарежда от CDN източници).

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

Проблемът: Дублиране и пререгистрация

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

Сега това също се случва, ако се случи да импортирате два различни html файла на уеб компонента, които зареждат един и същ javascript файл чрез <script src>: Той оценява скрипта два пъти.

Предимството на импортирането на JS модули пред импортирането на HTML. Но…

Това е много по-различно, когато „импортирате“ или „изисквате“ определен JS модул в Node JS, който много се имитира, когато използваме Browserify или Webpack за браузъра. Когато импортирате JS файл (да кажем a.js) дори от различни JS файлове (да кажем b.js и c.js) и сте импортирали b.js и c.js, a.js не се оценява два пъти.

Но това е начинът, по който работи и HTML импортирането. Да приемем, че a.html се импортира от b.html и c.html, а вие сте импортирали b.html и c.html. a.html не се зарежда (и оценява) два пъти, защото вече се намира в паметта на браузъра.

Дотук добре. Но защо не работи?

Проблемът възниква, когато a.html е вулканизиран вътре в b.html и c.html. Когато Polymer се вулканизира, скриптовете, които позволяват регистрацията на компонент, който се намира в оригиналния файл, се копират както в b.html, така и в c.html. Това означава, че скриптовете се оценяват два пъти: веднъж в b.html и другия в c.html. Тъй като няма съществуващ a.html, който да се постави в паметта на браузъра като „отделен файл“, няма начин браузърът да знае, че даден уеб компонент вече е регистриран и няма да има нужда да преоценява скрипта, дори ако просто регистрира същия елемент.

И така, как JS модулите го правят?

Силата (и проклятието) на JS модулите идва от модулите с уникални имена. Модулите, заредени в webpack или дори в NPM, са глобално уникално именувани (по отношение на вашата машина/браузър). Това означава, че знае, че вече е в паметта поради уникалното име. И уникалното име (използвано като параметър във функцията require()) е свързано с пътя на файла. Това означава, че ако нещо друго трябва да използва същото име, то сочи към същия път на файла (който вече е оценен и не е необходимо да се зарежда повече)

И има малка разлика между импортирането на JS модули и импортирането на HTML: импортирането на HTML знае само, че вече е заредило уникално име на файл. След като файлът вече се намира в паметта на браузъра, той повече няма да го зарежда, нито да го оценява. Но импортирането на HTML не свързва уникалния файл с компонентите, кодирани в него. Това се дължи на начина, по който работи вулканизацията: можете да поставите няколко компонента във файл (подобно на свързване на набор от javascript файлове в един файл). Компонентите се регистрират отделно от HTML файла. Това означава, че след като компонентите бъдат регистрирани, ще има конфликт, ако го регистрирате отново.

Така че защо просто не игнорирате, ако вече е регистриран?

Защото наистина не трябва да се случва. Грешката е там, за да не регистрирате отново същия компонент (или по-лошо друг компонент със същото име). Игнорирането му е игнориране, че компонент със същото име се опитва да се регистрира в библиотеката с елементи на браузъра, което е опасно.

Можем ли тогава просто да обхванем импортирания, така че да няма конфликт?

Мисля, че това е добър въпрос. Импортирането на JS модули работи, защото регистрирате модула в среда с обхват. Функцията за изискване всъщност е просто да стартира JS файла. И използвайки името на модула (или пътя на js файла) като уникално име, той може да запомни дали вече е оценен или не. Ако не е, той изпълнява JS файла. Ако има, тогава той просто получава крайното състояние на файла в паметта. Но каквото и да прави, ние все още го записваме в локална променлива в обхватна среда.

Регистрирането на компоненти в браузъра не работи по този начин. Браузърът, за да изобрази елемент в своя DOM, трябва да има името на елемента в „глобален обхват“. Това означава, че дори ако заредите компонент (да кажем <first-component>) в друг компонент (да кажем <second-component>), браузърът автоматично изобразява <first-component>, дори ако е бил използван в тялото на страницата. <first-component> не е предназначен да се използва само вътре в <second-component>, той може да се използва и на други места, стига да е регистриран в библиотеката с компоненти на браузъра.

И така, как да поправя? HTTP 2.0

Един от начините да поправите това е да НЕ вулканизирате. Това означава отделно зареждане на файловете. Това противоречи на принципа за свързване на файловете, така че да отнема само 1 повикване от сървъра. Но със силата на HTTP 2.0, извикването на няколко файла е лесно и може да има някои предимства пред конкатенацията, тъй като повечето пакети за конкатенация добавят някакъв код отгоре. HTTP 2.0 премахва просто извикването на 1 файл (който включва няколко файла), защото вече можете да извиквате няколко файла наведнъж. Като се има предвид това, сега можем да извикаме зависимостта на всеки компонент от собствения му файл и ако файлът е бил зареден, той няма да бъде оценен отново. Това решава проблема, който имам по-горе.

Но какво е кикърът?

3G мрежи. Или по-лоши мрежи. Когато се промъкне бавен интернет (особено в моята страна), броят на файловете, които се зареждат едновременно, оказва влияние върху скоростта. И ако има дълбоки зависимости в едно извикване на компонент, този компонент няма да започне да се изобразява, докато не бъдат заредени всички зависимости.

Имахме опит един от моите сайтове да се зарежда повече от 2 минути в лоша мрежа. И въпреки че все още нямам данните, за да го архивирам, имам предчувствие, че може да е невулканизираната версия и HTTP 2.0.

Ще има ли дългосрочно решение, ако искам отново да използвам вулканизация?

Засега и аз съм стъписан. Начинът, по който работят браузърите, когато оценяват заредените скриптове, е просто да ги оценяват отново, дори ако сте заредили от същия файл. И това всъщност е причината, поради която проблемът възниква на първо място: защото дефиницията за това как да се изобрази компонентът се намира в javascript файла, който също беше вулканизиран.

Ако има начин да разберете, че определен компонент, който е част от вулканизирания файл, е подобен на друг компонент, който е част от различен вулканизиран файл (като например използването на неговото репо в github). Тогава може да проработи. Но също така ще добави друга спецификация в спецификациите на HTML.

Е, кажете ми какво мислите. Ще има ли поправка или просто ще го използваме по този начин?