可以串成一条完整的 Vue 应用启动链路。先记住这张图:
text
浏览器访问 /
↓
index.html
↓
<script type="module" src="/src/main.ts">
↓
main.ts
↓
createApp(App)
↓
App.vue
↓
其他 Vue 组件
而 Vite 在旁边负责:
text
读取 package.json
读取 vite.config.ts
解析 TS / Vue / CSS
把 .vue 转成浏览器能执行的 JS
下面逐个解释。
1. package.json 是干什么的?
来源 package.json 是一个 Node.js / npm 项目的项目清单文件。
你可以把它理解成:
这个项目的"身份证 + 软件依赖清单 + 命令入口"。
典型 Vue 项目:
json
{
"name": "todo-list",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "vue-tsc -b && vite build",
"preview": "vite preview"
},
"dependencies": {
"vue": "^3.5.0"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.0",
"typescript": "~5.9.0",
"vite": "^7.0.0"
}
}
最重要的是三部分。
第一部分:项目信息
json
{
"name": "todo-list",
"version": "1.0.0"
}
告诉 npm:
text
这个项目叫什么
版本是多少
第二部分:scripts
json
"scripts": {
"dev": "vite",
"build": "vite build"
}
所以执行:
bash
npm run dev
本质就是:
bash
vite
执行:
bash
npm run build
本质就是:
bash
vite build
所以:
npm run xxx会去package.json的scripts里找对应命令。
第三部分:dependencies
json
"dependencies": {
"vue": "^3.5.0"
}
表示项目运行依赖 Vue。
而:
json
"devDependencies": {
"vite": "...",
"typescript": "...",
"@vitejs/plugin-vue": "..."
}
主要是开发、编译、构建阶段使用的工具。
因此可以这样记:
text
package.json
项目是谁?
↓
name / version
怎么运行?
↓
scripts
依赖什么?
↓
dependencies / devDependencies
2. vite.config.ts 是干什么的?
来源 它是 Vite 的配置文件。
最简单的 Vue 项目通常是:
ts
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})
这里最关键的一句:
ts
plugins: [vue()]
意思是:
给 Vite 安装 Vue 支持。
因为原始 Vite 并不是天然就能处理:
text
.vue
文件。
需要:
text
@vitejs/plugin-vue
来处理。
除此之外,vite.config.ts 还能配置很多工程能力。
例如路径别名:
ts
export default defineConfig({
resolve: {
alias: {
'@': '/src'
}
}
})
于是:
ts
import Todo from '@/components/Todo.vue'
代替:
ts
import Todo from '../../../components/Todo.vue'
还可以配置开发服务器:
ts
server: {
port: 3000
}
代理:
ts
server: {
proxy: {
'/api': {
target: 'http://localhost:8080'
}
}
}
构建:
ts
build: {
outDir: 'dist'
}
所以一句话:
vite.config.ts决定 Vite "怎么开发、怎么解析代码、怎么打包"。
3. index.html 为什么放在 src 外面?
这是一个很值得理解的问题。
首先:
来源 在 Vite 中,index.html 不只是普通静态文件,而是应用入口的一部分。
你的目录通常是:
text
todo-list/
│
├─ index.html
├─ package.json
├─ vite.config.ts
│
└─ src/
├─ main.ts
├─ App.vue
└─ components/
打开 index.html:
html
<!doctype html>
<html>
<head>
<title>Todo App</title>
</head>
<body>
<div id="app"></div>
<script
type="module"
src="/src/main.ts">
</script>
</body>
</html>
关键:
html
<script type="module" src="/src/main.ts"></script>
整个程序就是从这里开始进入 JS 世界的。
也就是说:
text
index.html
↓
main.ts
为什么不放 src?
因为 Vite 把:
text
项目根目录/index.html
当作开发服务器的 HTML 入口。
比如你运行:
bash
npm run dev
然后访问:
text
http://localhost:5173/
Vite 默认对应:
text
/index.html
而:
text
src/
主要放:
应用源代码。
比如:
text
src/
├── main.ts
├── App.vue
├── components/
├── views/
├── stores/
└── router/
所以两者职责不同:
text
项目根目录
│
├── index.html
│ ↑
│ 网页入口
│
└── src/
↑
应用源码
推导 可以理解成:
text
index.html = 房子的门
src = 房子里的东西
浏览器先通过门进来,再加载里面的应用。
4. main.ts 是干什么的?
main.ts 是 Vue 应用的:
JavaScript/TypeScript 启动入口。
典型代码:
ts
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
只有三步。
第一步
ts
import { createApp } from 'vue'
拿到 Vue 提供的创建应用函数。
第二步
ts
import App from './App.vue'
拿到你的根组件。
第三步
ts
createApp(App).mount('#app')
可以拆开:
ts
const app = createApp(App)
app.mount('#app')
意思是:
text
创建 Vue 应用
↓
根组件 = App.vue
↓
挂载到 index.html 的 #app
注意 index.html:
html
<div id="app"></div>
正好对应:
ts
.mount('#app')
最终:
text
index.html
<div id="app"></div>
↑
│
main.ts
createApp(App).mount('#app')
↑
│
App.vue
这三个文件因此连起来了。
5. App.vue 和 main.ts 是什么关系?
这个问题面试也很容易被问。
准确说:
text
main.ts
↓ 创建应用
App.vue
↓ 作为根组件
其他组件
例如:
ts
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
这里:
ts
App
就是:
text
App.vue
因此:
main.ts是程序启动入口,App.vue是组件树的根节点。
假设:
vue
<!-- App.vue -->
<script setup>
import Header from './components/Header.vue'
import TodoList from './components/TodoList.vue'
</script>
<template>
<Header />
<TodoList />
</template>
整个组件树就是:
text
index.html
<div id="app">
│
▼
App.vue
/ \
Header TodoList
│
▼
TodoItem
所以不要把它们混为一谈。
text
main.ts
职责:启动 Vue
App.vue
职责:描述 UI
可以类比:
text
main.ts = 点火开关
App.vue = 整辆车
6. 浏览器为什么能识别 .vue 文件?
这里有一个很重要的认知:
浏览器其实不认识
.vue文件。
来源 浏览器原生认识的主要是:
text
HTML
CSS
JavaScript
浏览器并不知道:
vue
<script setup>
const message = 'hello'
</script>
<template>
<h1>{{ message }}</h1>
</template>
<style>
h1 {
color: red;
}
</style>
是什么意思。
所以:
ts
import App from './App.vue'
直接扔给普通浏览器是不行的。
真正处理它的是:
text
Vite
+
@vitejs/plugin-vue
+
@vue/compiler-sfc
.vue 叫:
SFC ------ Single File Component,单文件组件。
例如:
vue
<script setup>
const count = 1
</script>
<template>
<div>{{ count }}</div>
</template>
<style>
div {
color: red;
}
</style>
Vite 遇到:
ts
import App from './App.vue'
后,大致会经过:
text
App.vue
↓
@vitejs/plugin-vue
↓
@vue/compiler-sfc
↓
<script>
↓
JavaScript
<template>
↓
render 函数
<style>
↓
CSS
例如:
vue
<template>
<h1>Hello</h1>
</template>
会被编译成类似:
js
function render() {
return createElementVNode(
"h1",
null,
"Hello"
)
}
最后浏览器真正执行的是:
text
JavaScript
+
CSS
而不是 .vue。
因此完整链路是:
text
浏览器
│
▼
index.html
│
▼
/src/main.ts
│
▼
import App.vue
│
│ 浏览器请求 App.vue
▼
Vite Dev Server
│
▼
Vue Plugin
│
▼
Vue Compiler
│
▼
JavaScript
│
▼
浏览器执行
最后把 6 个知识点串起来
这是你现在最应该形成的工程心智模型:
text
package.json
项目依赖 / 命令
│
▼
npm run dev
│
▼
Vite
│
vite.config.ts
配置 Vite 行为
│
▼
index.html
│
<script src="/src/main.ts">
│
▼
main.ts
│
createApp(App)
│
▼
App.vue
│
其他 Vue 组件
其中 .vue 文件:
text
.vue
↓
Vite + Vue Plugin
↓
Vue Compiler
↓
JavaScript + CSS
↓
浏览器
所以你可以用一句话概括整个 Vue 项目启动过程:
推导 浏览器首先加载根目录的
index.html,index.html通过 ES Module 引入main.ts;main.ts使用createApp创建 Vue 应用,以App.vue作为根组件并挂载到#app。开发过程中 Vite 根据vite.config.ts处理模块请求,并通过 Vue 插件把浏览器无法直接执行的.vueSFC 编译成 JavaScript 和 CSS。