本篇文章给大家谈谈vite,以及vue vite的知识点,希望对各位有所帮助,不要忘了收藏本站喔。
文章详情介绍:
由Vite提供支持的极速单元测试框架Vitest
《开源精选》是我们分享Github、Gitee等开源社区中优质项目的栏目,包括技术、学习、实用与各种有趣的内容。本期推荐的是一个 Vite 原生的单元测试框架——Vitest。
Vitest 旨在将自己定位为 Vite 项目的首选测试框架,即使对于不使用 Vite 的项目也是一个可靠的替代方案。
由于 Jest 的大规模使用,Vitest 提供了与之兼容的API,允许大家在大多数项目中将其作为备选使用。同时还包括了单元测试时最常见的功能(模拟,快照以及覆盖率)。
Vitest 非常注重性能,尽可能多地使用 Worker 线程进行并发运行。并且在一些端口的测试运行速度提高了一个数量级别。监听模式默认启用,与 Vite 推动开发者优先体验的理念保持一致。 即使在开发体验上进行了改进,Vitest 通过仔细挑选其依赖项(或直接内联所需的部分)来保持轻量级。
主要功能
与 Vite 通用的配置、转换器、解析器和插件。
使用你的应用程序中的相同配置来进行测试!
智能文件监听模式,就像是测试的 HMR!
支持测试 Vue、React、Lit 等框架中的组件。
开箱即用的 TypeScript / JSX 支持
ESM 优先,支持模块顶级 await
通过 tinypool 使用 Worker 线程尽可能多地并发运行
套件和测试的过滤、超时、并发配置
Jest 的快照功能
内置 Chai 进行断言 + 与 Jest expect 语法兼容的 API
内置用于对象模拟(Mock)的 Tinyspy
使用 jsdom 或 happy-dom 用于 DOM 模拟
通过 c8 来输出代码测试覆盖率
类似于 Rust 语言的 源码内联测试
快速起步
将 Vitest 安装到项目:
# 使用 npm
npm install -D vitest
# 使用 yarn
yarn add -D vitest
# 使用 pnpm
pnpm add -D vitest
提示:
Vitest 需要 Vite >=v2.7.10 和 Node >=v14
配置 Vitest:
Vitest 的主要优势之一是它与 Vite 的统一配置。如果存在,vitest 将读取你的根目录 vite.config.ts 以匹配插件并设置为你的 Vite 应用程序。例如,你的 Vite 有 resolve.alias 和 plugins 的配置将会在 Vitest 中起作用。如果你想在测试期间想要不同的配置,你可以:
创建 vitest.config.ts,优先级将会最高。
将 –config 选项传递给 CLI,例如 vitest –config ./path/to/vitest.config.ts。
在 defineConfig 上使用 process.env.VITEST 或 mode 属性(如果没有被覆盖,将设置为 test)有条件地在 vite.config.ts 中应用不同的配置。
如果要配置 vitest 本身,请在你的 Vite 配置中添加 test 属性。 你还需要使用 三斜线命令 ,同时如果是从 vite 本身导入 defineConfig,请在配置文件的顶部加上三斜线命令。
import { defineConfig } from 'vitest/config'
export default defineConfig({
test: {
// ...
},
})
命令行:
在安装了 Vitest 的项目中,你可以在 npm 脚本中使用 vitest 脚本,或者直接使用 npx vitest 运行它。 以下是脚手架 Vitest 项目中的默认 npm 脚本:
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
要在不监视文件更改的情况下运行一次测试,请使用 vitest run。 你还可以指定其他 CLI 选项,例如 –port 或 –https。 有关 CLI 选项的完整列表,可以在你的项目中运行 npx vitest –help。
Vitest UI
Vitest 由 Vite 提供能力,在运行测试时有一个开发服务器。这允许 Vitest 提供一个漂亮的 UI 界面来查看并与测试交互。Vitest 的 UI 界面是可选的,你可以通过以下安装:
npm i -D @vitest/ui
接下来,你可以通过传入 –ui 参数来启动测试的 UI 界面:
vitest --ui
最后,你可以访问 Vitest UI 界面,通过
http://localhost:51204/__vitest__/
—END—
开源协议:MIT
开源地址:
https://github.com/vitest-dev/vitest
超棒 Vite4+ElementPlus 中后台管理VueXSAdmin
今天再给大家推荐一款多功能 vue3+ts 开箱即用的后台系统管理模板XSAdmin。
vue-xs-admin 基于 Vue3+Vite4+Element-Plus 等主流技术开发的开箱即用后台模板,内置 I18n 翻译、路由权限控制等方案。
特点
最新技术栈:使用 Vue3/Vite3 等前端前沿技术开发
主题:可配置的主题
国际化:内置完善的国际化方案
常用组件:内置完善的常用组件封装
PWA:内置 PWA
快速构建
# 克隆项目
git clone https://github.com/jsxiaosi/vue-xs-admin.git
# 安装依赖
pnpm i
# 运行
npm run dev
# 打包构建
npm run build
Node 和 Git -项目开发环境
Vite – 熟悉 Vite 特性
Vue3 – 熟悉 Vue 基础语法
Es6+ – 熟悉 Es6 基本语法
Vue-Router-Next – 熟悉 Vue-Router 基本使用
Element-Plus – Ui 基本使用
挺不错的一款vue3后台系统,感兴趣的可以去看看。
// 演示地址
https://www.supercutexiaosi.top/
// 仓库地址
https://github.com/jsxiaosi/vue-xs-admin
OK,今天就分享到这里了。
Vite 开发环境为何这么快?
提到 Vite,第一个想到的字就是 快,到底快在哪里呢?为什么可以这么快? 本文从以下几个地方来讲
快速的冷启动: No Bundle + esbuild 预构建
模块热更新:利用浏览器缓存策略
按需加载:利用浏览器 ESM 支持
Vite 本质上是一个本地资源服务器,还有一套构建指令组成。
本地资源服务器,基于 ESM 提供很多内建功能,HMR 速度很快
使用 Rollup 打包你的代码,预配件了优化的过配置,输出高度优化的静态资源
快递的冷启动No-bundle
在冷启动开发者服务器时,基于 Webpack 这类 bundle based 打包工具,启动时必须要通过 依赖收集、模块解析、生成 chunk、生成模块依赖关系图,最后构建整个应用输出产物,才能提供服务。
这意味着不管代码实际是否用到,都是需要被扫描和解析。
而 Vite 的思路是,利用浏览器原生支持 ESM 的原理,让浏览器来负责打包程序的工作。而 Vite 只需要在浏览器请求源码时进行转换并按需提供源码即可。
这种方式就像我们编写 ES5 代码一样,不需要经过构建工具打包成产物再给浏览器解析,浏览器自己就能够解析。
与现有的打包构建工具 Webpack 等不同,Vite 的开发服务器启动过程仅包括加载配置和中间件,然后立即启动服务器,整个服务启动流程就此结束。
Vite 利用了现代浏览器支持的 ESM 特性,在开发阶段实现了 no-bundle 模式,不生成所有可能用到的产物,而是在遇到 import 语句时发起资源文件请求。
当 Vite 服务器接收到请求时,才对资源进行实时编译并将其转换为 ESM,然后返回给浏览器,从而实现按需加载项目资源。而现有的打包构建工具在启动服务器时需要进行项目代码扫描、依赖收集、模块解析、生成 chunk 等操作,最后才启动服务器并输出生成的打包产物。
正是因为 Vite 采用了 no-bundle 的开发模式,使用 Vite 的项目不会随着项目迭代变得庞大和复杂而导致启动速度变慢,始终能实现毫秒级的启动。
esbuild 预构建
当然这里的毫秒级是有前提的,需要是非首次构建,并且没有安装新的依赖,项目代码中也没有引入新的依赖。
这是因为 Vite 的 Dev 环境会进行预构建优化。 在第一次运行项目之后,直接启动服务,大大提高冷启动速度,只要没有依赖发生变化就会直接出发热更新,速度也能够达到毫秒级。
这里进行预构建主要是因为 Vite 是基于浏览器原生**支持 **ESM 的能力实现的,但要求用户的代码模块必须是ESM模块,因此必须将 commonJS 和 UMD 规范的文件提前处理,转化成 ESM 模块并缓存入 node_modules/.vite。
在转换 commonJS 依赖时,Vite 会进行智能导入分析,即使模块导出时动态分配的,具名导出也能正常工作。
ts复制代码// 符合预期
import React, { useState } from 'react'
另一方面是为了性能优化
为了提高后续页面加载的性能,Vite 将那些具有许多内部模块的 ESM 依赖转为单个模块。
比如我们常用的 lodash 工具库,里面有很多包通过单独的文件相互导入,而 lodash-es这种 ESM 包会有几百个子模块,当代码中出现 import { debounce } from ‘lodash-es’ 会发出几百个 HTTP 请求,这些请求会造成网络堵塞,影响页面的加载。
通过将 lodash-es 预构建成一个单独模块,只需要一个 HTTP 请求。
那么如果是首次构建呢?Vite 还能这么快吗?
在首次运行项目时,Vite 会对代码进行扫描,对使用到的依赖进行预构建,但是如果使用 rollup、webpack 进行构建同样会拖累项目构建速度,而 Vite 选择了 esbuild 进行构建。
btw,预构建只会在开发环境生效,并使用 esbuild 进行 esm 转换,在生产环境仍然会使用 rollup 进行打包。
生产环境使用 rollup 主要是为了更好的兼容性和 tree-shaking 以及代码压缩优化等,以减小代码包体积
为什么选择 esbuild?
esbuild 的构建速度非常快,比 Webpack 快非常多,esbuild 是用 Go 编写的,语言层面的压制,运行性能更好
核心原因就是 esbuild 足够快,可以在 esbuild 官网看到这个对比图,基本上是 上百倍的差距。
前端的打包工具大多数是基于 JavaScript 实现的,由于语言特性 JavaScript 边运行边解释,而 esbuild 使用 Go 语言开发,直接编译成机器语言,启动时直接运行即可。
更多关于 Go 和 JavaScript 的语言特性差异,可以检索一下。
不久前,字节开源了 Rspack 构建工具,它是基于 Rust 编写的,同样构建速度很快
Rust 编译生成的 Native Code 通常比 JavaScript 性能更为高效,也意味着 rspack 在打包和构建中会有更高的性能。
同时 Rust 支持多线程,意味着可以充分利用多核 CPU 的性能进行编译。而 Webpack 受限于 JavaScript 对多线程支持较弱,导致很难进行并行计算。
不过,Rspack 的插件系统还不完善,同时由于插件支持 JS 和 rust 编写,如果采用 JS 编写估计会损失部分性能,而使用 rust 开发,对于开发者可能需要一定的上手成本
同时发现 Vite 4 已经开始增加对 SWC 的支持,这是一个基于 Rust 的打包器,可以替代 Babel,以获取更高的编译性能。
**Rust 会是 JavaScript 基建的未来吗?**推荐阅读:
zhuanlan.zhihu.com/p/433300816
模块热更新
主要是通过 WebSocket 创建浏览器和服务器的通信监听文件的改变,当文件被修改时,服务端发送消息通知客户端修改相应的代码,客户端对应不同的文件进行不同的操作的更新。
Webpack 和 Vite 在热更新上有什么不同呢?
Webpack: 重新编译,请求变更后模块的代码,客户端重新加载
Vite 通过监听文件系统的变更,只对发生变更的模块重新加载,只需要让相关模块的 boundary 失效即可,这样 HMR 更新速度不会因为应用体积增加而变慢,但 Webpack 需要经历一次打包构建流程,所以 HMR Vite 表现会好于 Webpack。
核心流程
Vite 热更新流程可以分为以下:
创建一个 websocket 服务端和client文件,启动服务
监听文件变更
当代码变更后,服务端进行判断并推送到客户端
客户端根据推送的信息执行不同操作的更新
创建 WebSocket 服务
在 dev server 启动之前,Vite 会创建websocket服务,利用chokidar创建一个监听对象 watcher 用于对文件修改进行监听等等,具体核心代码在 node/server/index 下
createWebSocketServer 就是创建 websocket 服务,并封装内置的 close、on、send 等方法,用于服务端推送信息和关闭服务
源码地址:
packages/vite/src/node/server/ws.ts
执行热更新
当接受到文件变更时,会执行 change 回调
scss复制代码watcher.on('change', async (file) => {
file = normalizePath(file)
// invalidate module graph cache on file change
moduleGraph.onFileChange(file)
await onHMRUpdate(file, false)
})
当文件发生更改时,这个回调函数会被触发。file 参数表示发生更改的文件路径。
首先会通过 normalizePath 将文件路径标准化,确保文件路径在不同操作系统和环境中保持一致。
然后会触发 moduleGraph 实例上的 onFailChange 方法,用来清空被修改文件对应的 ModuleNode 对象的 transformResult 属性,**使之前的模块已有的转换缓存失效。**这块在下一部分会讲到。
ModuleNode 是 Vite 最小模块单元
moduleGraph 是整个应用的模块依赖关系图
源码地址:
packages/vite/src/node/server/moduleGraph.ts
ts复制代码onFileChange(file: string): void {
const mods = this.getModulesByFile(file)
if (mods) {
const seen = new Set<ModuleNode>()
mods.forEach((mod) => {
this.invalidateModule(mod, seen)
})
}
}
invalidateModule(
mod: ModuleNode,
seen: Set<ModuleNode> = new Set(),
timestamp: number = Date.now(),
isHmr: boolean = false,
hmrBoundaries: ModuleNode[] = [],
): void {
...
// 删除平行编译结果
mod.transformResult = null
mod.ssrTransformResult = null
mod.ssrModule = null
mod.ssrError = null
...
mod.importers.forEach((importer) => {
if (!importer.acceptedHmrDeps.has(mod)) {
this.invalidateModule(importer, seen, timestamp, isHmr)
}
})
}
可能会有疑惑,Vite 在开发阶段不是不会打包整个项目吗?怎么生成模块依赖关系图
确实是这样,Vite 不会打包整个项目,但是仍然需要构建模块依赖关系图,当浏览器请求一个模块时
Vite 首先会将请求的模块转换成原生 ES 模块
分析模块依赖关系,也就是 import 语句的解析
将模块及依赖关系添加到 moduleGraph 中
返回编译后的模块给浏览器
因此 Vite 的 Dev 阶段时动态构建和更新模块依赖关系图的,无需打包整个项目,这也实现了真正的按需加载。
handleHMRUpdate
在 chokidar change 的回调中,还执行了 onHMRUpdate 方法,这个方法会调用执行 handleHMRUpdate 方法
在 handleHMRUpdate 中主要会分析文件更改,确定哪些模块需要更新,然后将更新发送给浏览器。
浏览器端的 HMR 运行时会接收到更新,并在不刷新页面的情况下替换已更新的模块。
源码地址:
packages/vite/src/node/server/hmr.ts
ts复制代码export async function handleHMRUpdate(
file: string,
server: ViteDevServer,
configOnly: boolean,
): Promise<void> {
const { ws, config, moduleGraph } = server
// 获取相对路径
const shortFile = getShortName(file, config.root)
const fileName = path.basename(file)
// 是否配置文件修改
const isConfig = file === config.configFile
// 是否自定义插件
const isConfigDependency = config.configFileDependencies.some(
(name) => file === name,
)
// 环境变量文件
const isEnv =
config.inlineConfig.envFile !== false &&
(fileName === '.env' || fileName.startsWith('.env.'))
if (isConfig || isConfigDependency || isEnv) {
// auto restart server
...
try {
await server.restart()
} catch (e) {
config.logger.error(colors.red(e))
}
return
}
...
// 如果是 Vite 客户端代码发生更改,强刷
if (file.startsWith(normalizedClientDir)) {
// ws full-reload
return
}
// 获取到文件对应的 ModuleNode
const mods = moduleGraph.getModulesByFile(file)
...
// 调用所有定义了 handleHotUpdate hook 的插件
for (const hook of config.getSortedPluginHooks('handleHotUpdate')) {
const filteredModules = await hook(hmrContext)
...
}
// 如果是 html 文件变更,重新加载页面
if (!hmrContext.modules.length) {
// html file cannot be hot updated
if (file.endsWith('.html')) {
// full-reload
}
return
}
updateModules(shortFile, hmrContext.modules, timestamp, server)
}
配置文件更新、.env更新、自定义插件更新都会重新启动服务 reload server
Vite 客户端代码更新、index.html 更新,重新加载页面
调用所有 plugin 定义的 handleHotUpdate 钩子函数
过滤和缩小受影响的模块列表,使 HMR 更准确。
返回一个空数组,并通过向客户端发送自定义事件来执行完整的自定义 HMR 处理
插件处理更新 hmrContext 上的 modules
如果是其他情况更新,调用 updateModules 函数
流程图如下
在 updateModules 中主要是对模块进行处理,生成 updates 更新列表,ws.send 发送 updates 给客户端
ws 客户端响应
客户端在收到服务端发送的 ws.send 信息后,会进行相应的响应
当接收到服务端推送的消息,通过不同的消息类型做相应的处理,比如 update、connect、full-reload 等,使用最频繁的是 update(动态加载热更新模块)和 full-reload (刷新整个页面)事件。
源码地址:
packages/vite/src/client/client.ts
在 update 的流程里,会使用 Promise.all 来异步加载模块,如果是 js-update,及 js 模块的更新,会使用 fetchUpdate 来加载
ts复制代码if (update.type === 'js-update') {
return queueUpdate(fetchUpdate(update))
}
fetchUpdate 会通过动态 import 语法进行模块引入
浏览器缓存优化
Vite 还利用 HTTP 加速整个页面的重新加载。 对预构建的依赖请求使用 HTTP 头 max-age=31536000, immutable 进行强缓存,以提高开发期间页面重新加载的性能。一旦被缓存,这些请求将永远不会再次访问开发服务器。
这部分的实现在 transformMiddleware 函数中,通过中间件的方式注入到 Koa dev server 中。
源码地址:
packages/vite/src/node/server/middlewares/transform.ts
若需要对依赖代码模块做改动可手动操作使缓存失效:
ts复制代码vite --force
或者手动删除 node_modules/.vite 中的缓存文件。
总结
Vite 采用 No Bundle 和 esbuild 预构建,速度远快于 Webpack,实现快速的冷启动,在 dev 模式基于 ES module,实现按需加载,动态 import,动态构建 Module Graph。
在 HMR 上,Vite 利用 HTTP 头 cacheControl 设置 max-age 应用强缓存,加速整个页面的加载。
当然 Vite 还有很多的不足,比如对 splitChunks 的支持、构建生态 loader、plugins 等都弱于 Webpack。不过 Vite 仍然是一个非常好的构建工具选择。在不少应用中,会使用 Vite 来进行开发环境的构建,采用 Webpack5 或者其他 bundle base 的工具构建生产环境。
原文链接:
https://juejin.cn/post/72567825