Создание компонентов Vue.JS

Используйте макросы babel и макрос codegen для создания компонентов, совместимых с SSR.

Недавно я создал компонент кнопки, который должен был иметь значок либо слева, либо справа от текста. API примерно такой:

<icon-button right-glyph="lock">Sign In</icon-button

Свойство right-glyph принимает строку, соответствующую некоторому SVG. Проблема, с которой я столкнулся, заключалась в том, что мне нужно было импортировать все SVG в объект и сопоставлять свойства глифа с ключами объекта.

const svgs = {
  lock: () => import('src/assets/svg/lock.svg'),
  eye: () => import('src/assets/svg/eye.svg'),
  ...
}

Это было бы утомительно поддерживать; каждый SVG в assets/svg должен иметь соответствующий ключ в объекте svgs.

Хорошим решением этой проблемы является создание объекта svgs из SVG, которые существуют в папке во время компиляции. Таким образом, наш объект всегда синхронизируется, и мы можем использовать папку SVG в качестве ссылки при передаче свойств нашему компоненту.

Для реализации этого решения мы можем использовать babel-макросы. babel-macros позволяет запускать преобразования кода в виде пользовательских макросов. Мы используем макрос codegen. Макрос codegen требует, чтобы код, который вы хотите выполнить во время времени выполнения, был преобразован в строку во время во время компиляции.

Я создал функцию, которая находит все файлы в моей папке SVG и сопоставляет их имена файлов со строковым представлением объекта. Этот объект будет выглядеть так:

{ 
  name: 'lock', 
  component: () => require.ensure(
      [], 
      (require) => require('../../assets/svgs/lock.svg'
    )
}

Функция возвращает строку, которая при преобразовании в код сократит отображаемые объекты в один объект для использования.

Примечание. Я использую Webpack для включения разделения кода, поэтому require.ensure.

Примечание 2. vue-svg-loader загружает svgs как компоненты Vue.js.

Использовать функцию generateSvgComponents теперь легко. Нам просто нужно сказать codegen экспортировать результат функции:

Теперь, когда мы запускаем нашу среду разработки или собираем приложение, codegen сгенерирует код, необходимый для установки переменной svgs, что позволит нам использовать ее в другом месте:

<svg-loader :name="selectedSvg" />

Настраивать

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

$ yarn add --dev babel-macros

В файле .babelrc добавьте "babel-macros" к плагинам:

{
  "presets": [...],
  "plugins": [
    "babel-macros",
    ...
  ]
}

Добавьте свои макросы

$ yarn add --dev babel-plugin-codegen

См. этот пост для получения дополнительной информации о макросах babel и доступных макросах.

Примечания

Если вы собираетесь использовать vue-svg-loader или что-то подобное, гораздо проще импортировать SVG как точку разделения кода:

{
  template: `<div><lock-icon /></div>`,
  components: {
    LockIcon: () => import('./lock.svg')
  }
}

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

Кодекс

Источник: https://github.com/johnnynotsolucky/samples/tree/master/generated-svgs

Демонстрация: https://johnnynotsolucky.github.io/samples/generated-svgs/

P.S. У меня не было возможности понять, как использовать () => import('...') вместо () => require.ensure(...). import является предпочтительным способом для динамического разделения кода, но он генерирует синтаксическую ошибку при запуске внутри макроса. Если у кого-то есть решение, это было бы здорово!