Может ли шаблон Vue иметь несколько корневых узлов (фрагментов)?

Если вы попытаетесь создать шаблон Vue без корневого узла, например:

<template>
  <div>Node 1</div>
  <div>Node 2</div>
</template>

Вы получите ошибку компиляции и/или выполнения, так как шаблоны должен иметь один корневой элемент.

Как правило, вы решаете эту проблему, добавляя «оболочку» div как родитель. Этот элемент-оболочка не предназначен для отображения, он просто присутствует, поэтому ваш шаблон соответствует требованию с одним корнем.

<template>
  <div>
    <div>Node 1</div>
    <div>Node 2</div>
  </div>
</template>

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

Примечание: эта статья изначально была опубликована здесь, в блоге разработчиков Vue.js 2018/09/11.

Рендеринг массивов

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

Например, для правильной работы некоторых функций CSS требуется особая иерархия элементов, например CSS grid или flex. Наличие оболочки между родительским и дочерним элементами не вариант.

<template>
  
  <div style="display:flex">
    <FlexChildren/>
  </div>
</template>

Также существует проблема, из-за которой добавление элемента-оболочки к компоненту может привести к отображению недопустимого HTML-кода. Например, если вы строите таблицу, строку таблицы, <tr>должны иметь только ячейки таблицы, <td>для детей.

<template>
  <table>
    <tr>
      
      <TableCells/>
    </tr>
  </table>
</template>

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

Фрагменты

Это ограничение с одним корнем также было проблемой для React, но оно предоставило ответ в версии 16 с функцией под названием фрагменты. Чтобы использовать его, оберните свои многокорневые шаблоны в специальный React.Fragment элемент:

class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

Это отобразит дочерние элементы без оболочки. Есть даже аккуратный короткий синтаксис <>:

class Columns extends React.Component {
  render() {
    return (
      <>
        <td>Hello</td>
        <td>World</td>
      </>
    );
  }
}

Фрагменты в Vue

Будет ли Vue-эквивалент фрагментов? Вероятно, не в ближайшее время. Причина этого в том, что алгоритм сравнения виртуальных DOM основан на компонентах, имеющих один корень. По словам участника Vue Линус Борг:

«Разрешение фрагментов требует значительных изменений в [the diffing] алгоритм… важно не только заставить его работать правильно, но и сделать его высокопроизводительным… Это довольно тяжелая задача… React ждал полной перезаписи своего слоя рендеринга, чтобы снять это ограничение. «

Функциональные компоненты с функциями рендеринга

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

Есть еще предостережение: вам нужно использовать функцию рендеринга, так как vue-loader в настоящее время не поддерживает функцию мультирута (хотя есть обсуждение об этом).

TableRows.js

export default {
  functional: true,
  render: h => [
    h('tr', [
      h('td', 'foo'),
      h('td', 'bar'),
    ]),
    h('tr', [
      h('td', 'lorem'),
      h('td', 'ipsum'),
    ])
  ];
});

main.js

import TableRows from "TableRows";

new Vue({
  el: '#app',
  template: `<div id="app">
                <table>
                  <table-rows></table-rows>
                </table>
              </div>`,
  components: {
    TableRows
  }
});

Взлом с директивами

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

До:

<parent>
  <wrapper>
    <child/>
    <child/>
  </wrapper>
</parent>

Промежуточный шаг:

<parent>
  <wrapper/>
  <child/>
  <child/>
</parent>

После:

<parent>
  
  <child/>
  <child/>
</parent>

Немного сложно заставить это работать, поэтому замечательно, что плагин называется фрагменты просмотрасозданный Жюльеном Барбеем.

фрагменты представления

vue-fragments можно установить как плагин в ваш проект Vue:

import { Plugin } from "vue-fragments";
Vue.use(Plugin);

Этот плагин регистрирует глобальный VFragment компонент, который вы используете в качестве оболочки в своих шаблонах компонентов, аналогично синтаксису фрагментов React:

<template>
  <v-fragment>
    <div>Fragment 1</div>
    <div>Fragment 2</div>
  </v-fragment>
</template>

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


Станьте старшим разработчиком Vue в 2020 году.

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

Учить больше


Похожие записи

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

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