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

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

Я только что вулканизировал свой файл elements.html, и я в ужасе. Зверь 947KB без изображений, только голый 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: Vulcanize и Polymer auto Lazy Loading, но я не знаю, что с этим делать.

Это импорт моего файла 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) содержат больше полимерных компонентов.

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

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

2 ответа

Решение

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

TL; DR: мы получаем URL-адреса.HTML и используем JavaScript для создания link атрибут (импорт HTML) для загрузки элемента. Мы проверяем с помощью полимера 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-турнир-приложение находится в www/polymer/routes/tournament/pgarena-tournament-app.html, поскольку это относительно, мой JSON будет:

var PGArena = PGArena || {}; PGArena.LazyLoad = {"турнир": "маршруты / турнир /pgarena-tour-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(я полагаю). Polyfill использует XHR (AJAX) для загрузки импорта!!! Я упоминаю об этом, потому что в начале я пытался перехватить импорт HTML, а в Google Chrome это не сработало.

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

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

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

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

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

Другие вопросы по тегам