Render js что это
Перейти к содержимому

Render js что это

  • автор:

Функция render() — что такое аргумент h

Если вы когда-либо работали с фреймворком Vue.js, то вам приходилось сталкиваться в файле main.js с подобным способом рендеринга приложения:

new Vue( render: h => h(App)
>).$mount('#app')

В последней версии консольной утилиты Vue CLI 3 такой способ является способом по умолчанию.

Другим возможным случаем, когда вам приходилось сталкиваться с функцией рендеринга render(), является использование синтаксического расширения JSX в локальном компоненте Vue-приложения:

Vue.component('jsx-example', render (h) return 
id="foo">bar

>
>)

В обоих случаях у вас мог возникнуть вопрос — что такое h и для чего оно предназначено?

Если кратко — h является сокращением от hyperscript. Это название является (в свою очередь) сокращением для Hypertext Markup Language.

Такое имя используется потому, что при работе с Vue-приложением мы фактически имеем дело со скриптом, результатом работы которого является виртуальное DOM-дерево.

Использование подобного сокращения также встречается в официальной документации других JavaScript-фреймворков. Например, в документации Cycle.js есть определение h — The hyperscript function h().

Сокращение createElement до h — распространённое соглашение в экосистеме Vue и обязательное для использования JSX. В случае отсутствия h в области видимости, приложение выбросит ошибку.

В одном из ишью создатель фреймворка Vue.js Эван Ю (Evan You) так объясняет, что такое h:

Термин hyperscript можно объяснить так — это “скрипт, который генерирует HTML-структуру”

Сокращение h используется при написании кода — так быстрее и легче. Более подробно это сокращение также объясняется Эваном в Advanced Vue Workshop — курсов для фронтенд-разработчиков Frontend Masters.

Про сокращение h можно думать как о более краткой форме функции createElement. Например, полная форма функции createElement представлена ниже:

render: function (createElement) return createElement(App);
>

Если заменить createElement на h, то получим более сокращенный вариант этой функции:

render: function (h) return h(App);
>

… который можно сократить еще больше при помощи возможностей ES6:

render: h => h (App)

Vue-версия функции render() принимает три аргумента:

render(h) return h('div', <>, [. ])
>
  • первый аргумент — это тип элемента (в примере представлен div);
  • второй аргумент — это объект данных (здесь можно передать свойства, атрибуты, классы или стили);
  • третий аргумент — это массив дочерних Node-узлов; здесь можно размещать вложенные вызовы функции createElement и вернуть обратно дерево виртуальных DOM-узлов;

Термин hyperscript может вводить в заблуждение в некоторых случаях, так как это имя JavaScript-библиотеки hyperscript, у которой есть одноименная экосистема. В нашем конкретном случае речь не идет об этих сущностях.

Надеюсь, статья оказалась полезной для тех читателей, которые задавались подобным вопросом.

Как работает JS: движки рендеринга веб-страниц и советы по оптимизации их производительности

Сегодня, в переводе одиннадцатой части серии материалов, посвящённых JavaScript, мы поговорим о подсистемах браузера, ответственных за рендеринг веб-страниц. Они играют ключевую роль в деле преобразования описаний документов, выполненных с помощью HTML и CSS, в то, что мы видим на экране.

image

Автор материала говорит, что в компании SessionStack приходится уделять рендерингу огромное внимание. В этой статье он поделится советами, касающимися оптимизации веб-страниц с учётом особенностей их визуализации.

[Советуем почитать] Другие 19 частей цикла

Обзор

Создавая веб-приложения, мы не пишем изолированный JS-код, который занимается исключительно какими-то собственными «внутренними» делами. Этот код выполняется в окружении, предоставляемом ему браузером, взаимодействует с ним. Понимание устройства этого окружения, того, как оно работает, из каких частей состоит, позволяет разработчику создавать более качественные программы, даёт ему возможность предусмотреть возникновение возможных проблем с приложением, которое вышло в свет.

На рисунке ниже показаны основные компоненты браузера. Давайте поговорим о том, какую роль они играют в процессе обработки веб-страниц.

Основные компоненты браузера

  • Пользовательский интерфейс (User Interface). Этот компонент браузера включает в себя адресную строку, кнопки «Вперёд» и «Назад», команды для работы с закладками, и так далее. В целом, это всё то, что выводит на экран браузер — за исключением той области его окна, где находится отображаемая им веб-страница.
  • Движок браузера (Browser Engine). Он занимается поддержкой взаимодействия между пользовательским интерфейсом и движком рендеринга.
  • Движок рендеринга (Rendering Engine). Эта подсистема отвечает за показ веб-страницы. Движок рендеринга обрабатывает HTML и CSS и выводит то, что у него получилось, на экран.
  • Сетевая подсистема (Networking). Эта подсистема ответственна за сетевое взаимодействие браузера с внешним миром, в частности, например, её средствами выполняются XHR-запросы. Она поддерживает платформенно-независимый интерфейс, за которым скрываются конкретные реализации различных сетевых механизмов, специфичные для различных платформ. Здесь можно почитать подробности об этой подсистеме.
  • Подсистема поддержки пользовательского интерфейса (UI Backend). Эта подсистема отвечает за вывод базовых компонентов интерфейса, таких, как окна и элементы управления, вроде чекбоксов. Здесь браузеру предоставляется универсальный интерфейс, не зависящий от платформы, на которой он работает, а в основе этой подсистемы лежат возможности формирования элементов пользовательского интерфейса, предоставляемые конкретной операционной системой.
  • JavaScript-движок (JavaScript Engine). Мы разбирали JS-движок в одном из предыдущих материалов этой серии. Именно здесь осуществляется выполнение JS-кода.
  • Подсистема постоянного хранения данных (Data Persistence). Если приложению нужны возможности локального хранения данных, оно может пользоваться различными механизмами, предоставляемыми этой подсистемой. Среди них, например, такие API, как localStorage, IndexedDB, WebSQL и FileSystem.

О различных движках рендеринга

Главная задача движка рендеринга заключается в том, чтобы вывести запрошенную страницу в окне браузера. Движок может выводить HTML-документы, XML-документы, изображения. При использовании дополнительных плагинов движок может визуализировать и материалы других типов, например — PDF-документы.

Мы знаем, что существуют различные JS-движки, которые используют различные браузеры. То же самое справедливо и для движков рендеринга. Вот несколько популярных движков:

  • Gecko — используется в браузере Firefox.
  • WebKit — применяется в браузере Safari.
  • Blink — интегрирован в браузеры Chrome и Opera (с 15-й версии).

Процесс рендеринга веб-страницы

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

Процесс рендеринга веб-страницы

Вот основные этапы этого процесса:

  • Обработка HTML для создания дерева DOM.
  • Создание дерева рендеринга.
  • Расчёт параметров расположения элементов дерева рендеринга на экране, формирование макета страницы.
  • Визуализация (отрисовка) дерева рендеринга.

Создание дерева DOM

Первый этап работы движка рендеринга заключается в разборе HTML-документа и преобразовании того, что у него получилось, в узлы DOM, размещённые в дереве DOM. При этом веб-страница, которая представлена в виде HTML-кода, преобразуется в структуру, подобную той, которая показана на рисунке ниже.

Каждый элемент этого дерева, содержащий вложенные элементы, является для них родительским. Это справедливо для всех уровней вложенности.

Создание дерева CSSOM

CSSOM (CSS Object Model) — это объектная модель CSS. Когда браузер занимается созданием дерева DOM страницы, он находит в разделе head тег link , который ссылается на внешний CSS-файл, скажем, имеющий имя theme.css . Ожидая, что этот ресурс может понадобиться ему для рендеринга страницы, браузер выполняет запрос на загрузку данного файла. Этот файл содержит в себе обычный текст, представляющий собой описание стилей, применяемых к элементам страницы.

Как и в случае с HTML, движку нужно конвертировать CSS в нечто, с чем может работать браузер — в CSSOM. В результате получается дерево CSSOM, представленное на следующем рисунке.

Дерево CSSOM

Знаете, почему CSSOM имеет древовидную структуру? Когда выполняется формирование итогового набора стилей для элемента страницы, браузер начинает с наиболее общих правил, применимых к этому элементу, представленному узлом DOM (например, если узел является потомком элемента body , к нему применяются все стили, заданные для body ), а затем рекурсивно уточняет вычисленные стили, применяя более специфические правила.

Разберём пример, который представлен на предыдущем рисунке. Любой текст, содержащийся внутри тега span , который помещён в элемент body , выводится красным цветом и имеет размер шрифта, равный 16px . Эти стили унаследованы от элемента body . Если элемент span является потомком элемента p , значит его содержимое не выводится в соответствии с применённым к нему более специфичным стилем.

Кроме того, обратите внимание на то, что вышеприведённое дерево не является полным CSSOM-деревом. Тут показаны лишь стили, которые мы, в нашем CSS-файле, решили переопределить. У каждого браузера имеется стандартный набор стилей, применяемый по умолчанию, известный ещё как «стили пользовательского агента» (user agent styles). Именно результаты применения этих стилей можно видеть на странице, не имеющей связанных с ней CSS-правил. Наши же стили просто переопределяют некоторые из стандартных стилей браузера.

Создание дерева рендеринга

Инструкции о внешнем виде элементов, представленные в HTML, скомбинированные с информацией об их стилизации из дерева CSSOM, используются для формирования дерева рендеринга.

Что это такое? Это — дерево визуальных элементов, созданных в том порядке, в котором они будут выводиться на экран. Это — визуальное представление HTML-кода страницы, отражающее влияние соответствующих этой странице CSS-правил. Цель этого дерева заключается в том, чтобы обеспечить вывод элементов правильном порядке.

Узел дерева рендеринга известен в движке WebKit как «renderer» или «render object» (мы будем называть их «объектами рендеринга»).

Вот как будет выглядеть дерево рендеринга для деревьев DOM и CSSOM, показанных выше.

Дерево рендеринга

Вот общее описание действий браузера, выполняемых им при создании дерева рендеринга.

  • Начиная с корня дерева DOM, браузер обходит каждый видимый узел. Некоторые узлы невидимы (например — теги, содержащие ссылки на скрипты, мета-теги, и так далее), их браузер пропускает, так как они не влияют на внешний вид страницы. Некоторые узлы скрыты средствами CSS, браузер так же не включает их в дерево рендеринга. Например, узел span из нашего примера не выводится в дереве рендеринга, так как у нас имеется явным образом заданное правило, устанавливающего для него свойство display: none .
  • Для каждого видимого узла браузер находит подходящие CSSOM-правила и применяет их.
  • В результате формируется структура, содержащая видимые узлы и вычисленные для них стили.

Формирование макета страницы

После того, как объект рендеринга создан и добавлен в дерево, ему пока ещё не назначены позиция и размер. Вычисление этих значений и называется формированием макета страницы.

HTML использует потоковую модель компоновки. Это означает, что чаще всего система может вычислить геометрические параметры элементов за один проход. Тут используется координатная система, основанная на корневом объекте рендеринга, в ней применяются координаты left и top .

Формирование макета — это рекурсивный процесс. Он начинается в корневом объекте, который соответствует элементу документа . Процесс выполняется рекурсивно по всей иерархической структуре объекта рендеринга, производится вычисление размеров и положения для каждого элемента, который в этом нуждается.

Позиция корневого объекта рендеринга — 0,0 . Его размеры соответствуют размерам видимой части окна браузера (это называют «областью просмотра», viewport).

Процесс формирования макета означает задание каждому узлу точной позиции, в которой он должен появиться на странице.

Визуализация дерева рендеринга

На данном этапе осуществляется обход дерева рендеринга и вызов методов paint() объектов рендеринга, которые и выполняют вывод графического представления объектов на экран.

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

  • Глобальная отрисовка означает повторный вывод всего дерева рендеринга.
  • Инкрементная отрисовка выполняется в ситуации, когда меняются лишь некоторые из объектов рендеринга, причём так, что это не влияет на всё дерево. Подсистема рендеринга делает недействительными прямоугольные области на экране. Это приводит к тому, что операционная система воспринимает их как участки, содержимое которых нужно обновить и сгенерировать для них событие paint . Операционная система выполняет перерисовку областей интеллектуально, объединяя несколько областей в одну.

Порядок обработки JS-скриптов и CSS-файлов

Разбор и выполнение скрипта осуществляется сразу же после того, как система обработки кода страницы достигнет тега . Обработка документа приостанавливается до тех пор, пока скрипт не будет выполнен. Это означает, что данный процесс выполняется синхронно.

Если скрипт получают из внешнего источника, то сначала он должен быть загружен через сеть (тоже синхронно). Обработка страницы приостанавливается до тех пор, пока загрузка скрипта не будет завершена.

HTML5 позволяет указывать на возможность асинхронной загрузки и обработки скрипта с использованием отдельного потока.

Оптимизация производительности рендеринга

Если вы хотите оптимизировать своё приложение с учётом особенностей рендеринга страниц, существует пять основных областей, которые вы можете контролировать, и на которые нужно обратить внимание.

  • JavaScript. В предыдущих материалах этой серии мы рассказывали о том, как писать оптимизированный JS-код, не блокирующий пользовательский интерфейс, эффективно использующий память и реализующий другие полезные техники. Когда речь идёт о рендеринге, нам нужно учитывать то, как JS-код будет взаимодействовать с элементами DOM на странице. JavaScript может вносить множество изменений в пользовательский интерфейс, особенно если речь идёт об одностраничных приложениях.
  • Вычисление стилей. Это — процесс определения того, какое CSS-правило применяется к конкретному элементу с учётом соответствующих этому элементу селекторов. После определения правил осуществляется их применение и вычисление итогового стиля для каждого элемента.
  • Формирование макета страницы. После того, как браузер узнает о том, какие стили применяются к элементу, он может приступить к вычислению того, как много места на экране займёт этот элемент, и к нахождению его позиции. Модель макета веб-страницы указывает на то, что одни элементы могут влиять на другие элементы. Например, ширина элемента может влиять на ширину дочерних элементов, и так далее. Всё это означает, что процесс формирования макета — это задача, требующая интенсивных вычислений. Кроме того, вывод элементов выполняется на множество слоёв.
  • Отрисовка. Именно здесь выполняется преобразование всего, что было вычислено ранее, в пиксели, выводимые на экран. Этот процесс включает в себя вывод текста, цветов, изображений, границ, теней, и так далее. Речь идёт о каждой видимой части каждого элемента.
  • Компоновка. Так как части страницы вполне могут быть выведены на различных слоях, их требуется совместить в едином окне в нужном порядке, что приведёт к правильному выводу страницы. Это очень важно, особенно — для перекрывающихся элементов.

Оптимизация JS-кода

JavaScript-код часто приводит к изменению того, что можно наблюдать в браузере. Особенно это актуально для одностраничных приложений. Вот несколько советов, касающихся оптимизации JS для улучшения процесса рендеринга страниц.

  • Избегайте использования функций setTimeout() и setInterval() для обновления внешнего вида элементов страниц. Эти функции вызывают коллбэк в некоторый момент формирования кадра, возможно, в самом конце. Нам же нужно вызвать команду, приводящую к визуальным изменениям, в начале кадра, и не пропустить его.
  • Переносите длительные вычисления в веб-воркеры.
  • Используйте для выполнения изменений в DOM микро-задачи, разбитые на несколько кадров. Этим следует пользоваться тогда, когда задача нуждается в доступе к DOM, а доступ к DOM, из веб-воркера, например, получить нельзя. Это означает, что большую задачу нужно разбить на более мелкие и выполнять их внутри requestAnimationFrame , setTimeout , или setInterval , в зависимости от особенностей задачи.

Оптимизация CSS

Модификация DOM путём добавления и удаления элементов, изменения атрибутов и других подобных действий приведёт к тому, что браузеру придётся пересчитать стили элементов, и, во многих случаях, макет всей страницы, или, по крайней мере, некоторой её части. Для оптимизации процесса рендеринга страницы учитывайте следующее.

  • Уменьшите сложность селекторов. Использование сложных селекторов может привести к тому, что работа с ними займёт более 50% времени, необходимого для вычисления стилей элемента, остальное время уйдёт на конструирование самого стиля.
  • Уменьшите число элементов, для которых нужно выполнять вычисление стилей. То есть, лучше, если изменение стиля будет направлено на несколько элементов, а не на всю страницу.

Оптимизация макета

Пересчёт макета страницы может требовать серьёзных системных ресурсов. Для оптимизации этого процесса примите во внимание следующее.

  • Уменьшите число ситуаций, приводящих к пересчёту макета. Когда вы меняете стили, браузер выясняет, требуется ли пересчёт макета для отражения этих изменений. Изменения свойств, таких, как ширина, высота, или позиция элемента (в целом, речь идёт о геометрических характеристиках элементов), требуют изменения макета. Поэтому, без крайней необходимости, не меняйте подобные свойства.
  • Всегда, когда это возможно, используйте модель flexbox вместо более старых моделей создания макетов. Эта модель работает быстрее, чем другие, что может дать значительный прирост производительности.
  • Избегайте модели работы с документом, предусматривающей периодическое изменение параметров элементов и их последующее считывание. В JavaScript доступны параметры элементов DOM (вроде offsetHeight или offsetWidth ) из предыдущего кадра. Считывание этих параметров проблем не вызывает. Однако, если вы, до чтения подобных параметров, меняете стиль элемента (например, динамически добавляя к нему какой-то CSS-класс), браузеру потребуется потратить немало ресурсов для того, чтобы применить изменения стиля, создать макет и возвратить в программу нужные данные. Это может замедлить программу, подобного стоит избегать всегда, когда это возможно.

Оптимизация отрисовки

Часто эта задача отнимает больше всего времени, поэтому важно избегать ситуаций, приводящих к перерисовке страницы. Вот что здесь можно сделать.

  • Изменение любого свойства, за исключений трансформаций и изменений прозрачности, приводит к перерисовке. Используйте эти возможности умеренно.
  • Если ваши действия вызвали пересчёт макета, это приводит и к вызову перерисовки страницы, так как изменения геометрических параметров элемента ведут и к его визуальным изменениям.
  • Уменьшайте области страниц, которые необходимо перерисовывать, грамотно управляя расположением слоёв и анимацией.

Итоги

В этом материале мы поговорили о подсистемах рендеринга современных браузеров. Правильный подход к визуализации страниц ведёт к повышению производительности веб-приложений и к улучшению впечатлений пользователей от работы с ними.

Предыдущие части цикла статей:

Уважаемые читатели! Какие приёмы вы применяете для оптимизации рендеринга страниц ваших веб-проектов?

Render-функции и JSX

В большинстве случаев для формирования HTML с помощью Vue рекомендуется использовать шаблоны. Впрочем, иногда возникает необходимость в использовании всех алгоритмических возможностей JavaScript. В таких случаях можно применить render -функции — более низкоуровневую альтернативу шаблонам.

Давайте разберём простой пример, в котором использование render -функции будет целесообразным. Предположим, вы хотите сгенерировать заголовки с «якорями»:

h1> 
a name="hello-world" href="#hello-world">
Привет, мир!
a>
h1>

Для генерации представленного выше HTML вы решаете использовать такой интерфейс компонента:

anchored-heading :level="1">Привет, мир! anchored-heading> 

При использовании шаблонов для реализации интерфейса, который генерирует только заголовок, основанный на свойстве level , вы быстро столкнётесь со следующей ситуацией:

script type="text/x-template" id="anchored-heading-template"> 
h1 v-if="level === 1">
slot> slot>
h1>
h2 v-if="level === 2">
slot> slot>
h2>
h3 v-if="level === 3">
slot> slot>
h3>
h4 v-if="level === 4">
slot> slot>
h4>
h5 v-if="level === 5">
slot> slot>
h5>
h6 v-if="level === 6">
slot> slot>
h6>
script>
Vue.component('anchored-heading', { 
template: '#anchored-heading-template',
props: {
level: {
type: Number,
required: true
}
}
})

Выглядит не очень. Шаблон получился не только очень большим — приходится ещё и повторять для каждого возможного уровня заголовка.

Шаблоны хорошо подходят для большинства компонентов, но рассматриваемый сейчас — явно не один из них. Давайте попробуем переписать компонент, используя render -функцию:

Vue.component('anchored-heading', { 
render: function (createElement) {
return createElement(
'h' + this.level, // имя тега
this.$slots.default // массив дочерних элементов
)
},
props: {
level: {
type: Number,
required: true
}
}
})

Так-то лучше, наверное? Код короче, но требует более подробного знакомства со свойствами экземпляра Vue. В данном случае необходимо знать, что когда дочерние элементы передаются без указания директивы v-slot , как например Привет, мир! внутри anchored-heading , они сохраняются в экземпляре компонента как $slots.default . Если вы этого ещё не сделали, советуем вам пробежать глазами API-справочник свойств экземпляра, перед тем как углубляться в рассмотрение render-функций.

Узлы, деревья, и виртуальный DOM

Прежде чем погрузиться в render -функции, необходимо узнать, как работают браузеры. Возьмём, например, этот HTML-код:

div> 
h1>My title h1>
Some text content
TODO: Add tagline -->
div>

Когда браузер обрабатывает этот код, он создаёт дерево «узлов DOM», чтобы облегчить ему отслеживание всего, как, например, вы могли бы построить генеалогическое дерево для отслеживания вашей увеличивающейся семьи.

Дерево узлов DOM для HTML из примера выше выглядит следующим образом:

Визуализация дерева DOM

Каждый элемент является узлом. Каждый фрагмент текста является узлом. Даже комментарии это узлы! Узел — это всего лишь часть страницы. И так же, как и в генеалогическом дереве, каждый узел может иметь своих потомков (т.е. каждая часть может содержать в себе другие части).

Обновление всех этих узлов может быть затруднительно, но, к счастью, вам не придётся делать это вручную. Вы просто вставляете в шаблон определённый HTML-код, который вы хотите отобразить на странице:

h1>{{ blogTitle }} h1> 

Или то же, но с использованием render-функции:

render: function (createElement) { 
return createElement('h1', this.blogTitle)
}

В обоих случаях Vue автоматически будет обновлять страницу при изменениях blogTitle .

Виртуальный DOM

Vue реализует это созданием виртуального DOM, чтобы отслеживать изменения, которые ему требуется внести в реальный DOM. Рассмотрим подробнее следующую строку:

return createElement('h1', this.blogTitle)

Что в действительности возвращает createElement ? Это не в точности реальный DOM-элемент. Можно было бы назвать createNodeDescription , если быть точным, так как результат содержит информацию, описывающую Vue, какой именно узел должен быть отображён на странице, включая описания любых дочерних узлов. Мы называем это описание узла «виртуальным узлом», обычно сокращая до аббревиатуры VNode. «Виртуальный DOM» — это то, что мы называем всем деревом VNodes, созданным из дерева компонентов Vue.

Аргументы createElement

Следующий момент, с которым необходимо познакомиться, — это синтаксис использования возможностей шаблонизации функцией createElement . Вот аргументы, которые принимает createElement :

// @returns {VNode}
createElement(
// String
// Название тега HTML, опции компонента или асинхронная функция,
// возвращающая один из двух них. Обязательный параметр.
'div',

// {Object}
// Объект данных, содержащий атрибуты,
// который вы бы указали в шаблоне. Опциональный параметр.
{
// (см. детали в секции ниже)
},

// String
// Дочерние виртуальные узлы (VNode), создаваемые с помощью `createElement()`
// или просто строки для получения 'текстовых VNode'. Опциональный параметр.
[
'Какой-то текст прежде всех остальных элементов.',
createElement('h1', 'Заголовок'),
createElement(MyComponent, {
props: {
someProp: 'foobar'
}
})
]
)

Подробно об объекте данных

Обратите внимание: особым образом рассматриваемые в шаблонах атрибуты v-bind:class и v-bind:style , и в объектах данных виртуальных узлов имеют собственные поля на верхнем уровне объектов данных. Этот объект также позволяет вам связывать обычные атрибуты HTML, а также свойства DOM, такие как innerHTML (это заменит директиву v-html ):

{ 
// То же API, что и у `v-bind:class`, принимающий
// строку, объект, массив строк или массив объектов
class: {
foo: true,
bar: false
},
// То же API, что и у `v-bind:style`, принимающий
// строку, объект, массив строк или массив объектов
style: {
color: 'red',
fontSize: '14px'
},
// Обычные атрибуты HTML
attrs: {
id: 'foo'
},
// Входные параметры компонентов
props: {
myProp: 'bar'
},
// Свойства DOM
domProps: {
innerHTML: 'baz'
},
// Обработчики событий располагаются под ключом `on`,
// однако модификаторы, вроде как `v-on:keyup.enter`, не
// поддерживаются. Проверять keyCode придётся вручную.
on: {
click: this.clickHandler
},
// Только для компонентов. Позволяет слушать нативные события,
// а не генерируемые в компоненте через `vm.$emit`.
nativeOn: {
click: this.nativeClickHandler
},
// Пользовательские директивы. Обратите внимание, что `oldValue`
// не может быть указано, так как Vue сам его отслеживает
directives: [
{
name: 'my-custom-directive',
value: '2',
expression: '1 + 1',
arg: 'foo',
modifiers: {
bar: true
}
}
],
// Слоты с ограниченной областью видимости в формате
// name: props => VNode
scopedSlots: {
default: props => createElement('span', props.text)
},
// Имя слота, если этот компонент
// является потомком другого компонента
slot: 'name-of-slot',
// Прочие специальные свойства верхнего уровня
key: 'myKey',
ref: 'myRef',
// Если указываете одно имя в ref к нескольким элементам
// в render-функции — это сделает `$refs.myRef` массивом
refInFor: true
}

Полный пример

Узнав всё это, мы теперь можем завершить начатый ранее компонент:

var getChildrenTextContent = function (children) { 
return children.map(function (node) {
return node.children
? getChildrenTextContent(node.children)
: node.text
}).join('')
}

Vue.component('anchored-heading',
render: function (createElement) {
// создать id в kebab-case
var headingId = getChildrenTextContent(this.$slots.default)
.toLowerCase()
.replace(/\W+/g, '-')
.replace(/(^-
}, this.$slots.default)
]
)
},
props: {
level: {
type: Number,
required: true
}
}
})

Ограничения

Виртуальные узлы должны быть уникальными

Все виртуальные узлы в компоненте должны быть уникальными. Это значит, что render -функция ниже некорректна:

render: function (createElement) { 
var myParagraphVNode = createElement('p', 'hi')
return createElement('div', [
// Упс — дублирующиеся виртуальные узлы!
myParagraphVNode, myParagraphVNode
])
}

Если вы действительно хотите многократно использовать один и тот же элемент/компонент, примените функцию-фабрику. Например, следующая render -функция полностью корректный способ для отображения 20 одинаковых абзацев:

render: function (createElement) { 
return createElement('div',
Array.apply(null, { length: 20 }).map(function ( ) {
return createElement('p', 'Привет')
})
)
}

Реализация возможностей шаблона с помощью JavaScript

v-if и v-for

Функциональность, легко реализуемая в JavaScript, не требует от Vue какой-либо проприетарной альтернативы. Например, используемые в шаблонах v-if и v-for :

ul v-if="items.length"> 
li v-for="item in items">{{ item.name }} li>
ul>
p v-else>Ничего не найдено. p>

При использовании render -функции это можно легко переписать с помощью if / else и map :

props: ['items'],
render: function (createElement) {
if (this.items.length) {
return createElement('ul', this.items.map(function (item) {
return createElement('li', item.name)
}))
} else {
return createElement('p', 'Ничего не найдено.')
}
}

v-model

В render -функции нет прямого аналога v-model — вы должны реализовать эту логику самостоятельно:

props: ['value'],
render: function (createElement) {
var self = this
return createElement('input', {
domProps: {
value: self.value
},
on: {
input: function (event) {
self.$emit('input', event.target.value)
}
}
})
}

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

События и модификаторы клавиш

Для модификаторов событий .passive , .capture и .once , Vue предоставляет префиксы, которые могут быть использованы вместе с on :

Модификаторы Префикс
.passive &
.capture !
.once ~
.capture.once или
.once.capture
~!
on: { 
'!click': this.doThisInCapturingMode,
'~keyup': this.doThisOnce,
'~!mouseover': this.doThisOnceInCapturingMode
}

Для всех остальных событий и модификаторов клавиш нет необходимости в префиксе, потому что вы можете просто использовать методы события в обработчике:

Модификаторы Эквивалент в обработчике
.stop event.stopPropagation()
.prevent event.preventDefault()
.self if (event.target !== event.currentTarget) return
Клавиши:
.enter , .13
if (event.keyCode !== 13) return (измените 13 на любой другой код клавиши для модификаторов других клавиш)
Модификаторы клавиш:
.ctrl , .alt , .shift , .meta
if (!event.ctrlKey) return (измените ctrlKey на altKey , shiftKey или metaKey соответственно)

Пример использования всех этих модификаторов вместе:

on: 
}

Слоты

Вы можете получить доступ к статическому содержимому слотов в виде массивов VNode, используя this.$slots :

render: function (createElement) { 
// ` `
return createElement('div', this.$slots.default)
}

И получить доступ к слотам со своей областью видимости как к функциям, возвращающим VNode, используя this.$scopedSlots :

props: ['message'],
render: function (createElement) {
// ` `
return createElement('div', [
this.$scopedSlots.default({
text: this.message
})
])
}

Чтобы передать слоты со своей областью видимости в дочерний компонент при помощи render -функции, добавьте свойство scopedSlots в данные VNode:

render: function (createElement) { 
// `
{{ props.text }}
`

return createElement('div', [
createElement('child',
// передаём `scopedSlots` в объект data
// в виде { name: props => VNode
scopedSlots: {
default: function (props) {
return createElement('span', props.text)
}
}
})
])
}

JSX

Если приходится писать много render -функций, то такой код может утомлять:

createElement( 
'anchored-heading', {
props: {
level: 1
}
}, [
createElement('span', 'Привет'),
', мир!'
]
)

Особенно в сравнении с кодом аналогичного шаблона:

anchored-heading :level="1"> 
span>Привет span>, мир!
anchored-heading>

Поэтому есть плагин для Babel, позволяющий использовать JSX во Vue, и применять синтаксис, похожий на шаблоны:

import AnchoredHeading from './AnchoredHeading.vue'

new Vue({
el: '#demo',
render: function (h) {
return (
AnchoredHeading level={1}>
span>Привет span>, мир!
AnchoredHeading>
)
}
})

Сокращение createElement до h — распространённое соглашение в экосистеме Vue и обязательное для использования JSX. Начиная с версии 3.4.0 плагина Babel для Vue, мы автоматически внедряем const h = this.$createElement в любой метод и геттер (не функциях или стрелочных функциях), объявленных в синтаксисе ES2015 с JSX, поэтому можно удалить параметр (h) . В предыдущих версиях плагина, ваше приложение будет выкидывать ошибку, если h недоступен в области видимости.

Подробную информацию о преобразовании JSX в JavaScript можно найти в документации плагина.

Функциональные компоненты

Компонент для заголовков с «якорями», который мы создали выше, довольно прост. У него нет какого-либо состояния, хуков или требующих наблюдения данных. По сути, это всего лишь функция с параметром.

В подобных случаях мы можем пометить компоненты как функциональные (опция functional ), что означает отсутствие у них состояния (нет реактивных данных) и экземпляра (нет переменной контекста this ). Функциональный компонент выглядит так:

Vue.component('my-component', { 
functional: true,
// входные параметры опциональны
props: {
// .
},
// чтобы компенсировать отсутствие экземпляра
// мы передаём контекст вторым аргументом
render: function (createElement, context) {
// .
}
})

Примечание: в версиях до 2.3.0 опция props необходима, если вы хотите использовать входные параметры в функциональном компоненте. С версии 2.3.0 и выше вы можете опустить опцию props и все атрибуты, найденные на узле компонента, будут неявно извлечены в качестве входных данных.

Ссылка будет указывать на HTMLElement при использовании с функциональными компонентами, потому что у них нет состояния и экземпляра.

С версии 2.5.0+, если вы используете однофайловые компоненты, вы можете объявить функциональные компоненты, основанные на шаблоне таким образом:

template functional> 
template>

Всё необходимое компоненту передаётся через context — объект, содержащий следующие поля:

  • props : Объект со всеми переданными входными параметрами
  • children : Массив дочерних виртуальных узлов
  • slots : Функция, возвращающая объект со слотами
  • scopedSlots : (2.6.0+) Объект, содержащий все переданные слоты с ограниченной областью видимости. Также предоставляет доступ к обычным слотам в качестве функций
  • data : Объект данных целиком, переданный объекту вторым аргументом createElement
  • parent : Ссылка на родительский компонент
  • listeners : (2.3.0+) Объект, содержащий все зарегистрированные в родителе прослушиватели событий. Это просто псевдоним для data.on
  • injections : (2.3.0+) Если используется опция inject , будет содержать все разрешённые инъекции.

После указания functional: true , обновление render -функции нашего компонента для заголовков потребует только добавления параметра context , обновления this.$slots.default на context.children и замены this.level на context.props.level .

Поскольку функциональные компоненты — это просто функции, их отрисовка значительно быстрее.

Кроме того, они очень удобны в качестве обёрток. Например, если вам нужно:

  • Выбрать один из компонентов для последующей отрисовки в данной точке
  • Произвести манипуляции над дочерними элементами, входными параметрами или данными, перед тем как передать их в дочерний компонент

Вот пример компонента smart-list , делегирующего отрисовку к более специализированным компонентам, в зависимости от переданных в него данных:

var EmptyList = { /* . */ }
var TableList = { /* . */ }
var OrderedList = { /* . */ }
var UnorderedList = { /* . */ }

Vue.component('smart-list', {
functional: true,
props: {
items: {
type: Array,
required: true
},
isOrdered: Boolean
},
render: function (createElement, context) {
function appropriateListComponent () {
var items = context.props.items

if (items.length === 0) return EmptyList
if (typeof items[0] === 'object') return TableList
if (context.props.isOrdered) return OrderedList

return UnorderedList
}

return createElement(
appropriateListComponent(),
context.data,
context.children
)
}
})

Передача атрибутов и событий дочерним элементам/компонентам

В обычных компонентах, атрибуты не определённые как входные параметры, автоматически добавляются к корневому элементу компонента, заменяя или правильно объединяя любые существующие атрибуты с тем же именем.

Однако функциональные компоненты требуют явного определения этого поведения:

Vue.component('my-functional-button', { 
functional: true,
render: function (createElement, context) {
// Явная передача любых атрибутов, слушателей событий, дочерних элементов и т.д.
return createElement('button', context.data, context.children)
}
})

Передавая context.data вторым аргументом в createElement , мы передаём любые атрибуты или слушатели событий, используемые в my-functional-button . На самом деле это настолько очевидно, что для событий не требуется модификатор .native .

Если вы используете функциональные компоненты на основе шаблонов, вам также придётся вручную добавлять атрибуты и слушатели. Поскольку у нас есть доступ к индивидуальному содержимому контекста, мы можем использовать data.attrs для передачи любых атрибутов HTML и listeners (псевдоним для data.on ) для передачи любых слушателей событий.

template functional> 
button
class="btn btn-primary"
v-bind="data.attrs"
v-on="listeners"
>
slot/>
button>
template>

slots() vs children

Вы можете задаться вопросом зачем нужны slots() и children одновременно. Разве не будет slots().default возвращать тот же результат, что и children ? В некоторых случаях — да, но что если у нашего функционального компонента будут следующие дочерние элементы?

my-functional-component> 
p v-slot:foo>
первый
p>
p>второй p>
my-functional-component>

Для этого компонента, children даст вам оба абзаца, slots().default — только второй, а slots().foo — только первый. Таким образом, наличие и children , и slots() позволяет выбрать, знает ли компонент о системе слотов или просто делегировать это другому компоненту, путём передачи children .

Компиляция шаблонов

Возможно, вам будет интересно узнать, что Vue-шаблоны в действительности компилируются в render -функцию. Обычно нет необходимости знать подобные детали реализации, но может быть любопытным посмотреть на то, как компилируются те или иные возможности шаблонов. Ниже приведена небольшая демонстрация использования метода Vue.compile , который в режиме реального времени компилирует строки шаблонов:

Обнаружили ошибку или хотите добавить что-то своё в документацию? Измените эту страницу на GitHub! Опубликовано на Netlify .

render js что это

Говоря о библиотеке React, render — это метод жизненного цикла компонента, который вызывается для отображения компонента на странице. Он возвращает React-элемент, представляющий содержимое компонента.

Метод render определяет, что должно быть отображено на экране в ответ на изменение состояния или свойств компонента. Он вызывается автоматически, когда компонент монтируется или обновляется.

Пример использования render :

 

Hello, World!

); > >

В этом примере метод render возвращает React-элемент, содержащий заголовок h1 . Когда компонент MyComponent монтируется на странице или обновляется, React вызывает метод render , чтобы отобразить содержимое компонента.

13 апреля 2023

В контексте JavaScript, render может использоваться для динамического изменения содержимого веб-страницы без перезагрузки страницы. Другими словами, render — это такая функция, которая отвечает за отображение информации на странице.

Например, если у нас есть элемент div с id=»output» , мы можем задействовать render при отображении текста внутри этого элемента следующим образом:

const outputDiv = document.getElementById('output'); outputDiv.innerHTML = 'Привет, мир!'; 

Еще пример, где будет задействован render при создании новых элементов и добавления их на страницу:

const newDiv = document.createElement('div'); newDiv.innerHTML = 'Этот элемент создан с помощью JavaScript и добавлен на страницу!'; document.body.appendChild(newDiv); 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *