Всем привет, меня зовут Делайк, и сегодня я принесу вам вайт-анализ.
Наконец, предоставляется фоновый проект с использованием vite+react+concent.
слова, написанные впереди
vite, известный как следующее поколение инструментов разработки и построения интерфейса. Появление vite извлекает выгоду из поддержки браузером модулей, используя новые функции браузера для достижения чрезвычайно быстрой разработки; он может обеспечить чрезвычайно быструю горячую перезагрузку (hmr).
В режиме разработки поддержка модулей браузера используется для достижения максимальной эффективности разработки;
Компиляция и упаковка формальной среды построены с использованием свертки, которая впервые предложила встряхивание дерева;
Vite предоставляет множество параметров конфигурации, включая настройку самого vite, настройку esbuild, настройку накопительного пакета и т. д. Сегодня я познакомлю вас с vite с точки зрения исходного кода.
Фактически Vite можно разделить на три части: одна часть — это клиентская часть в процессе разработки, одна часть — это серверная часть в процессе разработки, а другая часть — это часть упаковки и компиляции, связанная с производством, поскольку упаковка и компиляция vite на самом деле используется для свертки, мы не делаем анализ, просто посмотрим на первые две части.
vite-client
Клиент Vite фактически обрабатывается как отдельный модуль, а его исходный код помещается вpackages/vite/src/client
; в нем четыре файла:
- client.ts: основная запись файла, выделенная ниже;
- env.ts: конфигурация, связанная с окружением, здесь мы обработаем конфигурацию определения в vite.config.js (файл конфигурации vite);
- overlay.ts: это отображение маски ошибок, которая будет отображать нашу информацию об ошибках;
- tsconfig.json: это файл конфигурации ts.
Раздел инструментов
Клиент предоставляет ряд инструментальных функций, в основном для HMR;
часть веб-сокета
- Установить соединение через веб-сокет
- Вызов вышеуказанного наложения для отображения ошибок
- Обмен сообщениями
Часть обмена сообщениями имеет несколько типов событий, как показано на следующем рисунке:
Например
Создал простую демонстрацию с помощью vite-app:
yarn create @vitejs/app my-react-ts-app --template react-ts
Используя приведенную выше команду, вы можете просто создать приложение react-ts vite.
npm install
npm run dev
Выполните приведенную выше команду, чтобы установить зависимости, затем запустите службу и откройте браузер:http://localhost:3000/, сетевой интерфейс, вы можете увидеть следующие запросы:
Я разделил эти типы данных на разные подразделения в соответствии с разными типами:
Давайте проанализируем содержимое html дальше:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="module" src="/@vite/client"></script>
<script type="module">
import RefreshRuntime from "/@react-refresh"
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite App</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
Как видите, задействованы три части js:
- клиент, путь запроса /@vite/client, обратите внимание на этот путь, который является зависимым путем самого vite;
- Код модуля react-refresh, это код, инжектируемый плагином react-refresh, @react-refresh запрашивается внутри кода, который является запросом sdk плагина react-refresh;
- main, путь запроса — /src/main/tsc, что связано с реальным кодом в нашем проекте;
В дополнение к вышеперечисленным трем есть еще env, путь запроса — /@vite/env.js, это запрос зависимостей env, выдаваемый @vite/client:import '/node_modules/vite/dist/client/env.js';
;
Конечно, есть также запрос sdk @react-refresh;
В дополнение к js, упомянутому выше, другие запросы на самом деле являются запросами в коде нашего проекта;
Первое, что должен сделать клиент, — это установить канал связи с веб-сокетом.Вы можете увидеть запрос localhost типа веб-сокета выше.Это конвейер для связи клиента с сервером и выполнения горячих обновлений.
vite- server
Поговорив о клиенте, возвращаемся к серверной части, файл входаpackages/vite/src/node/serve.ts
, основная логика на самом деле вpackages/vite/src/node/server/index.ts
;Серверную сторону мы временно называем нодовой, нодовая сторона в основном включает в себя обработку нескольких типов файлов, ведь это всего лишь прокси-сервер;
Давайте рассмотрим эти виды обработки в нескольких частях.
node watcher
Основная функция наблюдателя — отслеживать изменения файлов, а затем общаться с клиентом:
Каталог прослушивания — это корневой каталог всего проекта, а watchOptions — этоvite.config.js
Внутри конфигурации server.watch код инициализации выглядит следующим образом:
// 使用chokidar进行对文件目录的监听,
const watcher = chokidar.watch(path.resolve(root), {
ignored: ['**/node_modules/**', '**/.git/**', ...ignored],
ignoreInitial: true,
ignorePermissionErrors: true,
...watchOptions
}) as FSWatcher
Начните слушать файл:
// 如果发生改变,调用handleHMRUpdate,
watcher.on('change', async (file) => {
file = normalizePath(file)
// invalidate module graph cache on file change
moduleGraph.onFileChange(file)
if (serverConfig.hmr !== false) {
try {
await handleHMRUpdate(file, server)
} catch (err) {
ws.send({
type: 'error',
err: prepareError(err)
})
}
}
})
// 增加文件连接
watcher.on('add', (file) => {
handleFileAddUnlink(normalizePath(file), server)
})
// 减少文件连接
watcher.on('unlink', (file) => {
handleFileAddUnlink(normalizePath(file), server, true)
})
Обработчик, соответствующий событию прослушивания, находится вpackages/vite/src/node/server/hmr.ts
внутри файла. Детали обработки описывать не будем, на самом деле логика внутри почти такая же, в конце вызывается вебсокет и отправляется на сторону клиента.
тип зависимости узла
Тип зависимости на самом деле является пакетом зависимостей в node_modules, например:
Эти пакеты в основном неизменны.Подход Vite заключается в том, чтобы поместить эти зависимости в запуск службы..vite
Ниже каталога полученный запрос направляется непосредственно в .vite для его получения, а затем возвращается.
статические ресурсы узла
Статические ресурсы на самом деле представляют собой контент в разделе public/ или в разделе static/, который нам известен и с которым мы знакомы.Эти ресурсы относятся к статическим файлам, например:
Для таких данных vite не выполняет никакой обработки и возвращает напрямую.
node html
для входного файлаindex.html
, здесь мы будем говорить только о файлах с одной записью, также поддерживаются файлы с несколькими записями, смотрите подробностимногостраничное приложение;
// 删减后得代码如下
// @file packages/vite/src/node/server/middlewares/indexHtml.ts
export function indexHtmlMiddleware(server){
return async (req, res, next) => {
const url = req.url && cleanUrl(req.url)
const filename = getHtmlFilename(url, server)
try {
// 从本地读取index.html的内容
let html = fs.readFileSync(filename, 'utf-8')
// dev模式下调用createDevHtmlTransformFn转换html的内容,插入两个script
html = await server.transformIndexHtml(url, html)
// 把html的内容返回。
return send(req, res, html, 'html')
} catch (e) {
return next(e)
}
}
}
Для входного файла index.html vite сначала прочитает содержимое файла с жесткого диска. После серии операций он вернет содержимое операций. Давайте посмотрим на эту серию операций:
- Вызовите createDevHtmlTransformFn, чтобы получить обработчик:
// @file packages/vite/src/node/plugins/html.ts
export function resolveHtmlTransforms(plugins: readonly Plugin[]) {
const preHooks: IndexHtmlTransformHook[] = []
const postHooks: IndexHtmlTransformHook[] = []
for (const plugin of plugins) {
const hook = plugin.transformIndexHtml
if (hook) {
if (typeof hook === 'function') {
postHooks.push(hook)
} else if (hook.enforce === 'pre') {
preHooks.push(hook.transform)
} else {
postHooks.push(hook.transform)
}
}
}
return [preHooks, postHooks]
}
// @file packages/vite/src/node/server/middlewares/indexHtml.ts
export function createDevHtmlTransformFn(server: ViteDevServer) {
const [preHooks, postHooks] = resolveHtmlTransforms(server.config.plugins)
return (url: string, html: string): Promise<string> => {
return applyHtmlTransforms(
html,
url,
getHtmlFilename(url, server),
[...preHooks, devHtmlHook, ...postHooks],
server
)
}
}
Здесь мы по-прежнему берем проект реакции в качестве примера,Плагин react-refresh вставляется в postHooks; В итоге он фактически возвращает функцию безымянного типа промиса, вот и замыкание. Безымянная функция называетсяapplyHtmlTransforms
, посмотрим на параметры:
- html — это содержимое index.html в корневом каталоге.
- URL-адрес /index.html,
- Результатом выполнения третьего параметра является /index.html.
- Четвертый параметр — большой массив, prehooks пустой, второй — функция возврата собственной ссылки vite /@vite/client, а третий — плагин react-refresh в нем
- Пятый параметр - текущий сервер
Далее идет время вызова applyHtmlTransforms, где html-контент будет перезаписан, а затем возвращен.
Окончательный обработанный html-контент — это html-контент, который мы видели выше.
узлы других типов
Временно считать другие типы как другие типы, включая /@vite/client и бизнес-запросы, начинающиеся с @vite; все эти запросы будут направляться в один и тот жеtransformMiddleware
промежуточное ПО. Это промежуточное ПО делает следующее:
// @file packages/vite/src/node/server/middlewares/transform.ts
На самом деле, если вышеприведенная логика работает нормально, она достигнет кэш-попадания и кэш-промаха. Выберите один из двух, и попадание вернется напрямую. Если нет попадания, оно перейдет к преобразованию. Далее, давайте посмотрим на вызов.transform
процесс:
// @file packages/vite/src/node/server/transformRequest.ts
// 调用插件获取当前请求的id,如/@react-refresh,当然也有获取不到的情况;
const id = (await pluginContainer.resolveId(url))?.id || url
// 调用插件获取插件返回的内容,如/@react-refresh,肯定有不是插件返回的情况,
const loadResult = await pluginContainer.load(id, ssr)
// 接下来是重点
// 如果没有获取到结果,也就是不是插件类型的请求,如我们的入口文件/src/main.tsx
if (loadResult == null) {
// 从硬盘读取非插件提供的返回结果
code = await fs.readFile(file, 'utf-8')
} else {
if (typeof loadResult === 'object') {
code = loadResult.code
map = loadResult.map
} else {
code = loadResult
}
}
}
// 启动文件监听,调用watcher,和上面讲到的watcher遥相呼应
ensureWatchedFile(watcher, mod.file, root)
// 代码运行到这里,是获取到内容了不假,不过code还是源文件,也就是编写的文件内容
// 下面的transform是开始进行替换
const transformResult = await pluginContainer.transform(code, id, map, ssr)
code = transformResult.code!
map = transformResult.map
return (mod.transformResult = {
code,
map,
etag: getEtag(code, { weak: true })
} as TransformResult)
Общий процесс выглядит следующим образом:
async transform(code, id, inMap, ssr) {
const ctx = new TransformContext(id, code, inMap as SourceMap)
ctx.ssr = !!ssr
for (const plugin of plugins) {
if (!plugin.transform) continue
ctx._activePlugin = plugin
ctx._activeId = id
ctx._activeCode = code
let result
try {
result = await plugin.transform.call(ctx as any, code, id, ssr)
} catch (e) {
ctx.error(e)
}
if (!result) continue
if (typeof result === 'object') {
code = result.code || ''
if (result.map) ctx.sourcemapChain.push(result.map)
} else {
code = result
}
}
return {
code,
map: ctx._getCombinedSourcemap()
}
},
Фактически, на данный момент мы в основном поняли функции, реализуемые сервером vite, прокси-сервером, а затем модифицируем ссылку на свои собственные правила, а также анализируем и обрабатываем свои собственные правила. Что еще более важно, это vite:import-analysis
этот плагин.
vite + react
Прикрепите адрес, прежде чем мы начнем:github: vite-реагировать-concent-про; этот предмет был создан пользователемgithub: веб-пакет-реакция-концентрация-проПосле изменения проекта модуль кода бизнес-логики не изменился, изменилась только часть компиляции и упаковки.
Здесь я расскажу о процессе перехода с webpack на vite и некоторых возникших проблемах.
Изменения в проекте на самом деле не большие.По сути, после клонирования проекта удалите зависимости, связанные с веб-пакетом, и замените их на vite.Не забудьте добавить плагин для реакции vite:@vitejs/plugin-react-refresh; После замены, поскольку ссылочный путь в нашем проекте находится в папке src, нам необходимо указать следующий псевдоним для vite:
resolve: {
alias: { // 别名
"configs": path.resolve(__dirname, 'src/configs'),
"components": path.resolve(__dirname, 'src/components'),
"services": path.resolve(__dirname, 'src/services'),
"pages": path.resolve(__dirname, 'src/pages'),
"types": path.resolve(__dirname, 'src/types'),
"utils": path.resolve(__dirname, 'src/utils'),
},
},
Таким образом, мы можем сообщить vite, где найти какой файл, не меняя ссылки в нем.
цитируется вprocess.env.***
Подобные ссылки, которые используются для оценки некоторой логики, связанной с окружением, недоступны в vite.Переменные окружения vite передаются черезimport.meta.env.***
;
После изменения этих исполненийnpm run start
, может работать нормально.
яма 1
в исполненииnpm run build
После этого при предварительном просмотре выполняемnpm run preview
, появится следующий экран:
Возникает такая невидимая ошибка, и каково наше решение?
Прежде всего, убейте сжатие, не сжимайте его, сжатый код весь abcd, и вы ничего не видите; способ его убить — изменить конфигурацию vite:
build: {
minify: false, // 是否进行压缩,boolean | 'terser' | 'esbuild',默认使用terser
manifest: false, // 是否产出maifest.json
sourcemap: false, // 是否产出soucemap.json
outDir: 'build', // 产出目录
},
Мы изменили minify на false, а затем повторно выполнили команды сборки и предварительного просмотра.Мы можем видеть точную строку и место, где было сообщено об ошибке.
Как в итоге разрешилось? TMD оказывается библиотекой для проверки объектов, которая ссылается на пакет util, а затем в наших node_modules нет пакета util.
Больше об этих причинах говорить не буду.После двух-трёх часов метаний решение - это команда:npm i -S util
.
После повторного выполнения сборки и предварительного просмотра это нормально.
яма 2
Локальная разработка запущена, сборка + предварительный просмотр в порядке. Далее мы должны попробовать модульный тест. воплощать в жизньnpm run test
.果不其然,报错了,原因是没有babel-preset-react-app的babel配置。
Тогда мы можем увеличить конфигурацию, разве это не хорошо?
Мы добавили конфигурацию babel в package.json:
"bable": {
"presets": [
"react-app"
],
}
Затем мы бежимnpm run test
; Ну ладно, прогон прошел успешно.
Давайте снова протестируем и выполнимnpm run start
, TMD бросай трубку, беги! ! !
**
Using babel-preset-react-app
requires that you specify NODE_ENV
or BABEL_ENV
environment variables. Valid values are "development", "test", and "production". Instead, received: undefined
**
Что означает приведенная выше фраза? нашbabel-preset-react-app
Этот пакет требуетprocess.env.NODE_ENV
илиprocess.env.BABEL_ENV
Переменные.
Основываясь на том принципе, что vite ничего не делает в процессе, эту проблему нельзя решить, то есть конфигурацию babel нельзя реализовать конфигурацией, так как же это исправить? ?
проверилbabel-preset-react-app
Исходный код этого пакета передается в виде параметров, поэтому мы должны начать с того, что мы делали во время теста.Во время теста мы запускаем jest, и у jest есть свой конфигурационный файл, называемыйjest.config.js
; В конфигурационном файле jest есть объект преобразования, которыйbabel-jest
Эта библиотека, это Babel.
Здесь нам нужно что-то сделать, и, наконец, после долгой отладки конфигурация выглядит так:
// vite react项目里面单测需要在这里把babel-react-app传递进去,不可在项目中或者package.json里面配置babel
transform: {
// vite react项目里面单测需要在这里把babel-react-app传递进去,不可在项目中或者package.json里面配置babel
"^.+\\.(js|jsx|ts|tsx)$": ["<rootDir>/node_modules/babel-jest", {"presets": ['babel-preset-react-app'] }],
"^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
"^(?!.*\\.(js|jsx|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js",
},
Это самая большая яма, и мне потребовалось 2 часа, чтобы бросить ее.
напиши в конце
Вышла 2 версия vite, и ее можно использовать во внутренних проектах компании.Поскольку онлайн и офлайн запуск это не набор кодов, Boss You так же специально предусмотрел функцию предпросмотра, советую попробовать.
Кроме того, упомянутый выше проект:github: vite-реагировать-concent-про, функции, включенные в настоящее время, также относительно полны:
- start: начать разработку и отладку локально
- build: скомпилировать и упаковать
- Предварительный просмотр: Предварительный просмотр упакованного кода:
- тест: одиночный тест
- snap: создать снимок
В проект интегрированы react, concent (особенно полезная библиотека управления состоянием), antd, react-router-dom, axios и т. д., и его можно разрабатывать бесплатно.
Конечно, если ваш существующий проект хочет перейти на vite, это тоже очень просто:
- Клонируйте проект и удалите содержимое ниже src;
- Переместите файл под src вашего старого проекта в файл src этого проекта, а затем измените псевдоним и process.env;
- Не забудьте изменить index.html на ваш файл ввода
Подождем, чтобы увидеть чудо
После использования вите,npm run start может быть улучшен примерно на 80%;Сборка npm run может улучшиться примерно на 50%
! Ммм, как вкусно пахнет~
Наконец, если вы считаете, что текст неплох, не забудьте поставить лайк, добро пожаловать, чтобы следовать за мной.github:draven