Введение в функцию визуализации Vue (с примерами)
Свободный перевод статьи Introduction to Vue Render Functions (w/ Examples)
В этой статье, я хочу рассказать о функции рендеринга Vue, чтобы вы могли лучше понять как работает Vue.js в целом.
Что такое функция рендеринга?
Каждый компонент Vue реализует функцию рендеринга. В большинстве случаев функция создается компилятором Vue. Когда вы определяете шаблон (template) для вашего компонента, содержимое этого шаблона обрабатываться компилятором Vue (Vue.compile()), который возвращает функцию рендеринга. Функция рендеринга по существу возвращает виртуальный узел DOM, который будет визуализирован Vue в DOM вашего браузера.
Хорошо, а что такое виртуальный DOM?
Виртуальная объектная модель документа (или «DOM») позволяет Vue визуализировать ваш компонент в его памяти перед обновлением браузера. В виртуальном DOM это делается быстрее, потому что, в нем нет прямого взаимодействия с DOM браузером. Когда Vue обновляет DOM вашего браузера, он сравнивает обновленный виртуальный DOM с его предыдущим состоянием и обновляет в реальном DOM только те части, которые были изменены.
Функция рендеринга возвращает виртуальный узел DOM, обычно называемый VNode в экосистеме Vue, который является интерфейсом, который позволяет Vue записывать эти объекты в DOM вашего браузера. Они содержат всю информацию, необходимую для работы с Vue. Следующая версия Vue будет включать в себя совершенно новую виртуальную реализацию DOM, которая будет еще быстрее, чем сейчас. React, Riot, Inferno и многие другие также используют концепцию Virtual DOM.
Это изображение было найдено и изменено из статьи Майкла Галлахера Low Verbosity i18n
Более подробно о виртуальном DOM можно почитать тут
Вы можете реализовать свою функцию рендеринга Vue в любом компоненте Vue. Также, учитывая реактивность Vue, функция рендеринга будет вызываться всякий раз, когда реактивное свойство компонента будет обновлено. Вот краткий пример того, как можно визуализировать тег H1 непосредственно из функции рендеринга компонента:
new Vue({ el: '#app', render(createElement) { return createElement('h1', 'Hello world'); } });
Есть некоторые встроенные компоненты, которые используют все мощь функций рендеринга, таких как transition и keep-alive. Эти компоненты управляют VNodes непосредственно в функции рендеринга.
Вы также можете почитать официальную документацию Vue здесь.
Как компилятор Vue вписываются в функции рендеринга?
В большинстве случаев функция визуализации Vue будет компилироваться компилятором Vue во время сборки вашего проекта (например, с помощью Webpack). Функцией компилятора является Vue.compile(). Однако, если вам необходимо вы можете задействовать функцию компилятора в своем коде. Это может быть очень удобно. Пример того, как вы можете использовать компилятор для компиляции строки шаблона в функцию рендеринга:
const template = ` <ul> <li v-for="item in items"> {{ item }} </li> </ul>`; const compiledTemplate = Vue.compile(template); new Vue({ el: '#app', data() { return { items: ['Item1', 'Item2'] } }, render(createElement) { return compiledTemplate.render.call(this, createElement); } });
Как видите, Vue.compile возвращает объект, который содержит готовую к использованию функцию рендеринга.
Важность привязки событий в функциях рендеринга Vue
Это подводит нас к привязке событий. Функция createElement может принимать параметр, называемый объектом данных. Этот объект может иметь несколько свойств, которые эквивалентны директивам типа v-bind:on которые вы используете в своих стандартных шаблонах. Вот пример простого компонента счетчика с кнопкой, которая увеличивает количество кликов.
new Vue({ el: '#app', data() { return { clickCount: 0, } }, methods: { onClick() { this.clickCount += 1; } }, render(createElement) { const button = createElement('button', { on: { click: this.onClick } }, 'Click me'); const counter = createElement('span', [ 'Number of clicks:', this.clickCount ]); return createElement('div', [ button, counter ]) } });
Но объект данных не ограничен привязкой событий! Вы также можете применять классы к элементу, как если бы вы делали это с помощью директивы v-bind: class.
new Vue({ el: '#app', data() { return { clickCount: 0, } }, computed: { backgroundColor() { return { 'pink': this.clickCount%2 === 0, 'green': this.clickCount%2 !== 0, }; } }, methods: { onClick() { this.clickCount += 1; } }, render(createElement) { const button = createElement('button', { on: { click: this.onClick } }, 'Click me'); const counter = createElement('span', { class: this.backgroundColor, }, [ 'Number of clicks:', this.clickCount ]); return createElement('div', [ button, counter ]) } });
Вы найдете больше информации об объекте данных создаваемым createElement в этом разделе документации Vue.
Реальный сценарий использования переопределения шаблонов
Я думаю, что очень интересно понять, как Vue работает под капотом. Как говориться, единственный способ узнать, используете ли вы инструмент наиболее эффективно, — это точно знать, как он работает.
Это не значит, что вы должны начать конвертировать все ваши шаблоны на собственные функции рендеринга, но иногда они могут пригодиться, поэтому вы должны хотя бы знать, как их использовать.
В приведенном примере я покажу вам, как использовать пользовательскую функцию рендеринга в компоненте.
Сначала давайте создадим нашу начальную разметку.
<div id="app"> <heading> <span>Heading title is: {{ title }}</span> </heading> </div>
Внутри нашего div, где будет монтироваться приложение Vue, мы определим пользовательский шаблон. По сути, мы хотим, чтобы шаблон переопределял heading по умолчанию.
Первое, что мы сделаем, это просканируем пользовательские шаблоны и предварительно скомпилируем их с помощью компилятора Vue:
const templates = []; const templatesContainer = document.getElementById('app'); // We iterate through each custom templates and precompile them for (var i = 0; i < templatesContainer.children.length; i++) { const template = templatesContainer.children.item(i); templates.push({ name: template.nodeName.toLowerCase(), renderFunction: Vue.compile(template.innerHTML), }); }
Затем давайте создадим компонент heading:
Vue.component('heading', { props: ['title'], template: ` <overridable name="heading"> <h1> {{ title }} </h1> </overridable>` });
Это просто простой компонент, который имеет входящее props с именем title. Шаблон по умолчанию будет отображать тег h1 с title. Этот компонент будет состоять из компонента overridable, который мы создадим.
В нем мы будем использовать пользовательскую функцию рендеринга.
Vue.component('overridable', { props: ['name'], render(createElement) { // We use the templates array we created when the application was initialized. const template = templates.find(x => x.name === this.name); // We return the default content using the default slot when there are no custom templates. if (!template) { return this.$slots.default[0]; } // Using the precompiled render function return template.renderFunction.render.call(this.$parent, createElement); } });
Затем смонтируем наше приложение Vue:
new Vue({ el: '#app', template: `<heading title="Hello world"></heading>` });
В этом примере мы видим, что используется шаблон по умолчанию, поэтому это стандартный тег h1.
Если мы добавим пользовательский шаблон в div#app, то увидим, что компонент heading теперь будет отображать пользовательский шаблон, который мы указали.
Заключение
Функции рендеринга Vue являются фундаментальной частью самого Vue, поэтому я действительно считаю полезным потратить некоторое время и полностью понять концепцию (особенно если вы регулярно используете фреймворк). И поскольку Vue.js развивается и становится более эффективным, знания, которые вы приобретете, разобравшись с тем, что находится под капотом, помогут вам развиваться вместе с ним.