Эта статья участвовала в приказе о созыве Haowen, нажмите, чтобы просмотреть:Двойные заявки на внутреннюю и внешнюю стороны, призовой фонд в 20 000 юаней ждет вас, чтобы бросить вызов!
Это 105-я оригинальная статья без воды.Если вы хотите получить больше оригинальных статей, выполните поиск в официальном аккаунте и подпишитесь на нас~ Эта статья была впервые опубликована в блоге Zhengcaiyun:Возможности Vite и частичный анализ исходного кода
Особенности Вайт
Главной особенностью Vite является Bundleless. Встроенная поддержка браузераФункционал модуля JavaScript, модуль JavaScript зависит отimport
а такжеexport
Функции текущих основных браузеров в основном поддерживаются;
Если вы хотите увидеть конкретные поддерживаемые версии, вы можетекликните сюда;
Так в чем преимущество?
Удаление шага упаковки
Упаковка заключается в том, что разработчики используют инструменты упаковки для сборки различных модулей приложения вместе, чтобы сформировать пакет, и считывают код модулей с определенными правилами, чтобы их можно было использовать в браузерах, которые не поддерживают модуляризацию, и могут уменьшить количество http-запросов. Но на самом деле упаковка в процессе локальной разработки усложняет нам устранение проблем и увеличивает время отклика.Vite убирает этап упаковки из команды локальной разработки, тем самым сокращая время сборки.
нагрузка по требованию
Чтобы уменьшить размер пакета, обычно требуется загрузка по запросу. Существует два основных способа:
- Использовать динамический импорт
import()
Способ загрузки модулей асинхронно, импортированные модули по-прежнему должны быть скомпилированы и упакованы заранее; - Используйте встряхивание дерева и другие методы, чтобы максимально удалить модули, на которые не ссылаются;
Способ вите более прямым, он только загружает его, когда модуль является импортом, реализуя реальную нагрузку на потребность, уменьшая объем файла нагрузки, сокращая время;
Основной процесс среды разработки Vite
На следующем рисунке показан основной поток загрузки файлов Vite во время работы среды разработки.
Частичный анализ исходного кода Vite
Общая структура каталогов
|-CHANGELOG.md
|-LICENSE.md
|-README.md
|-bin
| |-openChrome.applescript
| |-vite.js
|-client.d.ts
|-package.json
|-rollup.config.js #打包配置文件
|-scripts
| |-patchTypes.js
|-src
| |-client #客户端
| | |-client.ts
| | |-env.ts
| | |-overlay.ts
| | |-tsconfig.json
| |-node #服务端
| | |-build.ts
| | |-cli.ts #命令入口文件
| | |-config.ts
| | |-constants.ts #常量
| | |-importGlob.ts
| | |-index.ts
| | |-logger.ts
| | |-optimizer
| | | |-esbuildDepPlugin.ts
| | | |-index.ts
| | | |-registerMissing.ts
| | | |-scan.ts
| | |-plugin.ts #rollup 插件
| | |-plugins #插件相关文件
| | | |-asset.ts
| | | |-clientInjections.ts
| | | |-css.ts
| | | |-esbuild.ts
| | | |-html.ts
| | | |-index.ts
| | | |-...
| | |-preview.ts
| | |-server
| | | |-hmr.ts #热更新
| | | |-http.ts
| | | |-index.ts
| | | |-middlewares #中间件
| | | | |-...
| | | |-moduleGraph.ts #模块间关系组装(树形)
| | | |-openBrowser.ts #打开浏览器
| | | |-pluginContainer.ts
| | | |-send.ts
| | | |-sourcemap.ts
| | | |-transformRequest.ts
| | | |-ws.ts
| | |-ssr
| | | |-__tests__
| | | | |-ssrTransform.spec.ts
| | | |-ssrExternal.ts
| | | |-ssrManifestPlugin.ts
| | | |-ssrModuleLoader.ts
| | | |-ssrStacktrace.ts
| | | |-ssrTransform.ts
| | |-tsconfig.json
| | |-utils.ts
|-tsconfig.base.json
|-types
| |-...
метод ядра сервера
Из входного файла cli.ts видно, что три команды соответствуют трем основным файлам и методам;
- команда dev.
Путь к файлу: ./server/index.ts;
Основной метод: createServer;
Основные функции: локальные команды разработки проекта, запуск службы на основе httpServer, Vite получает содержимое ресурса путем перехвата пути запроса и возвращает его браузеру, а сервер перезаписывает путь к файлу. Например:
Исходный код проекта выглядит следующим образом:
import { createApp } from 'vue';
import App from './index.vue';
После перезаписи сервером пути кода трехсторонних пакетов в папке node_modules также будут полностью склеены вместе.
import __vite__cjsImport0_vue from "/node_modules/.vite/vue.js?v=ed69bae0";
const createApp = __vite__cjsImport0_vue["createApp"];
import App from '/src/pages/back-sky/index.vue';
2.построить команду Путь к файлу: ./build.ts;
Основной метод: построить;
Основная функция: упаковать и скомпилировать с накопительным пакетом
3. оптимизировать команду
Путь к файлу: ./optimizer/index.ts;
Основной метод: optimiseDeps;
Основная функция: в основном для сторонних пакетов Vite будет использовать накопительный пакет для перекомпиляции стороннего пакета при выполнении runOptimize и поместить скомпилированный новый пакет, соответствующий спецификации модуля esm, в , Импорт пакета обрабатывается: содержимое скомпилированного пакета используется для замены содержимого исходного пакета, что решает проблему невозможности использования пакета cjs в Vite.
Ниже приведен файл _metadata.json в папке .vite, который создается в процессе предварительной компиляции и содержит список всех предварительно скомпилированных файлов и их путей. Например:
{
"hash": "31d458ff",
"browserHash": "ed69bae0",
"optimized": {
"element-plus/lib/utils/dom": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus_lib_utils_dom.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/utils/dom.js",
"needsInterop": true
},
"element-plus": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/element-plus.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/index.esm.js",
"needsInterop": false
},
"vue": {
"file": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js",
"src": "/Users/zcy/Documents/workspace/back-sky-front/node_modules/vue/dist/vue.runtime.esm-bundler.js",
"needsInterop": true
},
......
}
}
}
Разрешение модуля
готовыйИспользуется для повышения скорости перезагрузки страницы, конвертирует CommonJS, UMD и т. д. в формат ESM. Предварительно создайте этот шагesbuildВыполнение, что делает время холодного запуска Vite намного быстрее, чем у любого сборщика на основе JavaScript.
Почему ESbuild быстрее?
- Использовать язык Go
- Тяжелый параллелизм с использованием ЦП
- Эффективное использование памяти
- Написано на языке Scratch, что позволяет сократить использование сторонних библиотек и избежать неконтролируемой производительности.
Перепишите импорт на действительный URL-адрес, например./node_modules/.vite/my-dep.js?v=f3sf2ebd
чтобы браузер мог их корректно импортировать
Горячее обновление
Основной процесс горячего обновления выглядит следующим образом:
- Сервер отслеживает изменения файлов на основе наблюдателя, определяет метод обновления на основе типа и компилирует ресурсы.
- Клиент прослушивает некоторые обновленные типы сообщений через WebSocket.
- Клиент получает информацию о ресурсах, логика обновления выполняет тепло в соответствии с типом сообщения
Ниже приведена часть логики оценки в ядре hmr.ts горячего обновления сервера.
Если файл конфигурации или файл среды изменен, это приведет к перезапуску службы, прежде чем конфигурация вступит в силу.
if (file === config.configFile || file.endsWith('.env')) {
// auto restart server 配置&环境文件修改则自动重启服务
debugHmr(`[config change] ${chalk.dim(shortFile)}`)
config.logger.info(
chalk.green('config or .env file changed, restarting server...'),
{ clear: true, timestamp: true }
)
await restartServer(server)
return
}
При обновлении html-файла страница перезагружается.
if (file.endsWith('.html')) { // html 文件更新
config.logger.info(chalk.green(`page reload `) + chalk.dim(shortFile), {
clear: true,
timestamp: true
})
ws.send({
type: 'full-reload',
path: config.server.middlewareMode
? '*'
: '/' + normalizePath(path.relative(config.root, file))
})
} else {
// loaded but not in the module graph, probably not js
debugHmr(`[no modules matched] ${chalk.dim(shortFile)}`)
}
Когда Vue и другие файлы будут обновлены, они войдутupdateModules
метод, при нормальных обстоятельствах запускается только обновление, чтобы реализовать горячее обновление и горячую замену;
function updateModules(
file: string,
modules: ModuleNode[],
timestamp: number,
{ config, ws }: ViteDevServer
) {
const updates: Update[] = []
const invalidatedModules = new Set<ModuleNode>()
// 遍历插件数组,关联下面的片段
for (const mod of modules) {
const boundaries = new Set<{
boundary: ModuleNode
acceptedVia: ModuleNode
}>()
// 设置时间戳
invalidate(mod, timestamp, invalidatedModules)
// 查找引用模块,判断是否需要重载页面
const hasDeadEnd = propagateUpdate(mod, timestamp, boundaries)
// 找不到引用者则会发起刷新
if (hasDeadEnd) {
config.logger.info(chalk.green(`page reload `) + chalk.dim(file), {
clear: true,
timestamp: true
})
ws.send({
type: 'full-reload'
})
return
}
updates.push(
...[...boundaries].map(({ boundary, acceptedVia }) => ({
type: `${boundary.type}-update` as Update['type'],
timestamp,
path: boundary.url,
acceptedPath: acceptedVia.url
}))
)
}
// 日志输出
config.logger.info(
updates
.map(({ path }) => chalk.green(`hmr update `) + chalk.dim(path))
.join('\n'),
{ clear: true, timestamp: true }
)
// 向客户端发送消息,进行热更新操作
ws.send({
type: 'update',
updates
})
}
Модули в приведенном выше коде представляют собой различные плагины, которые необходимо запускать во время горячего обновления.
for (const plugin of config.plugins) {
if (plugin.handleHotUpdate) {
const filteredModules = await plugin.handleHotUpdate(hmrContext)
if (filteredModules) {
hmrContext.modules = filteredModules
}
}
}
Vite объединит зависимости модулей в модульGraph, который по структуре похож на дерево.При горячем обновлении, какие файлы необходимо обновить, также будет зависеть от moduleGraph; его содержимое файла примерно следующее:
// moduleGraph 返回的 ModuleNode 大致结构
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js',
file: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.js',
importers: Set {},
importedModules: Set {
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js?v=32cfd30c',
file: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/.vite/vue.js',
......
lastHMRTimestamp: 0,
url: '/node_modules/.vite/vue.js?v=32cfd30c',
type: 'js'
},
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue',
file: '/Users/zcy/Documents/workspace/back-sky-front/src/pages/back-sky/index.vue',
......
url: '/src/pages/back-sky/index.vue',
type: 'js'
},
ModuleNode {
id: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css',
file: '/Users/zcy/Documents/workspace/back-sky-front/node_modules/element-plus/lib/theme-chalk/index.css',
importers: [Set],
importedModules: Set {},
acceptedHmrDeps: Set {},
isSelfAccepting: true,
transformResult: [Object],
ssrTransformResult: null,
ssrModule: null,
lastHMRTimestamp: 0,
url: '/node_modules/element-plus/lib/theme-chalk/index.css',
type: 'js'
},
......
},
acceptedHmrDeps: Set {},
isSelfAccepting: false,
transformResult: {
code: 'import __vite__cjsImport0_vue from ' +
'"/node_modules/.vite/vue.js?v=32cfd30c"; const createApp = ' +
'__vite__cjsImport0_vue["createApp"];\nimport App from ' +
"'/src/pages/back-sky/index.vue';\nimport " +
"'/node_modules/element-plus/lib/theme-chalk/index.css';\n\nconst app = " +
'createApp(App);\n\nimport { addHistoryMethod } from ' +
"'/src/pages/back-sky/api/index.js';\nimport {\n ElButton,\n ElDropdown,\n " +
'ElDropdownMenu,\n ElDropdownItem,\n ElMenu,\n ElSubmenu,\n ElMenuItem,\n ' +
'ElMenuItemGroup,\n ElPopover,\n ElDialog,\n ElRow,\n ElInput,\n ' +
"ElLoading,\n} from '/node_modules/.vite/element-plus.js?v=32cfd30c';\n\n" +
'app.use(ElButton);\napp.use(ElLoading);\napp.use(ElDropdown);\n' +
'app.use(ElDropdownMenu);\napp.use(ElDropdownItem);\napp.use(ElMenu);\n' +
'app.use(ElSubmenu);\napp.use(ElMenuItem);\napp.use(ElMenuItemGroup);\n' +
'app.use(ElPopover);\napp.use(ElDialog);\napp.use(ElRow);\napp.use(ElInput);\n' +
"\nconst f = ()=>{\n return app.mount('#app');\n};\n\nconst $backsky = " +
"document.getElementById('back-sky');\nif($backsky) {\n $backsky.innerHTML " +
"= '';\n $backsky.appendChild(f().$el);\n} else {\n window.onload = " +
"function(){\n document.getElementById('back-sky') && " +
"document.getElementById('back-sky').appendChild(f().$el);\n };\n}\n\n" +
"window.addHistoryListener = addHistoryMethod('historychange');\n" +
"history.pushState = addHistoryMethod('pushState');\nhistory.replaceState " +
"= addHistoryMethod('replaceState');\n\n// 监听hash路由变化,不与onhashchange互相覆盖\n" +
'addHashChange(()=>{\n setTimeout(() => {\n const $backsky = ' +
"document.getElementById('back-sky');\n if($backsky && " +
"$backsky.innerHTML === '') {\n $backsky.appendChild(f().$el);\n }\n " +
" },0);\n});\n\nfunction addHashChange(callback) {\n if('onhashchange' in " +
'window === false){//浏览器不支持\n return false;\n }\n ' +
'if(window.addEventListener) {\n ' +
"window.addEventListener('hashchange',function(e) {\n callback && " +
'callback(e);\n },false);\n }else if(window.attachEvent) {//IE 8 及更早 IE ' +
"版本浏览器\n window.attachEvent('onhashchange',function(e) {\n callback " +
'&& callback(e);\n });\n }\n ' +
"window.addHistoryListener('history',function(e){\n callback && " +
'callback(e);\n });\n}\n\n\n',
map: null,
etag: 'W/"846-Qa424gJKl3YCqHDWXXsM1mFHRqg"'
},
ssrTransformResult: null,
ssrModule: null,
lastHMRTimestamp: 0,
url: '/src/pages/back-sky/index.js',
type: 'js'
}
Переключиться на исходный проект
Наконец, давайте посмотрим, как использовать Vite для упаковки старого проекта Vue;
Сначала нам нужно обновить Vue3
npm install vue@next
И добавьте в проект файл конфигурации vite, создайте vite.config.js в корневом каталоге и добавьте в него некоторую базовую конфигурацию.
// vite.config.js
// vite2.1.5
const path = require('path');
import vue from '@vitejs/plugin-vue';
export default {
// 配置选项
resolve: {
alias: {
'@utils': path.resolve(__dirname, './src/utils')
},
},
plugins: [vue()],
};
Упомянутую стороннюю библиотеку компонентов также может потребоваться обновить, например: обновить element-ui до element-plus.
npm install element-plus
Vue3 необходимо использовать при импортеcreateApp
метод инициализации
import { createApp } from 'vue';
import App from './index.vue';
const app = createApp(App);
import {
ElInput,
ElLoading,
} from 'element-plus';
app.use(ElButton);
app.use(ElLoading);
......
Здесь вы можете запустить проект. Примечание. Vite официально не позволяет опускать суффикс .vue, иначе будет сообщено об ошибке;
[plugin:vite:import-analysis] Failed to resolve import "./todoList" from "src/pages/back-sky/components/header/index.vue". Does the file exist?
/components/header/index.vue:2:23
1 |
2 | import todoList from './todoList';
import todoList from './todoList.vue';
Наконец, давайте сравним время сравнения двух методов строительства проекта;
Холодный запуск Webpack, занимает 7513 мс:
⚠ 「wdm」: Hash: 1ad1dd54289cfad8ecbe
Version: webpack 4.46.0
Time: 7513ms
Built at: 2021-05-24 13:59:35
Холодный запуск того же проекта Vite занимает 924 мс:
> vite
Pre-bundling dependencies:
vue
element-plus
@zcy/zcy-request
element-plus/lib/utils/dom
(this will be run only when your dependencies or config have changed)
vite v2.3.3 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 924ms.
Второй запуск (предварительно скомпилированные зависимости уже существуют) занимает 407 мс;
> vite
vite v2.3.3 dev server running at:
> Local: http://localhost:3000/
> Network: use `--host` to expose
ready in 407ms.
Суммировать
Использование Vite для локального запуска службы и горячего обновления значительно повысит эффективность.Каковы различия в процессе компиляции и упаковки? Как эффект? На какие ямы ты наступал? Сообщи мне.
Рекомендуемое чтение:
What are CJS, AMD, UMD, and ESM in Javascript?
Рекомендуемое чтение
Изучите неизменяемость за 15 минут
работы с открытым исходным кодом
- Zhengcaiyun интерфейсный таблоид
адрес с открытым исходным кодомwww.zoo.team/openweekly/(На главной странице официального сайта таблоида есть группа обмена WeChat)
Карьера
ZooTeam, молодая, увлеченная и творческая команда, связанная с отделом исследований и разработок продукции Zhengcaiyun, базируется в живописном Ханчжоу. В настоящее время в команде более 40 фронтенд-партнеров, средний возраст которых составляет 27 лет, и почти 30% из них — инженеры полного стека, настоящая молодежная штурмовая группа. В состав членов входят «ветераны» солдат из Ali и NetEase, а также первокурсники из Чжэцзянского университета, Университета науки и технологий Китая, Университета Хандянь и других школ. В дополнение к ежедневным деловым связям, команда также проводит технические исследования и фактические боевые действия в области системы материалов, инженерной платформы, строительной платформы, производительности, облачных приложений, анализа и визуализации данных, а также продвигает и внедряет ряд внутренних технологий. Откройте для себя новые горизонты передовых технологических систем.
Если вы хотите измениться, вас забрасывают вещами, и вы надеетесь начать их бросать; если вы хотите измениться, вам сказали, что вам нужно больше идей, но вы не можете сломать игру; если вы хотите изменить , у вас есть возможность добиться этого результата, но вы не нужны; если вы хотите изменить то, чего хотите достичь, вам нужна команда для поддержки, но вам некуда вести людей; если вы хотите изменить установившийся ритм, это будет "5 лет рабочего времени и 3 года стажа работы"; если вы хотите изменить исходный Понимание хорошее, но всегда есть размытие того слоя оконной бумаги.. , Если вы верите в силу веры, верьте, что обычные люди могут достичь необыкновенных вещей, и верьте, что они могут встретить лучшего себя. Если вы хотите участвовать в процессе становления бизнеса и лично способствовать росту фронтенд-команды с глубоким пониманием бизнеса, надежной технической системой, технологиями, создающими ценность, и побочным влиянием, я думаю, что мы должны говорить. В любое время, ожидая, пока вы что-нибудь напишете, отправьте это наZooTeam@cai-inc.com