- PVSM.RU - https://www.pvsm.ru -
Перевод статьи «HTML Imports #include for the web [1]», Eric Bidelman.
Ссылка на первую часть перевода. [2]
HTML-импорт упрощает загрузку и повторное использование кода. В частности, это хороший способ распространения веб-компонентов. Это касается как простых HTML <template> [3], так и полноценных кастомных элементов [4] с теневым DOM [1 [5], 2 [6], 3 [7]]. Когда эти технологии работают вместе, импорт становится инструментом для подключения [8] веб-компонентов.
HTML-шаблоны [9] это хороший пример того, где может пригодиться импорт. Тэг <template> позволяет выносить определенные секции разметки для дальнейшего их использования. Шаблоны полезны тем, что их содержимое и скрипты не будут отображаться и выполняться до их добавления на страницу при помощи JS кода.
import.html
<template>
<h1>Hello World!</h1>
<!-- Img is not requested until the <template> goes live. -->
<img src="">
<script>alert("Executed when the template is activated.");</script>
</template>
index.html
<head>
<link rel="import" href="import.html">
</head>
<body>
<div id="container"></div>
<script>
var link = document.querySelector('link[rel="import"]');
// Clone the <template> in the import.
var template = link.import.querySelector('template');
var clone = document.importNode(template.content, true);
document.querySelector('#container').appendChild(clone);
</script>
</body>
Пользовательские элементы [10] это еще одна технология веб-компонентов, которая отлично сочетается с HTML-импортом. Как мы знаем в импорте могут выполняться скрипты [11], таким образом мы можем объявлять и регистрировать пользовательские элементы, позволяя использовать их в разметке главного документа. Назовем это авторегистрацией.
elements.html
<script>
// Define and register <say-hi>.
var proto = Object.create(HTMLElement.prototype);
proto.createdCallback = function() {
this.innerHTML = 'Hello, <b>' +
(this.getAttribute('name') || '?') + '</b>';
};
document.registerElement('say-hi', {prototype: proto});
// Define and register <shadow-element> that uses Shadow DOM.
var proto2 = Object.create(HTMLElement.prototype);
proto2.createdCallback = function() {
var root = this.createShadowRoot();
root.innerHTML = "<style>::content > *{color: red}</style>" +
"I'm a " + this.localName +
" using Shadow DOM!<content></content>";
};
document.registerElement('shadow-element', {prototype: proto2});
</script>
Данный импорт определяет (и регистрирует) два элемента: <say-hi> и <shadow-element>. Мы можем использовать их прямо в разметке импортирующего документа без каких-либо дополнительных предусловий.
index.html
<head>
<link rel="import" href="elements.html">
</head>
<body>
<say-hi name="Eric"></say-hi>
<shadow-element>
<div>( I'm in the light dom )</div>
</shadow-element>
</body>
Как мне кажется, такой подход превращает HTML-импорт в идеальный способ предоставления веб-компонентов.
Эй, ты, я вижу тебе понравился импорт, так что я вложил импорт внутрь импорта1 [12].
Иногда нужно импортировать что-нибудь внутри импорта. Например, если вы хотите повторно использовать или расширить компонент, вы можете использовать вложенный импорт.
Снизу представлен реальный пример из Полимера [13]. Это компонент tab (<polymer-ui-tabs>) использующий компоненты layout и selector. Эти зависимости загружаются при помощи HTML-импорта.
polymer-ui-tabs.html
<link rel="import" href="polymer-selector.html">
<link rel="import" href="polymer-flex-layout.html">
<polymer-element name="polymer-ui-tabs" extends="polymer-selector" ...>
<template>
<link rel="stylesheet" href="polymer-ui-tabs.css">
<polymer-flex-layout></polymer-flex-layout>
<shadow></shadow>
</template>
</polymer-element>
Вот так разработчики могут использовать этот элемент:
<link rel="import" href="polymer-ui-tabs.html">
<polymer-ui-tabs></polymer-ui-tabs>
Когда выйдет новая версия селектора, например <polymer-selector2>, вы сможете подменить <polymer-selector> и сразу же начать пользоваться новой версией. Благодаря импорту и веб-компонентам, это обновление не заденет код использующий компонент polymer-ui-tabs.
Как вы знаете, если дважды подключить JQuery, это приведет к ошибкам. Не будет ли это проблемой, если несколько компонентов используют одну библиотеку? Нет, если мы будем использовать HTML-импорт! Использование импорта само по себе разрешает проблему управления зависимостями.
Оборачивая используемые библиотеки в импорт, вы автоматически избегаете их повторной загрузки. Импортируемый документ парсится только один раз. Скрипты в нем тоже выполняются только раз. Для примера вы можете импортировать jquery.html, который загружает сам JQuery.
jquery.html
<script src="http://cdn.com/jquery.js"></script>
Данный импорт может быть использован в других импортируемых компонентах:
import2.html
<link rel="import" href="jquery.html">
<div>Hello, I'm import 2</div>
ajax-element.html
<link rel="import" href="jquery.html">
<link rel="import" href="import2.html">
<script>
var proto = Object.create(HTMLElement.prototype);
proto.makeRequest = function(url, done) {
return $.ajax(url).done(function() {
done();
});
};
document.registerElement('ajax-element', {prototype: proto});
</script>
Если понадобится, главный документ также может использовать jquery.html:
<head>
<link rel="import" href="jquery.html">
<link rel="import" href="ajax-element.html">
</head>
<body>
...
<script>
$(document).ready(function() {
var el = document.createElement('ajax-element');
el.makeRequest('http://example.com');
});
</script>
</body><source lang="html">
Несмотря на то, что jquery.html подключается в нескольких разных документах, его загрузка и исполнение происходит только один раз. Это можно увидеть заглянув на панель network:

HTML-импорт — замечательная технология, но как и с любой новой веб-технологией, вы должны с умом подойти к её использованию. Лучшие практики веб-разработки никто не отменял. Вот несколько вещей, которые вы должны помнить, работая с импортом.
Очень важно сократить количество запросов при загрузке страницы. Поэтому, если вам нужно импортировать большое количество компонентов верхнего уровня, постарайтесь объединить их в единственный файл для импорта.
Vulcanize [14], инструмент от создателей Полимера [15], используемый для построения проектов из npm. Он производит рекурсивную сборку всех импортируемых документов в единственный главный файл, это, по сути, объединение всех веб-компонентов в один файл. Данный инструмент написали создатели Полимера [15].
HTML-импорт также хорошо справляется с логикой кеширования. Импорт http://cdn.com/bootstrap.html содержит вложенные ресурсы, но они так же кешируются.
Содержимое никак не вызывается, до тех пор пока оно не использовано в скриптах. Для примера у нас есть динамически созданная таблица стилей:
var link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'styles.css';
Браузер не запросит styles.css, до тех пор пока элемент link не будет добавлен в DOM:
document.head.appendChild(link); // browser requests styles.css
Другой пример, это динамически созданная разметка:
var h2 = document.createElement('h2');
h2.textContent = 'Booyah!';
Элемент h2 ничего не изменит, пока вы не добавите его в DOM.
Эта же идея остается истинной и для импортируемых документов. Пока вы не добавите их в DOM, они ничего не делают. Но на самом деле, скрипты являются исключением, они исполняются, сразу же при загрузке импорта. Смотрите скрипты в импорте [16].
Импорт не блокирует парсинг главного документа. Скрипты внутри импорта выполняются по очереди, но не блокируют импортирующую страницу. Это значит, что происходит отложенное выполнение скриптов. Преимущество подключения импорта в элементе <head> заключается в том, что это не заблокирует работу с содержимым документов. Имея это ввиду, важно все же помнить, что скрипты в главном документе все равно блокируют загрузку страницы <script>:
<head>
<link rel="import" href="/path/to/import_that_takes_5secs.html">
<script>console.log('I block page rendering');</script>
</head>
Есть несколько способов для оптимизации асинхронного поведения, использование которых зависит от структуры вашего приложения. Следующие техники помогут избежать блокировки отображения главной страницы.
<head> или по ходу тела документаЯ рекомендую использовать скрипты как можно ниже, чтобы предотвратить немедленную загрузку вашего импорта. Но вы ведь и так следуете этой практике, НЕ ТАК ЛИ!? ;)
вот пример:
<head>
<link rel="import" href="/path/to/import.html">
<link rel="import" href="/path/to/import2.html">
<!-- avoid including script -->
</head>
<body>
<!-- avoid including script -->
<div id="container"></div>
<!-- avoid including script -->
...
<script>
// Other scripts n' stuff.
// Bring in the import content.
var link = document.querySelector('link[rel="import"]');
var post = link.import.querySelector('#blog-post');
var container = document.querySelector('#container');
container.appendChild(post.cloneNode(true));
</script>
</body>
Все скрипты расположены снизу.
<body>Другой вариант, это позволять импорту самому себя рендерить [16]. Если автор импорта предоставляет соглашения о размещении импорта в главном документе, то он может сам себя размещать в оговоренных местах:
import.html:
<div id="blog-post">...</div>
<script>
var me = document.currentScript.ownerDocument;
var post = me.querySelector('#blog-post');
var container = document.querySelector('#container');
container.appendChild(post.cloneNode(true));
</script>
index.html
<head>
<link rel="import" href="/path/to/import.html">
</head>
<body>
<!-- no need for script. the import takes care of things -->
</body>
<head> или по ходу тела документа
Если у вас есть большой импорт, на загрузку которого нужно много времени, следующий за ним скрипт остановит рендеринг страницы. Google Analytics например, рекомендует добавлять отслеживающий код в элементе <head>. Если вы не можете избежать размешения скриптов в элементе <head>, динамическое добавление тега импорта предотвратит блокировку:
<head>
<script>
function addImportLink(url) {
var link = document.createElement('link');
link.rel = 'import';
link.href = url;
link.onload = function(e) {
var post = this.import.querySelector('#blog-post');
var container = document.querySelector('#container');
container.appendChild(post.cloneNode(true));
};
document.head.appendChild(link);
}
addImportLink('/path/to/import.html'); // Import is added early :)
</script>
<script>
// other scripts
</script>
</head>
<body>
<div id="container"></div>
...
</body>
Как вариант, вы можете добавить весь импорт в конце <body>.
<head>
<script>
// other scripts
</script>
</head>
<body>
<div id="container"></div>
...
<script>
function addImportLink(url) { ... }
addImportLink('/path/to/import.html'); // Import is added very late :(
</script>
</body>
Примечание: Самый последний вариант — наименее предпочтителен. Парсер не начинает работать с содержимым страницы до самого окончания страницы.
text/html<style> также добавляются автоматически. Это все составляет огромную разницу между HTML-импортом и <iframe>, который говорит браузеру «загрузи и отобрази контент прямо здесь».
HTML-импорт позволяет предоставлять набор HTML/CSS/JS кода, как единый ресурс. Это полезно, как само по себе, так и в контексте веб-компонентов. Разработчики могут создавать повторно используемые компоненты, для их потребления другими разработчиками при помощи <link rel="import">.
HTML-импорт это простая идея, но она предоставляет нам несколько интересных полезностей.
<link rel="import" href="chunks.html">:
<script>/* script chunk A goes here */</script>
<script>/* script chunk B goes here */</script>
<script>/* script chunk C goes here */</script>
...
1. В оригинале предложение звучит так «Yo dawg. I hear you like imports, so I included an import in your import.», это отсылка к известному выражению [20] репера Xzibit
Автор: jojo97
Источник [21]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/web-razrabotka/67136
Ссылки в тексте:
[1] HTML Imports #include for the web: http://html5rocks.com/en/tutorials/webcomponents/imports/#deliver-webcomponents
[2] Ссылка на первую часть перевода.: http://habrahabr.ru/post/230877/
[3] HTML <template>: http://www.html5rocks.com/webcomponents/template/
[4] кастомных элементов: http://www.html5rocks.com/tutorials/webcomponents/customelements/#registering
[5] 1: http://www.html5rocks.com/tutorials/webcomponents/shadowdom/
[6] 2: http://www.html5rocks.com/tutorials/webcomponents/shadowdom-201/
[7] 3: http://www.html5rocks.com/tutorials/webcomponents/shadowdom-301/
[8] подключения: http://en.cppreference.com/w/cpp/preprocessor/include
[9] HTML-шаблоны: http://www.html5rocks.com/tutorials/webcomponents/template/
[10] Пользовательские элементы: http://www.html5rocks.com/en/tutorials/webcomponents/imports/tutorials/webcomponents/customelements/
[11] в импорте могут выполняться скрипты: http://www.html5rocks.com/en/tutorials/webcomponents/imports/#includejs
[12] 1: #first-annotation
[13] Полимера: http://polymer-project.org/
[14] Vulcanize: https://github.com/Polymer/vulcanize
[15] Полимера: http://www.polymer-project.org/
[16] скрипты в импорте: http://habrahabr.ru/post/230877/#scripting-in-import
[17] взаимосвязанного HTML/CSS/JS кода в одной связке: http://habrahabr.ru/post/230877/#bundling
[18] пользовательских элементов: http://www.html5rocks.com/tutorials/webcomponents/customelements/
[19] Управление зависимостями: #dependecies
[20] известному выражению: http://lurkmore.to/Yo_dawg
[21] Источник: http://habrahabr.ru/post/231845/
Нажмите здесь для печати.