Исследуйте JSX в Vue 3

внешний интерфейс Vue.js
Исследуйте JSX в Vue 3

Автор: Dali Intelligent Technology Team — Front-end Lin Chengzhang

Сегодня (22 мая) Лин Чэнчжан, фронтенд-инженер энергичной интеллектуальной команды ByteDance, принял участие в конференции «Vue Conf 21», пообщался с энтузиастами фронтенд-технологий и сделал на конференции статью под названием «Изучаем Vue 3». , Совместное использование JSX в . Ниже приводится полное содержание этого обмена,Окончательное резюме - основные моменты.

введение

Добрый день, одноклассники, я Лин Чэнчжан из энергичной интеллектуальной команды фронтенда ByteDance.В последние шесть месяцев мое свободное время (плюс немного времени на рыбалку) в основном посвящено поддержке Vue 3.Babel JSX Plugin, сегодня я поделюсь с вами про JSX.

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

Хотя текущий еженедельный объем загрузок на NPM составляет более 560 000 (даже больше, чем Vue 3 🤪), причина огромного объема загрузок здесь в основном в том, что проекты, созданные с помощью vue-cli (будь то Vue 2 или Vue 3), будут загружаться.@vue/babel-plugin-jsxДля этого пакета количество пользователей, которые фактически используют JSX, должно быть намного меньше этого числа. Невозможно подсчитать, сколько пользователей разрабатывают с помощью JSX. Большинство пользователей по-прежнему используютtemplateметод развития.

основная концепция

  • template

Во Вью,sfcэто.vueКонец файла обычно содержит три типа языковых блоков верхнего уровня.<template>,<script>а также<style>, который можно понимать как комбинацию HTML, JS и CSS. Каждый.vueВсе файлы в конце файла являются компонентами, и по умолчанию можно экспортировать только один компонент.

  • JSX

сам JS

Почему JSX также поддерживается в Vue

Vue 官方推荐的开发方式是 template,从 Vue 2 开始,template 在运行之前,会被编译成 JavaScript 的 render function。 Этиrender functionНа этапе выполнения это легендарныйVirtual DOM.

Всякий раз, когда мы говорим о шаблоне и JSX, может обсуждаться более крупный вопрос.Reactа такжеVueКак лучше. Некоторым людям может не понравиться выражать пользовательский интерфейс напрямую через JavaScript, но значительное число людей подумают, что использование шаблона для написания может раздражать, особенно для опытных игроков в React. Поскольку vue — самый дружелюбный UI-фреймворк в мире и имеет широкую массовую базу, некоторые люди привыкли напрямую использовать HTML и CSS для написания кода, для них лучше изменить логику написания UI с HTML на шаблон, чем чтобы они думали. Гораздо проще полностью измениться, чтобы начать думать о том, как создавать пользовательские интерфейсы на JavaScript. Но я также должен признать, что для некоторых студентов, которые раньше занимались бэкендом, или разработчиков iOS и Android, они раньше не имели большого контакта с HTML, и писать UI с помощью строки не очень хорошо. шаблоны.

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

Что такое «настоящий» JSX

JSXПервоначально это была спецификация, разработанная facebook, а последняяXМожно понять, что это расширение синтаксиса JavaScript, Заинтересованные студенты могут войти и посмотреть конкретный контент по этой ссылке. Поскольку реализация каждого внешнего интерфейса отличается, он не будет реализован движком или браузером. Его необходимо преобразовать в обычный JS после Transform. Мы можем понимать этот шаг как «включение», прежде чем он сможет работать в браузере. . JSX на самом деле похож на язык шаблонов, но имеет все функции JavaScript, но из-за некоторых ограничений в шаблонах производительность кода, написанного в шаблонах, намного выше, чем у JSX.

<h1>Hello, world!</h1>;

Синтаксис JSX здесь на самом деле:

import { createVNode as _createVNode } from "vue"

_createVNode("h1", null, "Hello, world!");

Изменения, внесенные Vue 3

Сначала Vue 2 был написан на чистом JavaScript, и по мере того, как проект рос, FacebookFlow. Хотя Flow в определенной степени помог, некоторые проблемы все же есть.You Da также публично заявил, что выбор Flow вместо TypeScript был «неправильной ставкой».

В Vue 2 для компиляции JSX требуются зависимости@vue/babel-preset-jsxа также@vue/babel-helper-vue-jsx-merge-propsэти два пакета. Первый пакет отвечает за компиляцию синтаксиса JSX, а второй пакет используется для введения среды выполнения.mergePropsфункция.

Но если вы хотите писать в среде TSX, вам необходимо установить дополнительныеvue-tsx-support.

В Vue 3, пока вы устанавливаете плагин Babel, все готово.Можно понять, что никаких дополнительных сторонних библиотек не требуется, а исходный кодjsx.d.tsПроверка типов для поддержки JSX

Сценарии с использованием JSX

Давайте теперь рассмотрим некоторые сценарии, в которых JSX более элегантен, чем шаблоны.

Запись нескольких компонентов в один файл

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

Например, здесь мы инкапсулируем компонент ввода. Мы надеемся экспортировать компонент "Пароль" и компонент "Текстовая область" одновременно, чтобы пользователи могли использовать его в соответствии с их реальными потребностями. Эти два компонента используют компонент ввода для внутреннего использования, но лишь настраивают некоторые реквизит. В JSX это очень удобно, в принципе достаточно написать простой компонент-функцию, достаточно объявить свойства через интерфейс. Но если он написан в шаблоне, он может быть разбит на три файла, а может и еще на один.index.jsВходной файл для экспорта трех компонентов, время на рыбалку меньше.

Сильно зависит от времени компиляции

Шаблон ссылается наscriptОбъявленный в плагине vscode может помочь проверить его, но он все еще может работать.

Если он написан в TS, здесь содержится ссылка на необъявленную переменную c, и TS может предотвратить выполнение кода непосредственно на этапе компиляции. В настоящее время шаблоны по-прежнему компилируются непосредственно в JS, поэтому сделать это вtemplateВыполняется проверка типов во время компиляции.

Иметь полные навыки программирования в JS

Поскольку сущностью JSX является JavaScript, он обладает всеми возможностями программирования JavaScript. Например, нам нужно перевернуть набор DOM-узлов через часть логики.Если она написана в шаблоне, предполагается, что будет написано две части кода.

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

Общие компоненты

В шаблоне, в силу каких-то исторических причин, текущий универсальный компонент действительно не поддерживается, но это не значит, что это будет невозможно в будущем. Если вам нужно использовать дженерики, вы можете использовать функциональные компоненты, чтобы сначала обернуть слой, но будьте осторожны, чтобы не объявитьFunctionalComponentтип. мы здесь.tsxзаявление в файлеFooКомпоненты, Реквизиты являются общим типом. После объявления вернитесь к шаблону, и мы увидим, что только что определенный общий компонент вступил в силу. Поддержка SFC TS IDE может использовать volar. Volar также поддерживает универсальные компоненты, которые ничем не отличаются от TSX.

На что следует обратить внимание при использовании JSX

Обработка реквизита

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

Обработка слотов

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

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

Мы смотрим на то, как Vue обрабатывает слоты:

Требование Vue к слоту предпочтительно является функцией, которая значительно поможет улучшить производительность во время выполнения. Таким образом, дочерние узлы компонента A будут скомпилированы в{ default: () => [123] }. В соответствии с JSX, согласно ментальной модели обычных пользователей, когда есть только один ребенок, напишите его как{ default: () => [123] }Это тоже нереально.Обычный способ написать это - подключить ребенка напрямую. Однако он должен быть преобразован в функцию на этапе компиляции, иначе во время разработки будет выдано предупреждение, что очень недружелюбно для разработчиков. На самом деле компилятору легко обернуть слой функций в VNode дочернего узла, и это сделано.

В случае с несколькими слотами это немного сложнее, чем с одной сценой. Невозможно передать через реквизит слоты, отличные от дефолтных, и мы можем только найти способ передать их способом, подобным «инструкциям», поэтому самый ранний дизайнv-slotsкоманда для обработки слота. ноv-slotsНекоторым разработчикам это может показаться непонятным. Более интуитивно понятным способом было бы что-то вроде этого, т.е.obejct slots:

Кратко поговорим о двух понятиях: компиляция и время выполнения. Компиляция — это преобразование нашего кода в код, понятный движку JavaScript, а среда выполнения — это когда движок JavaScript начинает выполнять ваш код. Точно так же, как проверка резюме и собеседования при приеме на работу, проверка резюме может быть составлена ​​и проведена для запуска. Вы не можете сказать, что это за кандидат, просто взглянув на резюме. Возвращаясь к только что заданному вопросу, легко написать дочерние элементы как встроенный объект, но если это переменная, при компиляции компилятор не может знать, что передается, будь то слоты или переменная. на самом деле видно при компиляции. Если он находится в файле, компилятор все еще может оценить его, но он не может быть оценен при импорте из другого файла. Каждый файл, который обрабатывает Babel, представляет собой «замкнутый цикл». Итак, на данный момент вам нужно добавить оценку времени выполнения:

Хотя проблема определения того, является ли это слотом, решена, добавление оценки времени выполнения для каждой переменной окажет некоторое влияние на объем скомпилированного продукта.jsx-next #255

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

Сравнение производительности шаблона и JSX

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

В традиционном дереве VDOM мы не можем получить информацию для оптимизации во время выполнения. В Vue 3 статическая информация шаблона полностью используется и, наконец, отражается в дереве VDOM. Например, при сравнении вы можете узнать, какие узлы являются динамическими и какие атрибуты узлов являются динамическими. С помощью этой информации мы можем отметить, какие свойства являются динамическими (целевое обновление) при создании VNode, что является легендарным PatchFlags. КромеPatchFlagsКроме того, VDOM Vue 3 также выполняет некоторое кэширование во время выполнения, например кэширование детей.

Позволь мне объяснитьPatchFlagsКак это работает?По сути, это число, но при запуске ему придается другое значение:

  • Номер 2 (PatchFlags.CLASS): указывает, что класс является динамическим.

  • Номер 4 (PatchFlags.STYLE): указывает, что стиль является динамическим.

Возможно, некоторые студенты не понимают, для чего это нужно.CLASS = 1 << 1, который на самом деле представлен в двоичном формате, в приведенном выше коде:

TEXT = 0000000001

CLASS = 0000000010

STYLE = 0000000100

Например, если класс и стиль узла динамические, поместите его в тегPatchFlags.CLASS | PatchFlags.STYLE,получать0000000011. Чтобы судить, является ли его ТЕКСТ динамическим, просто нужноFLAG & TEXT > 0Просто сделай это.

Так выглядит только что поставленныйpropsКажется, VDOM тоже можно размечать в JSX?

Давайте рассмотрим немного более сложный сценарий. Мы виделиtextareaЗависит от attrs, поэтому после компиляции соответствующий PatchFlag должен быть

_createVNode("textarea", _mergeProps({
  "id": "textarea"
}, attrs), null, 16);

Можно взять этот код и запустить его в одиночку, но посколькуtextareaНа внешнем уровне также есть некоторые компоненты. Attrs — это переменная, определенная отдельно, и она не отвечает. Давайте проигнорируем переменную attrs и будем рассматривать этот код как шаблон. Когда шаблон компилируется, дочерние элементы A фактически делают слой кеша при компиляции.Каждый раз, когда он перерендеривается, нет необходимости создавать VNODE дочерних элементов.В то же время формируется замыкание для дети. Если этот код скомпилирован, дочерние элементы закэшированы и будет отмечена статическая метка, то attrs всегда будет получать значение первого рендеринга.

<A>
  {{
      default: () => (
          // children
      )
   }}
</A>

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

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

Суммировать