vue3使用vite打包的原理
缘起: Vue3 + Vite 为什么 npm run dev 一跑就能看到访问连接:
1. Vite 的核心原理
Vite 的名字来自法语 "vite" = 快速。它的核心理念就是利用浏览器原生 ES Module 能力,避免传统构建工具(Webpack/Rollup)那种“先打包再启动”的流程。
传统打包工具(Webpack):
- 启动时会把整个项目打包成一个 bundle 文件(或多个 chunk)。
- 大项目启动时要等很久才能看到页面。
Vite:
- 启动时不打包,只是起一个开发服务器。
- 浏览器请求哪个文件,就按需即时编译(on-demand transformation),用 ES Module 直接返回。
- 这样启动速度非常快。
2. 为什么 npm run dev 就能看到链接
通常在 package.json 里会有这样一行:
"scripts": {
"dev": "vite"
}
当你执行 npm run dev 时,本质上是调用了 vite CLI(开发服务器)。它的执行流程是这样的:
读取配置
- Vite 会读取
vite.config.ts或vite.config.js(如果有)。 - 同时识别项目入口(默认是
index.html)。
- Vite 会读取
启动开发服务器
- Vite 内置了一个基于 connect 的 dev server。
- 监听一个端口(默认
5173)。 - 输出类似
http://localhost:5173/的访问链接。
拦截浏览器请求
- 浏览器访问
http://localhost:5173/时,Vite 会返回index.html。 - 当
index.html里引用了src/main.ts,Vite 就会动态编译这个文件,并以 ESM 模块的形式返回给浏览器。 - 如果
main.ts又 import 了 Vue 组件,Vite 会继续即时编译这些.vue文件(通过@vitejs/plugin-vue插件)。
- 浏览器访问
热更新 (HMR)
- Vite 内置 WebSocket 连接。
- 当文件修改时,Vite 只会重新编译变动的文件,并通过 HMR 更新浏览器里的模块,而不是整个页面刷新。
- 这就是为什么改一个组件页面能秒级看到变化。
3. 为什么能直接访问 Vue3 代码
因为 Vite 把 Vue 文件和 TypeScript 等等转译成浏览器可运行的 ESM:
.vue文件 → 转成 JS 模块(render function + script).ts文件 → 用 esbuild 转成 JS- 依赖包(比如
vue)→ 预构建成单个 ESM(用 esbuild 优化)
所以浏览器收到的都是标准的 JS 模块,能直接运行。
✅ 总结:
npm run dev启动的是 Vite 的开发服务器。- Vite 的原理是 利用浏览器原生 ES Modules,做到即开即用、按需编译。
- 浏览器访问链接后,Vite 会按需编译并返回文件,同时通过 HMR 实现热更新。
什么是ES Moudle?
关键点:ESM 模块。
npm run dev 之所以能让 Vue3 + Vite 立即跑起来,核心就在 ESM(ES Modules,原生模块化标准)。我给你拆一下:
1. 什么是 ESM 模块
ESM 是 浏览器和 Node.js 官方支持的模块化规范,用 import / export 来组织代码。 例如:
// math.js
export function add(a, b) {
return a + b
}
// main.js
import { add } from './math.js'
console.log(add(2, 3))
📌 关键点:
- 浏览器可以 原生识别
import(不用打包工具) - 每个模块文件就是一个 独立作用域
- 按需加载,避免一次性打包成大文件
2. Vite 为什么要用 ESM
Vite 在开发模式下,完全依赖浏览器的 ESM 特性:
你访问
http://localhost:5173/时,index.html里会写:<script type="module" src="/src/main.ts"></script>浏览器看到
type="module",就会去请求/src/main.ts。Vite 拦截这个请求:
- 如果是
.ts→ 用 esbuild 转译成 JS - 如果是
.vue→ 用插件转成 JS 模块(render + script) - 如果是依赖(如
vue)→ 提前 预构建 成 ESM(避免node_modules太多小文件导致请求过多)
- 如果是
浏览器再按需加载这些 JS 模块,形成一个 模块依赖树。
- 改一个文件,Vite 只需要重新编译这个文件,并通过 HMR 替换模块,而不是整个页面刷新。
3. 为什么 ESM = Vite 极速开发体验
- 不用一次性打包 → 启动极快
- 模块天然分割 → 修改一个模块时,只更新这个模块(HMR)
- 浏览器直接支持 → 少了一层构建的中间步骤
✅ 总结: Vite 的核心就是基于浏览器原生 ESM 模块,把每个 .vue、.ts、依赖都当作独立的 ESM 来处理。 这也是它比 Webpack/Gulp 那些工具快得多的根本原因。
ES Modules 分类
在 Vite 的 开发模式 下,最后所有代码都会变成 ES Modules (ESM),再交给浏览器去执行。 不过这里要分三类情况来看:
1. 你的源码(.vue、.ts、.js)
.vue文件 → 通过@vitejs/plugin-vue编译成 JS 模块:// App.vue → 编译后 export default { render() { ... }, setup() { ... } }.ts文件 → 用 esbuild 转成 JS,再包装成 ESM:// main.ts import { createApp } from 'vue'→ 转译后仍然是
import语法,浏览器直接识别。
👉 最终:每个源码文件就是一个 独立的 ESM 模块。
2. 依赖包(node_modules 里的第三方库)
这里是 Vite 的一个 优化点:
node_modules里的库(例如vue,lodash-es)有的不是 ESM 格式,而是 CommonJS 或 UMD。- Vite 会在启动时做依赖预构建(dependency pre-bundling),把这些库用 esbuild 转换成 ESM 格式。
比如 vue 最终会被处理成一个 ESM 文件放在:
node_modules/.vite/deps/vue.js
👉 所以即便原始库不是 ESM,Vite 也会先帮你“转译成 ESM”,保证浏览器能用。
3. 最终效果
开发模式下: 所有请求的文件(Vue/TS/依赖包)最后都是 ES Modules,浏览器原生加载。
生产模式下(
vite build): Vite 会调用 Rollup,把所有模块打包、拆分优化(代码分块、Tree Shaking、压缩)。 最终线上跑的不是一堆 ESM 文件,而是构建后的 bundle,这样减少网络请求数、提升性能。
📌 总结一句话:
- 开发模式:所有
.vue、.ts、依赖包 → 全部转成 ESM,浏览器直接按需加载。 - 生产模式:Vite 会再把这些 ESM 模块交给 Rollup 打包优化,生成少量高效的产物。
type="module"的作用
好问题 👍,<script type="module"> 正是 Vite 能在浏览器里直接跑的关键。
1. 背景
在最早的浏览器里,JavaScript 只有 全局脚本:
<script src="main.js"></script>
所有变量、函数都挂在 window 上,容易冲突,也没办法分模块管理。
后来有了各种 模块化方案:
- CommonJS(Node.js 用的
require()) - AMD/UMD(浏览器异步模块加载) 但这些都是“约定俗成”,浏览器并不原生支持。
直到 ESM(ECMAScript Modules) 标准推出,浏览器原生支持 import/export,才有了今天的 <script type="module">。
2. type="module" 的作用
当你写:
<script type="module" src="/src/main.js"></script>
它和普通 <script> 有这些区别:
✅ 支持 ESM 语法
可以在浏览器里直接写:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
浏览器会自动去请求并执行这些模块。
✅ 默认是 延迟加载 (defer)
等 DOM 解析完再执行,避免阻塞页面加载。 (普通 <script> 会阻塞解析)
✅ 每个模块有自己的作用域
不会像普通 <script> 那样污染全局变量。 模块之间通过 import/export 明确依赖关系。
✅ 自动开启严格模式 (use strict)
提升代码安全性,比如不能随便用未声明的变量。
✅ 可以加载远程模块
甚至可以直接写 CDN 地址:
<script type="module">
import _ from "https://cdn.skypack.dev/lodash-es"
console.log(_.join(["Hello", "Vite"], " "))
</script>
(这也是 Vite 为什么可以直接利用浏览器的能力。)
3. 在 Vite 里的意义
当 Vite 返回 index.html 时,它会自动插入:
<script type="module" src="/src/main.ts"></script>
👉 浏览器一看到 type="module" 就会触发 原生 ESM 加载流程:
- 请求
/src/main.ts - 里面
import App from './App.vue' - 浏览器继续请求
/src/App.vue(Vite 会在后台把它编译成 ESM)
最终整个 Vue 项目就跑起来了。
✅ 一句话总结: type="module" 的作用是告诉浏览器:
这个脚本是一个 ESM 模块,请按模块化规则加载(支持
import/export、作用域隔离、延迟执行)。
关于connect
https://github.com/senchalabs/connect
connect是一个 Node.js 里的 HTTP 中间件框架。
1. 什么是 connect
connect是一个非常轻量的 Node.js 库,用来快速创建 HTTP 服务器。- 它本质上是基于 Node.js 的
http模块,在其上增加了 中间件机制。 - Express(很流行的 Node 框架)就是在
connect的基础上扩展出来的。
简单示例:
import connect from 'connect'
import http from 'http'
const app = connect()
// 注册中间件
app.use((req, res, next) => {
console.log('Request URL:', req.url)
next()
})
app.use((req, res) => {
res.end('Hello from Connect server')
})
// 启动 HTTP 服务
http.createServer(app).listen(3000, () => {
console.log('Server running at http://localhost:3000')
})
运行后,你就有了一个 http://localhost:3000 的服务。
2. Vite 为什么用 connect
当你执行 npm run dev 启动 Vite 开发服务器时:
Vite 底层会创建一个 connect 应用
然后注册各种中间件,例如:
- 处理静态文件请求(返回 index.html、图片、CSS 等)
- 处理 ESM 请求(拦截
.vue、.ts转译成 JS) - HMR WebSocket 通信
最终由 Node.js 内置的
http模块把这个服务跑起来
所以你能在浏览器访问 http://localhost:5173,但真正的服务是 Node.js + connect 起的。
3. 总结
connect是 Node.js 里的 HTTP 框架,不是浏览器的。- 它给 Vite 提供了一个可扩展的 中间件体系。
- 浏览器只是作为客户端,请求 Vite 用
connect起的本地服务。这个是重点,只负责启动一个本地服务。
