Vue шаблоны на JSX

Vue шаблоны на JSX

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

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

Аргумент ЗА

  • Вся мощь JavaScript в вашем распоряжении.
  • Нет необходимости регистрировать компоненты.
  • Spread оператор на максималках.
  • Именование компонентов и props-параметров.
  • Отсутствие конфликтов с существующими HTML-элементами.
  • Вы не привязаны к паттерну: один файл = один компонент.

Аргумент ПРОТИВ

  • Для обычного верстальщика это выглядит чуждым.
  • Отсутствие директивной управления структурой.
  • Больше никаких тегов style в ваших однофайловых компонентах.
  • JSX непопулярен в экосистеме Vue.

Я собираюсь поделиться своими первоначальными мыслями об использовании JSX с Vue. Я буду выкладывать одновременно два примера шаблонов Vue и их JSX-аналогов, для сравнения.

Чтобы заставить шар катиться, вот простой пример того, как выглядит JSX в простом компоненте Vue:

<template>
  <h1>{{ message }}</h1>
</template>

<script>
  export default {
    data: () => ({
      message: "Hello, JSX!"
    })
  };
</script>
export default {
  data: () => ({
    message: "Hello, JSX!"
  }),

  render() {
    return <h1>{this.message}</h1>;
  }
};

За: Вся мощь JavaScript в вашем распоряжении.

Шаблоны Vue ограничиваются использованием только зарегистрированных компонентов. С помощью JSX можно делать всё что угодно внутри функции рендеринга.

Нет необходимости присваивать функции методам, что означает немного меньше шаблонов.

<template>
  <span>{{ formatPrice(this.price) }}</span>
</template>

<script>
  import { formatPrice } from "./util";

  export default {
    props: ["price"],

    methods: {
      formatPrice
    }
  };
</script>
import { formatPrice } from "./util";

export default {
  props: ["price"],

  render() {
    return <span>{formatPrice(this.price)}</span>;
  }
};

За: Нет необходимости регистрировать компоненты

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

<template>
  <span class="price-tag">
    <formatted-price :price="price"></formatted-price>
  </span>
</template>

<script>
  import FormattedPrice from "./FormattedPrice";

  export default {
    data: () => ({
      price: 100
    }),

    components: {
      FormattedPrice
    }
  };
</script>
import FormattedPrice from "./FormattedPrice";

export default {
  data: () => ({
    price: 100
  }),

  render() {
    return (
      <span class="price-tag">
        <FormattedPrice price={this.price} />
      </span>
    );
  }
};

За: Spread оператор

В шаблонах Vue мы можем использовать v-bind для передачи объекта в качестве prop-параметра компонента. Пример из документации Vue:

<blog-post v-bind="post"></blog-post>

<!-- Will be equivalent to: -->

<blog-post v-bind:id="post.id" v-bind:title="post.title"></blog-post>

Это очень похоже на синтаксис JavaScript spread, который доступен нам в JSX.

Дополнительным преимуществом синтаксиса spread является то, что его можно использовать несколько раз в компоненте. Так как v-bind является атрибутом, он ограничен одним объявлением, а на использование spread такое ограничение не накладывается.

<template>
  <!-- This doesn't work! -->
  <blog-post v-bind="post" v-bind="metaData"></blog-post>
</template>

<script>
  // ...
</script>
export default {
  // ...
  render() {
    return <BlogPost {...this.post} {...this.metaData} />;
  }
};

За: Именование для людей

Именование во Vue достаточно грубое, и, иногда, запутывающее.

Шаблонам нужно, чтобы всё было кебаб-кейсом, в то время как всё в вашем сценарии, вероятно, верблюжьим кейсом.

Из документации Vue:

HTML имена атрибутов не чувствительны к регистру, поэтому браузеры будут интерпретировать любые заглавные символы как строчные. Это означает, что когда вы используете шаблоны в DOM, имена props-параметров в формате camelCase должны использовать свои эквиваленты в формате шаблона (с дефисом).

Вы можете использовать PascalCase для компонентов и camelCase для props-параметров ваших файлов .vue, но тогда они не будут работать в шаблонах встраиваемых в DOM. О, и это относится только к названиям компонентов и props-ам. События должны быть написаны эквивалентно тому, как объявлены, никаких закулисных изменений кейса там нет.

<!-- App.vue -->
<template>
  <!-- Компоненты могут быть вызваны с помощью PascalCase, а аттрибуты kebab-аннотацией -->
  <PostList
    posts="posts"
    link-color="blue"
    @post-click="handlePostClick"
  ></PostList>

  <!-- Это также работает, но не в шаблонах в DOM -->
  <!-- Не забывайте, что вы не можете изменить регистр событий! -->
  <PostList
    posts="posts"
    linkColor="blue"
    @post-click="handlePostClick"
  ></PostList>
</template>

<!-- PostList.vue -->
<script>
export default {
  // Meanwhile, props should be declared with camelCase
  props: ['linkColor'],
}

Все эти проблемы исчезают при использовании JSX. Поскольку вы пишете JavaScript, вы просто используете PascalCase и kebabCase везде, где пожелаете.

export default {
  // ...
  render() {
    return (
      <PostList
        posts={this.posts}
        linkColor="blue"
        onPostClick={() => this.handlePostClick()}
      />
    );
  }
};

За: Отсутствие конфликтов с существующими элементами HTML

Больше дополнительных преимуществ, потому что мы отходим от HTML. Из документации Vue:

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

Кроме того, если вы хотите использовать пользовательский компонент формы, вам нужно переименовать его, потому что он будет конфликтовать с существующим тегом формы. Переименовывать вещи уже по себе достаточно сложно!

<template>
  <my-form></my-form>
</template>

import Form from "./Form";

export default {
  render() {
    return <Form />;
  }
};

За: Вы не привязаны к требованию создания файла на отдельный компонент.

Иногда вам хочется написать небольшой компонент, который будет использоваться только в контексте другого компонента. С файлами .vue, вам нужно будет создать два файла, даже если второй тривиален и не должен использоваться больше нигде.

С помощью JSX вы можете структурировать вещи как угодно. Обычно я придерживаюсь одного компонента в каждом файле, но может быть полезно извлечь из него кусочки, чтобы сделать вещи более читабельными.

<template>
  <article>
    <post-title :title="title"></post-title>
    <section>{{ post.contents }}</section>
  </article>
</template>
<script>
  import PostTitle from "./PostTitle";

  export default {
    props: ["post"],

    components: {
      PostTitle
    }
  };
</script>

<!-- PostTitle теперь можно импортировать куда угодно, в то время как мы создали его только для компонента Post -->
<template>
  <h1>{{ title }}</h1>
</template>
<script>
  export default {
    props: ["title"]
  };
</script>
// Поскольку остальная часть приложения не нуждается в PostTitle, мы не должны делать этот компонент доступным другим компонентам
const PostTitle = {
  props: ["title"],

  render() {
    return <h1>{this.title}</h1>;
  }
};

export default {
  props: ["post"],

  render() {
    return (
      <article>
        <PostTitle title={this.post.title} />
        <section>{this.post.contents}</section>
      </article>
    );
  }
};

Недостаток: Это выглядит пугающим для людей, которые не имели опыта работы с JSX

Если кто-то другой подготавливает вёрстку вашего приложения, вы принуждаете его к пугающей JavaScript-территории, а не к счастливой и беззаботной HTML-странице.

Это зависит от вашей команды и ситуации, стоит ли идти на компромисс.

Недостаток: Никаких директив управления структурой DOM

Возможно, вы будете скучать по Vue синтаксису рендеринга блоков и вывода их в цикле. Но, лично я, не против полностью использовать JavaScript, например, используя map вместо v-for.

Меньше пользовательских директив означает меньше абстракции между шаблоном и кодом, который он компилирует.

<template>
  <ul>
    <li v-for="post in posts" :key="post.id">{{ post.title }}</li>
  </ul>
</template>
export default {
  // ...
  render() {
    return (
      <ul>
        {this.posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    );
  }
};

Недостаток: Больше никаких тегов style в ваших однофайловых компонентах.

Если вы используете теги style или хотите, чтобы в компонентах Vue присутствовали скопированные стили, вам придётся искать другое решение.

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

Недостаток: JSX непопулярен в экосистеме Vue.

Несмотря на то, что Vue предлагает возможность использования JSX, похоже, что в сообществе Vue оно не имеет большой популярности.

С JSX приходится делать что-то другое, в отрезе от фреймворка Vue. Обычно, проще всего держаться за большинство, когда дело касается стека, если только это не приносит существенной пользы.

Дадим шанс JSX во Vue

Я работаю над проектом, в котором достаточно много низкоуровневых компонентов. Они содержат большое количество скриптов с небольшим количеством шаблонов. В JSX чувствуется глоток свежего воздуха в этом случае.

С другой стороны, при построении больших шаблонов, состоящих из огромных кусков html с некоторыми пользовательскими компонентами и директивами, шаблоны Vue подходят лучше.

К счастью, нам не нужно выбирать один из этих вариантов, мы можем использовать оба! Я буду заниматься низкоуровневыми компонентами на JSX, а "представления", которые будут писаться другими разработчиками, будут писать с привычными шаблонами Vue.

Однозначно, стоит попробовать на одном-двух проектах, оценить, как оно, и принять решение: остаться на стороне JSX, или полностью и бесповоротно вернуться на стандартные шаблоны Vue.

Резюме

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