Я параноик? Жестоко большой сайт Polymer после Vulcanize. Что я могу сделать, чтобы это исправить?

Может быть, я параноик. Я всегда хотел, чтобы мой код был как можно более компактным. Я всегда ориентирую свои веб-сайты на размер менее 1,5 МБ (все изображения сжаты и изменены в соответствии с размерами). сайт.

Я только что вулканизировал свой файл elements.html и пришел в ужас. Зверь весит 947 КБ без изображений, только чистый HTML и JS. У меня есть около 40 пользовательских элементов + пара каталогов элементов (и я даже не близок к созданию новых элементов). (GZip составляет 307,40 КБ из 947 КБ) (с использованием ASP.NET MVC5 и .NET 4.6).

При обычном 3G-соединении загрузка в Chrome 52 занимает около 5,15 секунды (что ужасно). Демонстрация Polymer Shop загружается очень быстро (‹3 секунды из холодного кеша в обычном 3G)

Во-первых, допустимо ли это? Я пытаюсь попасть до 3-х секундной отметки (или подобраться к ней как можно ближе).

Кроме того, есть много файлов JavaScript, которые загружаются как часть Vulcanize, которые мне не нужны.

Я видел этот Gist: Автоматическая отложенная загрузка вулканизации и полимеризации, но я не знаю, что делать. делать с этим.

Это импорт моего файла elements.html:

<link rel="import" href="../bower_components/app-route/app-route.html">
<link rel="import" href="../bower_components/app-route/app-location.html">


<link rel="import" href="../bower_components/app-layout/app-drawer-layout/app-drawer-layout.html">
<link rel="import" href="../bower_components/app-layout/app-drawer/app-drawer.html">

<link rel="import" href="./pgarena-drawer/pgarena-drawer.html">
<link rel="import" href="./pgarena-navbar/pgarena-navbar.html">
<link rel="import" href="./pgarena-auth/pgarena-oauth/pgarena-oauth.html">

<link rel="import" href="./routes/pgarena-app.html">

Затем во все мои пользовательские элементы (pgarena) встроено больше полимерных компонентов.

Я пробовал несколько комбинаций (только с моими элементами) и (только с показанными полимерными элементами), и у меня были разные результаты (как и ожидалось).

Я не знаю, что делать ... Прежде чем прибегать к хакерским вещам ... Есть рекомендации?


person Jose A    schedule 01.08.2016    source источник


Ответы (2)


Хорошо, люди, потерпите меня. Это будет длинный ответ. Он может стать немного волосатым. Прежде всего, это было решение Polymer 1.x. Я не знаю, что из этого изменилось для версии 2.0.

TL;DR: мы получаем URL-адреса .HTML и используем JavaScript для создания атрибута link (импорт HTML) для загрузки элемента. Мы проверяем с помощью Polymer, используя Polymer.isInstance(element), чтобы увидеть, был ли установлен объект или нет.

Вот код:

Чтобы это работало, я использовал iron-pages и пользовательский JavaScript.

У нас есть наше приложение, как показано ниже:

Примечание*: Следующий код у меня был в том же файле, вы можете разделить его по своему усмотрению.

<!-- Main Entry point for the application. This will work as the main "Controller"-->

<link rel="import" href="../../bower_components/polymer/polymer.html">

<link rel="import" href="../../bower_components/app-route/app-location.html">
<link rel="import" href="../../bower_components/app-route/app-route.html">
<link rel="import" href="../../bower_components/iron-pages/iron-pages.html">
<dom-module id="pgarena-app">
    <template>
        <pgarena-action-config></pgarena-action-config>
        <app-route route="{{route}}"
                   pattern="/:page"
                   data="{{data}}"
                   tail="{{tail}}">
        </app-route>
        <iron-pages selected="[[data.page]]" attr-for-selected="title" fallback-selection="404">
            <pgarena-home-app title="" route="[[tail]]"></pgarena-home-app>
            <pgarena-tournament-app title="tournaments" route="[[tail]]"></pgarena-tournament-app>
            <!--<pgarena-clash-app title="clash" route="[[tail]]"></pgarena-clash-app>-->
            <pgarena-account-app title="account" route="[[tail]]"><content></content></pgarena-account-app>
            <pgarena-teams-app title="teams" route="[[tail]]"></pgarena-teams-app>
            <div title="404">
                <h1>{{data.page}} could not be found!</h1>
            </div>
        </iron-pages>

    </template>
    <script>
        (function () {
            'use strict';
            Polymer({
                is: 'pgarena-app',
                ready: function () {
                    /* console.log("Route is ");
                     console.log(this.data.page);
                    console.log(this.tail);*/
                    document.addEventListener('iron-select',
                        function (event) {
                            /*
                            console.log("---------------------");
                            console.log(event);
                            console.log("---------------------");*/
                            var element = getSelectedElement(event);
                            var tagName = element.tagName.toLowerCase();
                            LazyLoad(Polymer, element, tagName, event.target);
                        });
                }
            });
        })();

    </script>

Сначала кое-что:

  1. Мое приложение называется: "pgarena-app"
  2. Я не знаю, было ли это исправлено, но у элемента app-route есть проблема с двусторонней привязкой данных. Это означает, что для железных страниц мне пришлось использовать двойные скобки [[]] для односторонней привязки данных.
  3. Маршрут приложения передает информацию из URL-адреса на железные страницы, чтобы он мог переключать различные элементы.
  4. Это не обязательно, и я не знаю, правильно ли это сделать. Я разделил свое приложение на «представления», которые сами по себе являются элементами. Они загружают все элементы, необходимые для этого «представления». Примечание. Это никакого эффекта при отложенной загрузке.

Обратите внимание, что элементы не включены в URL-адрес, потому что мы собираемся загружать их лениво.

Давайте перейдем к части JavaScript этого элемента:

<script>
    (function () {
        'use strict';
        Polymer({
            is: 'pgarena-app',
            ready: function () {

                document.addEventListener('iron-select',
                    function (event) {

                        var element = getSelectedElement(event);
                        var tagName = element.tagName.toLowerCase();
                        LazyLoad(Polymer, element, tagName, event.target);
                    });
            }
        });
    })();

</script>

Код здесь простой. Мы определяем наш элемент и прослушиваем событие выбора железа. Это сигнализирует нам о том, что была выбрана страница железа. Мы лениво загружаем элемент, если его там нет. Магия, стоящая за этим, кроется в пользовательском LazyLoad JavaScript, который приведен ниже.

<script>
        /**
        * Defines all the routes of the imports in here
        *
        * This is how it goes: The Key name is the tag name of the element.
        * The value is the relative URL from the elements folder.
        *
        * You then get the element's tag name and look for it.
        *
        * DO NOT PUT TRAILING SLASH BEFORE THE URL! Thanks :)
        **/
        var PGArena = PGArena || {};
        PGArena.LazyLoad =
        {
            "pgarena-home-app": "routes/home/pgarena-home-app.html",
            "pgarena-tournament-app": "routes/tournament/pgarena-tournament-app.html",
            "pgarena-account-app": "routes/account/pgarena-account-app.html",
            "pgarena-clash-app": "routes/clash/pgarena-clash-app.html",
            "pgarena-teams-app": "routes/teams/pgarena-teams-app.html",

            "pgarena-tournament-index-view": "views/tournament/pgarena-tournament-index-view/pgarena-tournament-index-view.html",
            "pgarena-tournament-list-view": "views/tournament/pgarena-tournament-list-view/pgarena-tournament-list-view.html",

            "pgarena-account-index-view": "views/account/pgarena-account-index-view/pgarena-account-index-view.html",
            "pgarena-account-login-view": "views/account/pgarena-account-login-view/pgarena-account-login-view.html",
            "pgarena-account-register-view": "views/account/pgarena-account-register-view/pgarena-account-register-view.html",
            "pgarena-account-confirm-email-view": "views/account/pgarena-account-confirm-email-view/pgarena-account-confirm-email-view.html",
            "pgarena-account-oauth-view": "views/account/pgarena-account-oauth-view/pgarena-account-oauth-view.html",

            "pgarena-clash-index-view": "views/clash/pgarena-clash-index-view/pgarena-clash-index-view.html",
            "pgarena-clash-brawl-view": "views/clash/pgarena-clash-brawl-view/pgarena-clash-brawl-view.html",

            "pgarena-teams-index-view": "views/team/pgarena-teams-index-view/pgarena-teams-index-view.html",
            "pgarena-teams-create-view": "views/team/pgarena-teams-create-view/pgarena-teams-create-view.html"

        };
        /**
        * This variable keeps track of all the vulcanized elements.
        *
        **/
        PGArena.Vulcanized = {

        }
        /**
        * Global Placeholder for checking which is the selected item of the iron-selectors that
         are ready for lazy loading.
        **/
        PGArena.IronSelected = {

        }

        /**
        * LazyLoad
        *
        * Lazy Loads the elements as needed. This function is triggered by iron-select
        * event. If the element is already registered, then it is not loaded again.
        *
        * Polymer => Dependency Injection of the Polymer object. (Polymer itself)
        * element => The element (DOM-wise: a.k.a tags with everything)
        * elementName => The element's name.
        * selectorTrigger => The element who triggered the select.
        **/
        function LazyLoad(Polymer, element, elementName, selectorTrigger) {
            if (Polymer.isInstance(element)) {
                // console.log(elementName + " is already registered ;)");
                return;
            } else {
                //console.log(elementName+" isn't registered. On its way for Lazy Loading!");
            }
            //console.log("Lazy Load Started");
            var hasProp = PGArena.LazyLoad.hasOwnProperty(elementName);
            if (!hasProp) {
                console.log("Property " + elementName + " not found for Lazy Loading");
                return;
            }

            var href = PGArena.LazyLoad[elementName];
            LoadImportAsync(href, elementName, selectorTrigger); 
        }

        function Spinner(elementName, active) {
            var paperId = 'js-' + elementName;
            var queryName = active ? elementName : paperId;
            var createElem = active ? 'paper-spinner-lite' : elementName;
            var elem = document.querySelector(queryName);
            var spinner = document.createElement(createElem);
                spinner.setAttribute('active', '');
            if (elem === null || elem === undefined)
                return;
            console.log("Element Name is");
            console.log(queryName);
            console.log("Element is");
            console.log(elem);

            console.log("Spinner is:");
            console.log(spinner);
            if (active) {
                spinner.setAttribute('id', 'js-' + elementName);

                console.log("replacing time");
                elem.parentNode.replaceChild(document.createTextNode("Caca"), elem);
                //elem.parentNode.replaceChild(spinner, elem);
            }
            else {
                console.log("Replaced");
                //elem.parentNode.replaceChild(elem, spinner);
            }

        }

        function ForcedLoad() {

        }
        /**
        * Loads the required import and appends it to the document. It really doesn't
        * matter where it is appended.
        *
        **/
        function LoadImportAsync(href, elementName) {

            var link = document.createElement('link');
            link.rel = 'import';
            link.href = getBaseUrl() + "/NodeJS/Polymer/app/elements/" + href;
            link.setAttribute('async', ''); // make it async!
            link.onload = function () { Spinner(elementName, false); }
            link.onerror = function (e) { console.log("There was an error loading " + elementName + ". Please Check the URL") };
            document.head.appendChild(link);
        }

        function getBaseUrl() {
            var pathArray = location.href.split('/');
            var protocol = pathArray[0];
            var host = pathArray[2];
            return protocol + '//' + host;
        }

        /**
        * On non-blink browsers (a.k.a Firefox , Edge, Internet Explorer)
        * The event.srcElement is undefined. We need to search for it ourselves.
        *
        * The way we do that is that we get the current targetted element which is the iron form.
        * Retrieve its selection mechanism and the supposed element's index.
        *
        * We proceed by query Selecting the element in the DOM all the way until we nab it.
        * Then we are faced with the next challenge. We don't know if the element is using an
        * index-based approach (0, 1, 2...) or an attribute approach(title="home", title="tournament",etc.)
        *
        * So we proceed to fetch its selection mechanism by grabbing the attrForSelected. If null, it means that
        * it is using the index-based approach. We continue and get the children position at the element.
        *
        * Note that selectedAttr variable will return me either the index or the selected attribute's value.
        * So it's going to be 0, 1, 2 if using the index based approach.
        *
        **/

        function getSelectedElement(event) {
            if (event.srcElement !== undefined)
                return event.srcElement.selectedItem;

            var element = event.target;

            //Get the current selected attribute:
            var selectedAttr = element.selected;
            //Gets the attribute that is being used for selection:
            var attrForSelected = element.attrForSelected;

            //This means that it is not using index based
            if (attrForSelected !== null) {
                return element.querySelector('[' + attrForSelected + '="' + selectedAttr + '"]');
            }

            //Continues using index based:
            var childelem = element.children[parseInt(selectedAttr)];
            return childelem;


        }



    </script>

Первое, что мы делаем, это определяем URL-адреса относительно документа, который у меня есть. Я делаю это, определяя json с ключом, имя которого является атрибутом title для iron-pages и значением с относительным URL-адресом этого документа (приложение pgarena).

Я имею в виду, что в случае, если я хочу загрузить pgarena-tournament-app, а моя pgarena-app (моя основная точка входа в приложение) находится в www/polymer/pgarena-app.html, а мое приложение pgarena-tournament-app находится в www/polymer/routes/tournament/pgarena-tournament-app.html, поскольку это относительно, мой JSON будет:

var PGArena = PGArena || {}; PGArena.LazyLoad = { "турнир" : "маршруты/турнир/pgarena-tournament-app.html", };

Примечание PGArena.LazyLoad может быть чем угодно, это глобальная переменная, которую я определил в пространстве имен PGArena.

Затем мы видим, что вызывается код LazyLoad:

function LazyLoad(Polymer, element, elementName, selectorTrigger) {
        if (Polymer.isInstance(element)) {
            // console.log(elementName + " is already registered ;)");
            return;
        } else {
            //console.log(elementName+" isn't registered. On its way for Lazy Loading!");
        }
        //console.log("Lazy Load Started");
        var hasProp = PGArena.LazyLoad.hasOwnProperty(elementName);
        if (!hasProp) {
            console.log("Property " + elementName + " not found for Lazy Loading");
            return;
        }

        var href = PGArena.LazyLoad[elementName];
        LoadImportAsync(href, elementName, selectorTrigger); 
    }

Что я делаю здесь, так это проверяю, есть ли ссылка на элемент, который я хочу отложить, в JSON, который я определил (PGarena.LazyLoad). Если его там нет, то я регистрирую это сообщение. Если он есть и не загружен, я загружаю его асинхронно, создавая импорт HTML и добавляя его в заголовок:

 /**
        * Loads the required import and appends it to the document. It really doesn't
        * matter where it is appended.
        *
        **/
        function LoadImportAsync(href, elementName) {

            var link = document.createElement('link');
            link.rel = 'import';
            link.href = getBaseUrl() + "/NodeJS/Polymer/app/elements/" + href;
            link.setAttribute('async', ''); // make it async!
            link.onload = function () { Spinner(elementName, false); }
            link.onerror = function (e) { console.log("There was an error loading " + elementName + ". Please Check the URL") };
            document.head.appendChild(link);
        }

Пожалуйста, обратите внимание (я не знаю, исправили ли они это). Существует полифилл для импорта HTML для Firefox, Edge и Safari (я полагаю). Полифилл использует XHR (AJAX) для загрузки импорта!!! Я упоминаю об этом, потому что в начале я пытался перехватить импорт HTML, и в Google Chrome это не сработало.

Позвольте мне знать, если вам нужно что-нибудь еще. Как видите, я пытался использовать счетчик, но у меня не получилось. Надеюсь это поможет!

person Jose A    schedule 13.06.2017

Я бы начал с того, что по умолчанию Vulcanize только объединяет файлы. Вы добавили переключатели для удаления комментариев, например?

Кроме того, вы хотели бы минимизировать связанные файлы HTML и JS. В большинстве примеров будет показана настройка Gulp, но вы также можете минимизировать вулканизированные файлы на втором этапе в сценарии npm.

Возможно, этот пост поможет: https://jongeho.wordpress.com/2015/06/24/endeavors-with-polymer-1-0-vulcanize-crisper-html-minifier/

Тем не менее, это правда, что приложение с большим количеством веб-компонентов, естественно, будет довольно большим. Это то, что я тоже заметил

person Tomasz Pluskiewicz    schedule 01.08.2016
comment
Огромное спасибо! *Эх... и я думаю, что дальше будет еще хуже, так как впереди еще много элементов. У меня проблемы с запуском html-minifier. Он говорит, что replace.value не является функцией. Любые идеи? - person Jose A; 01.08.2016
comment
Не было такой проблемы. Заслуживает еще один вопрос, может быть - person Tomasz Pluskiewicz; 01.08.2016
comment
Спасибо! Мне удалось выполнить минимизацию вручную через prettydiff.com, и он просто сократил 100 КБ (что значительно), но все же У меня есть довольно большой файл. Сейчас я смотрю на ленивую загрузку компонентов с помощью ручного JavaScript. Если это не сработает, я посмотрю, могут ли Webpack или Browserify помочь с задачей. - person Jose A; 01.08.2016
comment
Я просто кое-что проверял. Я думаю, что проблема все-таки не в Полимере. Проблема в HTTP. Теперь я понимаю, почему Polymer опережает свое время. Мне нужно связать все с HTTP/2 (к счастью, Cloudflare начал поддерживать его, и я могу преодолеть ограничение Azure). Таким образом, решение будет заключаться в минимизации скриптов и ленивой загрузке элементов с помощью функции importHref в Polymer. Давайте посмотрим, как я могу придумать глобальное решение. - person Jose A; 01.08.2016
comment
Я также проверял время до первого байта (TTFB), и это было ужасно. Я думаю, что сервер в этом случае был виновником. Мне удалось начать кэширование вывода, и скорость значительно увеличилась. - person Jose A; 01.08.2016
comment
@JoseA Я столкнулся с той же проблемой, размер моего файла после вулканизации составляет 1,2 МБ. Поиск способа ленивой загрузки полимерных элементов. Как вы это реализовали в итоге? - person mukund; 13.06.2017
comment
@mukund: я опубликую ответ через некоторое время. Дай мне пару минут. - person Jose A; 13.06.2017