Предыдущее состояние
Создание приложения Create-react-app занимает около 3 минут и требует около 3 ГБ оперативной памяти.
Почему Vite
- Нам нужна была быстрая миграция без трения (поэтому выбор фреймворка вроде Next не входит в наши планы);
- Мы хотели избежать низкоуровневых инструментов. Мы хотим получить что-то хорошо поддерживаемое с хорошей предустановкой из коробки;
- Похоже, что Vite достиг этих целей, другие подобные инструменты тоже могут быть достигнуты;
Настройки миграции
Это может немного измениться в зависимости от того, какие вещи есть в вашем проекте. Вот что было у нас:
Начальная настройка
- В документации Vite нет статьи о «миграции из существующего проекта», поэтому я запустил стартовый проект и скопировал следующие файлы:
vite.config.ts
tsconfig.node.json
- Просмотрите
package.json
и удалите все, что связано с Create React App, Babel или Webpack. Например:react-scripts
- Также замените скрипты package.json соответствующим образом. Например:
"vite": "vite",
"start": "vite",
"build": "vite build",
"preview": "vite preview",
"test": "vitest --run",
"test:watch": "vitest",
- Добавить Vite (
yarn add vite
). Обновите TS до последней версии, поскольку у вас больше нет CRA, блокирующего вас на древней версии;
Плагин React
Одной из первых вещей, которые необходимо добавить, является плагин React в Vite Config. (@vitejs/plugin/react
).
Ниже показана окончательная версия конфига vite:
/// <reference types="vitest" />
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve, parse } from 'path';
import * as fs from 'fs';
import svgr from 'vite-plugin-svgr';
const rootPaths = fs.readdirSync('src').reduce((out, item) => {
const parsed = parse(item);
return { ...out, [parsed.name]: resolve('src', item) };
}, {});
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svgr(), react()],
resolve: {
alias: rootPaths,
},
envPrefix: 'REACT_APP',
test: {
globals: true,
environment: 'happy-dom',
},
});
Сопоставление путей
В CRA папки в корне источника могут быть доступны как абсолютные пути.
- Например,
/src/ListComponent/Somefile.ts
может быть импортирован как import Somefile from 'ListComponent/Somefile'
Эта специальная обработка не существует в Vite. Затем я вручную вшил это сопоставление в настройку resolve.alias
в vite config.
const rootPaths = fs.readdirSync('src').reduce((out, item) => {
const parsed = parse(item);
return { ...out, [parsed.name]: resolve('src', item) };
}, {});
export default defineConfig({
// ..
resolve: {
alias: rootPaths,
},
});
Импорт SVG
Create React App импортирует библиотеку «SVGR». Если вы используете какой-либо импорт, например…
import { ReactComponent as MySvg } from './file.svg'
…то это больше не будет работать.
Исправление, не требующее усилий, заключается в добавлении vite-plugin-svgr
, показанного выше (найдено в ответе на Stack Overflow).
Vite основан на Rollup, и SVGR предоставляет свой собственный плагин Rollup,
@svgr/rollup
. К сожалению, здесь он не работает должным образом, потому что Vite уже встроил плагин URL статических файлов, который имеет более высокий приоритет. Даже если следовать приведенным шагам, чтобы использовать его вместе с плагином URL, ничего хорошего не вышло.
Переменные окружения
Vite не читает переменные окружения из process.env
, а скорее из import.meta.env
; Также переменная NODE_ENV
находится в import.meta.env.mode
, которая устанавливается в зависимости от используемой среды сборки (Vite dev server, Vite build или vitest);
Некоторые дурные переменные окружения, такие как BROWSER=none
или PORT
больше не понадобятся (сервер Vite принимает аргумент --port
, как и 99% других программ в мире).
Безопасный префикс переменной окружения по умолчанию VITE_APP
вместо REACT_APP
. Это можно изменить в настройке envPrefix
(как показано выше), чтобы избежать некоторого рефакторинга.
Определения типов
Если ранее вы писали строго типизированный process.env
, вам может понадобиться переместить эти типы в соответствующие глобальные интерфейсы ImportMetaEnv
и ImportMeta
, как показано в документации по переменным окружения;
Нам также нужно заменить типы инструментов сборки. В react.app-env.d.ts замените:
- /// <reference types="react-scripts" />
+ /// <reference types="vite/client" />
index.html
index.html
теперь находится в корневой папке. Он также требует нового тега script в своем теле, указывающего на корень проекта:
<body>
<div id="root"></div>
<script type="module" src="/src/index.tsx"></script>
</body>
Кроме того, все теги %PUBLIC_URL%
должны быть удалены.
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
Рефакторинг синхронизации require()
На webpack вы все еще можете уйти от написания синхронного CommonJS require()
в любом месте. На Vite это просто не получится (разве что с помощью плагина);
Папка сборки по умолчанию
Папка сборки по умолчанию в Vite — dist
вместо build
. Это можно настроить с помощью build.outDir
.
Тестирование
Самым быстрым способом обойти тестирование, вероятно, является переход на Vitest, так как Jest test runner вроде как полагается на Babel/Webpack;
Мы все еще сохранили Jest в проекте, мы просто больше не используем его тестовый бегунок. Другие части Jest, такие как assertions или mocks, остались.
Vitest читает из того же конфигурационного файла (vite.config.ts
). Чтобы TS не жаловался, нужно добавить директиву его типа:
// on vite.config.ts:
/// <reference types="vitest" />
Как было показано ранее, нам понадобилась пара дополнительных настроек для клавиши «test».
test: {
globals: true,
environment: 'happy-dom',
},
- globals добавляет в контекст мока-подобные глобалы (
describe
,test
и т.д.); - окружение позволяет включить JSDOM или другое;
- Когда вы задаете окружение, CLI предложит вам установить его отдельно.
ESLint
Многие плагины ESLint, которые ранее поставлялись в комплекте с CRA, приходилось устанавливать и добавлять вручную.
- @typescript-eslint/eslint-plugin
- @typescript-eslint/parser
- eslint-plugin-jsx-a11y
- eslint-plugin-react
- eslint-plugin-react-hooks
В итоге мы получили что-то вроде этого в eslint.config
:
{
"root": true,
"parser": "@typescript-eslint/parser",
"plugins": [
"@typescript-eslint",
"jsx-a11y"
],
"extends": [
"plugin:react/recommended",
"plugin:react-hooks/recommended",
"plugin:import/recommended",
"plugin:import/typescript",
],
"settings": {
"react": {
"version": "17.0"
}
}
}
Сборка и разработка
Сервер Vite Dev не включает автоматическую проверку TS. Он предлагает вам запустить tsc
на задаче сборки (tsc && vite build
). В tsconfig уже предлагается noEmit
.
Хотя вы, вероятно, можете добавить tsc
в сборку через плагин, в конечном итоге я думаю, что лучше этого не делать, так как VSCode уже запускает свой собственный TS Server. Запуск tsc
на сервере разработки создает дублирующий TS Server.
Если вы хотите проверить ошибки по всему проекту:
- вы все еще можете запустить
tsc -w
- или использовать задачу VS Code Task: F1 > Run Build Task > tsc — watch
Поскольку проверка типов и сборка теперь являются отдельными задачами, вы можете запускать их параллельно на CI.
Ощущения от работы
Время сборки составило около 25 секунд по сравнению с 3 минутами (могло быть меньше, если бы я не отключил SMT на своем процессоре); Хотя Webpack использует только одно ядро в течение большей части сборки, Vite показывает некоторую среднюю активность на всех ядрах.
Пиковое использование памяти снизилось до 1,2 ГБ, по сравнению с ~3 ГБ.
- Сервер разработки запускается сразу, так как на самом деле ничего не компилировалось. Страницы компилируются по мере их загрузки (аналогично тому, как это происходит на Next.js). Режим разработки может показаться не таким уж быстрым при первой загрузке страницы, поскольку каждая зависимость обслуживается отдельно. Если вы посмотрите на панель запросов, то увидите огромное количество обслуживаемых файлов;
- Тем не менее, это на порядок быстрее, чем 3-минутная сборка всего в Webpacks;
- Компилируются и обслуживаются только файлы, необходимые для конкретной страницы;
- Это также означает, что при выполнении HMR повторно обслуживаются только измененные файлы. HMR ощущается более отзывчивым;
- Это также может означать, что после первой загрузки браузер может использовать кэширование отдельных файлов на своей стороне;
- В режиме производства файлы собираются в пакеты, как это происходит в других традиционных инструментах. Сборки для разработки и производства значительно отличаются друг от друга. Различия объясняются прямо на первой странице документации.