Создание собственного плагина для Vite: самое простое руководство

Создание собственного плагина для Vite: самое простое руководство

22 января 2024 г.

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

Как работают плагины в Vite

Чтобы создать плагин, важно знать, что Vite использует разные системы сборки для сервера разработки (команда vite) и пакета (команда vite build).

Для сервера разработки он использует esbuild с собственными ES-модулями, которые поддерживаются современными браузерами, и нам не нужно связывать код. в один файл, и это дает нам быструю HRM (горячую замену модуля).

Для пакета используется rollup.js, поскольку он гибкий и имеет большую экосистему; он позволяет создавать высокооптимизированные производственные пакеты с различными форматами вывода.

Интерфейс плагина Vite основан на Rollup, но с дополнительными опциями и возможностями для работы с сервером разработки.

Создание базового плагина

Когда вы создаете плагин, вы можете встроить его в свой vite.config.js. Для него нет необходимости создавать новый пакет. Как только вы увидите, что плагин оказался полезен в ваших проектах, подумайте о том, чтобы поделиться им с сообществом и внести свой вклад в экосистему Vite.

Кроме того, поскольку у Rollup.js более широкое сообщество и экосистема, вы можете рассмотреть возможность создания плагина дляrollup.js, и он будет работать так же хорошо в Vite. Таким образом, если функциональность вашего плагина работает только для пакета, вы можете использовать плагинrollup.js вместо Vite, и пользователи смогут без проблем использовать ваш плагин Rollup в своих проектах Vite.

Если вы создадите плагин для объединения, вы охватите больше пользователей, которые используют толькоrollup.js. Если ваш плагин повлияет на сервер разработки, вам подойдет плагин Vite.

Начнем создавать плагин непосредственно в vite.config.ts:

// vite.config.ts
import { defineConfig, Plugin } from 'vite';

function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    configResolved(config) {
      console.log(config);
    },
  };
}

export default defineConfig({
  plugins: [
    myPlugin(),
  ],
});

В этом примере я создал плагин под названием myPlugin, который печатает конфигурацию Vite, как только она будет разрешена в консоли на обоих этапах: сервер разработки и пакет. Если я хочу распечатать конфигурацию только в режиме сервера разработки, мне следует добавить apply: 'serve' и apply: 'build' для пакета.

// vite.config.ts
import { defineConfig, Plugin } from 'vite';

function myPlugin(): Plugin {
  return {
    name: 'my-plugin',
    apply: 'serve',
    configResolved(config) {
      console.log(config);
    },
  };
}

export default defineConfig({
  plugins: [
    myPlugin(),
  ],
});

Кроме того, я могу вернуть массив плагинов; это полезно для разделения функций сервера разработки и пакета:

// vite.config.ts
import { defineConfig, Plugin } from 'vite';

function myPlugin(): Plugin[] {
  return [
    {
      name: 'my-plugin:serve',
      apply: 'serve',
      configResolved(config) {
        console.log('dev server:', config);
      },
    },
    {
      name: 'my-plugin:build',
      apply: 'build',
      configResolved(config) {
        console.log('bundle:', config);
      },
    },
  ];
}

export default defineConfig({
  plugins: [
    myPlugin(),
  ],
});

И это почти все; вы можете легко добавлять небольшие плагины в конфигурацию Vite. А если плагин становится слишком большим, я предпочитаю переместить его в другой файл или даже создать пакет.

Если вам нужно что-то более сложное, в документации Vite можно найти множество полезных приемов. Но в качестве примера давайте разберем мой собственный плагин ниже.

Разработка настоящего плагина

Итак, у меня есть плагин для создания SVG-спрайтов на основе файлов значков — vite-plugin-svg- карта спрайтов.

Цель — захватить все значки .svg в папке src/icons и собрать их содержимое в один файл .svg, который называется спрайтом SVG. Начнем со стадии сборки:

import { Plugin, ResolvedConfig } from 'vite';
import path from 'path';
import fs from 'fs-extra';

function myPlugin(): Plugin {
  let config: ResolvedConfig;

  return {
    name: 'my-plugin:build',
    apply: 'build',
    async configResolved(_config) {
      config = _config;
    },
    writeBundle() {
      const sprite = getSpriteContent({ pattern: 'src/icons/*.svg' });
      const filePath = path.resolve(config.root, config.build.outDir, 'sprite.svg');
      fs.ensureFileSync(filePath);
      fs.writeFileSync(filePath, sprite);
    },
  };
}

  1. Хук configResolved позволяет нам получить конфигурацию, когда она будет решена, чтобы использовать ее в следующих хуках;
  2. 2. Хук writeBundle вызывается после завершения процесса объединения, и здесь я создам файл sprite.svg;

    3. Функция getSpriteContent возвращает строку подготовленных спрайтов SVG на основе шаблона src/icons/*.svg. Я не буду углубляться в этот вопрос; вы можете прочитать другую мою статью, объясняющую весь процесс генерации SVG-спрайтов;

    4. Затем я создаю абсолютный путь к sprite.svg, чтобы поместить в него содержимое спрайта SVG с помощью path.resolve(), проверяю, что файл существует (или создаю one) с помощью fs.ensureFileSync. и запишите в него содержимое спрайта SVG.

    Теперь самое интересное — этап сервера разработки. Я не могу использовать здесь writeBundle и не могу размещать файлы, когда работает сервер разработки, поэтому нам нужно использовать промежуточное программное обеспечение сервера, чтобы перехватить запрос к sprite.svg.

    import { Plugin, ResolvedConfig } from 'vite';
    
    function myPlugin(): Plugin {
      let config: ResolvedConfig;
    
      return {
        name: `my-plugin:serve`,
        apply: 'serve',
        async configResolved(_config) {
          config = _config;
        },
        configureServer(server) { // (1)
          return () => {
            server.middlewares.use(async (req, res, next) => { // (2)
              if (req.url !== '/sprite.svg') {
                return next(); // (3)
              }
              const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor });
              res.writeHead(200, { // (4)
                'Content-Type': 'image/svg+xml, charset=utf-8',
                'Cache-Control': 'no-cache',
              });
              res.end(sprite);
            });
          };
        },
      };
    }
    

    1. configureServer — это перехватчик для настройки сервера разработки. Он срабатывает до установки внутреннего промежуточного программного обеспечения Vite; в моем случае мне нужно добавить собственное промежуточное ПО после внутреннего промежуточного ПО, поэтому я возвращаю функцию;
    2. 2. Чтобы добавить собственное промежуточное программное обеспечение для перехвата каждого запроса к серверу разработки, я использую server.middlewares.use(). Мне нужно, чтобы он обнаруживал запросы с URL-адресом [localhost:3000/sprite.svg](http://localhost:3000/sprite.svg), чтобы я мог эмулировать поведение файла;

      3. Если URL запроса не /sprite.svg — перейти к следующему промежуточному программному обеспечению (т. е. передать управление следующему обработчику в цепочке);

      4. Чтобы подготовить содержимое файла, я помещаю результат getSpriteContent в переменную sprite и отправляю его в качестве ответа с настроенными заголовками (тип контента и статус HTTP 200).< /п>

      В результате я смоделировал поведение файла.

      Но если файлы в src/icons изменены, удалены или добавлены, нам следует перезапустить сервер, чтобы сгенерировать новый контент спрайта через getSpriteContent; для этого я буду использовать библиотеку просмотра файлов — chokidar. Добавим в код обработчики chokidar:

      import { Plugin, ResolvedConfig } from 'vite';
      import chokidar from 'chokidar';
      
      function myPlugin(): Plugin {
        let config: ResolvedConfig;
        let watcher: chokidar.FSWatcher; // Defined variable for chokidar instance.
      
        return {
          name: `my-plugin:serve`,
          apply: 'serve',
          async configResolved(_config) {
            config = _config;
          },
          configureServer(server) {
            function reloadPage() { // Function that sends a signal to reload the server.
              server.ws.send({ type: 'full-reload', path: '*' });
            }
      
            watcher = chokidar
              .watch('src/icons/*.svg', { // Watch src/icons/*.svg
                cwd: config.root, // Define project root path
                ignoreInitial: true, // Don't trigger chokidar on instantiation.
              })
              .on('add', reloadPage) // Add listeners to add, modify, delete.
              .on('change', reloadPage)
              .on('unlink', reloadPage); 
      
            return () => {
              server.middlewares.use(async (req, res, next) => {
                if (req.url !== '/sprite.svg') {
                  return next();
                }
                const sprite = getSpriteContent({ pattern, prefix, svgo, currentColor });
                res.writeHead(200, {
                  'Content-Type': 'image/svg+xml, charset=utf-8',
                  'Cache-Control': 'no-cache',
                });
                res.end(sprite);
              });
            };
          },
        };
      }
      

      Как видите, API создания плагинов не очень сложен. Вам просто нужно найти хуки от Vite или Rollup, которые подойдут под ваши задачи. В моем примере я использую writeBundle из Rollup.js (как я уже сказал, он используется для создания пакета) и configureServer из Vite, потому что Rollup.js этого не делает. У меня нет встроенной поддержки сервера разработки.

      В случае с writeBundle все было очень просто: мы взяли содержимое спрайта SVG и поместили его в файл. В случае с сервером разработки меня смутило, почему я не могу сделать то же самое; Я посмотрел плагины других авторов, и все они делают примерно то же самое.

      Итак, я использую configureServer и через аргумент server добавляю промежуточное программное обеспечение, которое запускает каждый запрос к серверу разработки, перехватывая запрос sprite.svg. .

      Использование хуков Vite

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

      https://vitejs.dev/guide/api-plugin#universal-hooks

      https://vitejs.dev/guide/api-plugin#vite-specific-hooks

      Как назвать плагин

      Что касается именования, в Vite существуют некоторые соглашения для плагинов, поэтому вам лучше ознакомиться с ними, прежде чем закончить. Вот несколько ключевых моментов:

      * Плагины Vite должны иметь уникальное имя с префиксом vite-plugin-;

      * Включите ключевое слово vite-plugin в package.json;

      * Включите в документацию плагина раздел, объясняющий, почему это плагин только для Vite (например, он использует специальные плагины для Vite);

      * Если ваш плагин будет работать только с определенной платформой, включите его имя в префикс (vite-plugin-vue-, vite-plugin-react-, < code>vite-plugin-svelte-).

      Как опубликовать и поделиться своим плагином

      Если вы решите опубликовать свой плагин в NPM, я рекомендую это, поскольку обмен знаниями и опытом является фундаментальным принципом ИТ-сообщества, способствующим коллективному росту. Чтобы узнать, как публиковать и поддерживать свой пакет, ознакомьтесь с моим руководством → Самый простой способ. для создания пакета NPM.

      Я также настоятельно рекомендую отправить ваш плагин в список сообщества Vite — awesome-vite. Многие люди ищут там наиболее подходящие плагины, и это была бы прекрасная возможность внести свой вклад в экосистему Vite! Процесс отправки вашего плагина прост — просто убедитесь, что вы соответствуете условиям, и создайте запрос на включение. Список терминов можно найти здесь.

      В целом, он должен быть специфичным для Vite (не накопительным), иметь открытый исходный код и хорошую документацию. Удачи вам с вашими плагинами!


      Оригинал
PREVIOUS ARTICLE
NEXT ARTICLE