От автора: сегодня мы поговорим о совместном использовании Vue JSX. Vue.js обладает легким API и парой опций определения HTML шаблонов в компонентах. Можно использовать вариант с тегом template, определить свойство template в корневом компоненте или использовать компоненты Single-File.
Опции сверху удивительны и идеально работают, но в жизненном цикле вашего приложения наступает такой момент, когда эти способы начинают выглядеть глупо, чрезмерно спроектированными и крайне негибкими.
Так зачем же использовать JSX вместо других определений шаблонов?
JSX легко читать. <div>…</div> объективно лучше чем this.$createElement(‘div’, {}, […])
Серьезно, это просто JS
Vue поддерживает JSX
JSX облегчает импорт и управление Vue компонентами
Быстрое введение
Давайте я вам на примере поясню, почему JSX – это хорошо.
Нам нужно создать компонент <TextField/>, который одновременно может быть input для однострокового текста и textarea для многострокового текста. Наш шаблон может быть таким:
<div> <textarea v-if="multiline" v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false"> <input v-else v-model="content" :name="name" :placeholder="placeholder" :aria-invalid="false"> </div>
Как видно из кода сверху, мы сразу сталкиваемся с парой проблем типа дублирования кода и т.д. Представьте, как вам придется поддерживать свойства на input. Этот маленький кусок кода вырастет, и его обслуживание превратится в кошмар.
Чтобы это исправить, нам нужно пойти по низкому уровню с Vue. Чтобы исправить этот беспорядок, нам нужно ближе подобраться в внутреннему API Vue.
Метод render()
У каждого компонента в Vue есть метод render. Здесь Vue выбирает компонент для рендера. Даже если мы не определяли этот метод, Vue сделает это за нас.
Это значит, когда мы определяем HTML шаблоны в Vue, компилятор шаблонов Vue компилирует их в функцию createElement, принимающую пару параметров и возвращающую результат функции render.
Чтобы поправить код выше, нужно удалить свойство template или тег и определить метод render() в компоненте. Если метод render определен в компоненте, Vue проигнорирует определение шаблона.
... export default { name: 'TextField', render (createElement) { const tag = this.multiline ? 'textarea' : 'input' return createElement(tag, { class: { 'text-input': true, 'is-disabled': false }, attrs: { name: this.name, placeholder: this.placeholder, 'aria-invalid': false } }) } } ...
Что делает код выше:
Метод render принимает хелпер createElement из Vue
Мы программно определяем тег
Далее мы создаем тег и передаем его атрибуты, классы и т.д. в виде объекта. В createElement можно передать лишь пару опций.
Мы возвращаем новый созданный элемент для рендера.
Заметка: все шаблоны, созданные для компонента Vue, будут конвертированы в метод render, который возвращает функцию createElement. Поэтому метод render будет иметь приоритет над определением шаблона.
Разберем пример:
<div> <p>Only you can stop forest fires</p> </div>
Компилятор шаблона конвертирует HTML выше в:
... render (createElement) { return createElement( 'div', {}, createElement( 'p', {}, 'Only you can stop forest fires' ) ) } ...
ОК! Теперь вы спросите: «а разве это не совсем нечитаемо?». Ответ – да. Как только вы определяете компонент с множеством вложенных элементов или парой смежных элементов, мы сталкиваемся с новой проблемой. Мы пожертвовали читаемостью. Как говорится «из огня да в полымя».
Сейчас мы вернем JSX его читаемый вид.
Что такое JSX
Если вы знаете JSX, можете перейти к следующему разделу, где я покажу, как использовать JSX в Vue.
JSX – термин, придуманный командой Facebook.
«JSX – это XML-подобное расширение для JS без определенной семантики.»
JSX не предназначен для реализации движками или браузерами. Вместо этого мы используем транспиллеры типа Babel для перевода JSX в обычный JS.
// this line below is an example of JSX const heading = <h1>Welcome to Scotch</h1>;
JSX позволяет использовать HTML синтаксис в JS.
К сожалению, эта статья предполагает знание JSX, поэтому изучение JSX выходит за рамки статьи. Но я укажу, куда вам нужно двигаться. В JSX очень легко разобраться, это можно сделать за пару минут.
Настройка Vue под использование JSX
Если вы используете Vue-cli 3.0 и выше, то вам повезло, он поддерживает JSX. В более старых версиях Vue-cli можно добавить поддержку, установив babel-preset-vue-app и добавив его в файл .babelrc.
Установка:
# Using npm npm install --save-dev babel-preset-vue-app # Using yarn yarn add --dev babel-preset-vue-app
Файл .babelrc:
{ "presets": ["vue-app"] }
Теперь мы можем использовать JSX в функции render нашего компонента.
Минусы синтаксиса Vue JSX
У JSX в Vue есть несколько минусов.
Первое – вы больше не можете использовать : и @ для привязки и ожидания событий. В синтаксисе JSX они невалидны, и код не пройдет компиляцию.
Чтобы слушать события в JSX, необходимо добавить префикс on. Например, для кликов используйте onClick.
render (createElement) { return ( <button onClick={this.handleClick}></button> ) }
Для модификации событий используйте:
render (createElement) { return ( <button onClick:prevent={this.handleClick}></button> ) }
Для привязки переменной вместо : используйте:
render (createElement) { return ( <button content={this.generatedText}></button> ) }
Чтобы устанавливать HTML строки как контент элемента вместо v-html используйте:
render (createElement) { return ( <button domPropsInnerHTML={htmlContent}></button> ) }
Можно распространять большой объект.
render (createElement) { return ( <button {...this.largeProps}></button> ) }
Использование JSX в рендере
Вернемся к нашему первоначальному TextField компоненту. После активации JSX в Vue приложении мы можем сделать это:
render (createElement) { const inputAttributes = { class: 'input-field has-outline', // class definition onClick: this.handleClick // event handler backdrop: false // custom prop } const inputMarkup = this.multiline ? <textarea {...inputAttributes}></textarea> : <input {...inputAttributes}/> return inputMarkup }
Импорт Vue JSX компонентов
Еще один плюс использования JSX в Vue – больше не нужно регистрировать все компоненты. Просто импортируем и используем.
import {Button} from '../components' export default { render (createElement) { return <Button primary={true}>Edit</Button> } }
Как заставить JSX работать с TypeScript
TypeScript используется как механизм, добавляющий типы в JS. Чтобы добавить поддержку JSX в TypeScript, нужно лишь изменить tsconfig.json. Для активации JSX в TypeScript сохраните файл как .tsx и измените tsconfig.json следующим образом:
{ "compilerOptions": { .... "jsx": "preserve", } }
Установка jsx в значение preserve значит, что TypeScript не должен обрабатывать JSX. Это позволяет Babel взять контроль над JSX и TypeScript, так как он еще не поддерживается в Vue JSX.
Затем создайте файл jsx.d.ts в проекте и добавьте объявления TypeScript JSX для Vue.
import Vue, {VNode} from 'vue' declare global { namespace JSX { interface Element extends VNode {} interface ElementClass extends Vue {} interface ElementAttributesProperty { $props: {} } interface IntrinsicElements { [elemName: string]: any } } }
Проверьте, чтобы TypeScript загрузил файл объявлений. Можно добавить автозагрузку в tsconfig.json через:
{ "compilerOptions": { ... "typesRoot": ["./node_modules/@types", "./types"] } }
Заключение
На сегодня все. Наслаждайтесь парой или всеми Vue.js шаблонами в JSX.
И пожалуйста, не жалуйтесь о том, что JSX ломает SOC (separation of concerns). Я не могу принять еще один из тех аргументов. Если вам нравится использовать функцию createElement с объектами, делайте это!!
Пишите свои мысли и предложения в комментариях.
Автор: Samuel Oloruntoba
Источник: https://scotch.io/
Редакция: Команда webformyself.