知

模块一 · 起步与基础

第 1 / 17 节18 分钟阅读型

起步:用 Vite 跑通第一个 Vue 应用

从装环境到跑起第一个 SFC,讲清脚手架选项、四个脚本、目录结构、模块解析与 HMR,让"项目能跑"不再是靠运气。

读完这一节,你能做到

  1. 判断本机环境是否满足 Vue 3 项目要求,并选对包管理器。
  2. 用官方脚手架起项目,说清每个选项与脚本的作用。
  3. 读懂目录结构,知道一个 .vue 文件从磁盘到浏览器的完整路径。
  4. 定位"改了代码页面不生效"的四类原因。

一、先装对环境

Vue 3 项目至少要三样东西:Node、包管理器、编辑器插件。

工具要求检查方式不满足的后果
Node18 LTS 及以上(20/22 更稳)node -vVite 起不来,报 crypto.hash is not a function
包管理器npm 9+ 或 pnpm 8+npm -v依赖装不齐、幽灵依赖
编辑器插件Vue - Official(Volar)扩展面板模板无类型提示、.vue 报错
浏览器插件Vue DevTools开发者工具多一个面板无法看组件树与响应式数据

两个常见误区:

  • 装 Vetur。Vetur 是 Vue 2 时代插件,在 Vue 3 + <script setup> 下会给出大量错误提示,必须禁用。
  • 团队里 npm 与 pnpm 混用。锁文件不同(package-lock.json 与 pnpm-lock.yaml),依赖树会漂移。团队只允许一种,并在 package.json 里写 "packageManager" 字段固定版本。

版本还要看语法:defineModel() 需要 Vue 3.4+,useTemplateRef() 需要 3.5+。老项目里看到这两个 API 报未定义,先查 package.json 里的 vue 版本,而不是怀疑写法。

二、三种起项目的方式

方式命令适用代价
CDN 单文件引入 vue.global.js试语法、给静态页加交互无构建、无 SFC、性能差
Vite 脚手架npm create vue@latest绝大多数项目需要 Node 环境
Nuxtnpx nuxi init需要 SSR/SEO/文件路由约定多、调试链路长

新手从 Vite 脚手架开始。命令会进入交互式提问:

npm create vue@latest my-app
cd my-app
npm install
npm run dev

交互项的建议:

选项建议理由
TypeScript选是组件 props 与接口类型能省掉大量运行时错误
JSX一般不选模板够用,JSX 只在极动态场景需要
Vue Router选是多页面必然要路由
Pinia选是跨页面状态迟早要有
Vitest选是至少能写组合式函数的单测
E2E按需Playwright 体积大,先用 Vitest
ESLint / Prettier选是团队协作的底线

不要一次全选。每多一个选项,就多一套配置要先理解;先最小跑通,再按需补。

三、四个脚本各自做什么

{
  "scripts": {
    "dev": "vite",
    "build": "run-p type-check \"build-only {@}\" --",
    "preview": "vite preview",
    "type-check": "vue-tsc --build"
  }
}
  • dev:启动开发服务器,不打包,浏览器请求哪个模块就编译哪个模块。
  • build:先做类型检查,再把模板预编译成 render 函数,产物在 dist/。
  • preview:用本地静态服务器托管 dist/,用来验证生产产物。
  • type-check:vue-tsc 会检查模板里的表达式类型,这是纯 tsc 做不到的。

注意 preview 不是生产服务器。它只做静态托管,没有 gzip、缓存头与 history fallback 的完整配置,上线仍要用 nginx 或 CDN。

四、目录结构:每个文件负责什么

my-app/
├─ index.html          # 唯一的 HTML 入口,里有 <div id="app">
├─ vite.config.ts      # 构建与插件配置
├─ tsconfig.json       # 类型检查配置(含路径别名)
├─ public/             # 原样拷贝的资源,不参与构建
└─ src/
   ├─ main.ts          # 应用入口,createApp 并挂载
   ├─ App.vue          # 根组件
   ├─ components/      # 可复用组件
   ├─ views/           # 路由页面级组件
   ├─ router/          # 路由表
   ├─ stores/          # Pinia
   └─ assets/          # 参与构建的图片与样式

两个容易混的点:

  • public/ 里的文件原样输出,路径写 /logo.svg;src/assets/ 里的文件参与构建,要用 import 或 new URL(..., import.meta.url) 引用,才会被哈希与压缩。
  • index.html 不在 public/ 里,它是 Vite 的入口。Vue CLI 时代靠 html-webpack-plugin 生成 HTML,Vite 反过来以 HTML 为入口,这也是它冷启动快的原因之一。

五、SFC 三段式与第一个组件

<script setup lang="ts">
import { ref } from "vue";

const count = ref(0);
</script>

<template>
  <button class="counter" @click="count++">{{ count }}</button>
</template>

<style scoped>
.counter {
  padding: 6px 12px;
  border-radius: 6px;
}
</style>

三个要点:

  1. <script setup> 是编译期语法糖,顶层变量自动暴露给模板,不需要 return,也没有 this。
  2. Vue 3 的模板支持多根节点(Fragment),但多根组件在接收 attrs 时需要显式指定落到哪个节点,初学建议先保持单根。
  3. scoped 会给元素加 data-v-xxx 属性并改写选择器,样式只作用于当前组件;要影响子组件内部,用 :deep()。

入口文件只有三行:

import { createApp } from "vue";
import App from "./App.vue";

createApp(App).mount("#app");

六、路径别名:为什么每个项目都要配

深目录里写 ../../../components/Button.vue 既难读又易错。配一个 @ 指向 src:

// vite.config.ts
resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)) } }

还要在 tsconfig.json 的 compilerOptions.paths 里同步一份 "@/*": ["./src/*"],否则编辑器和 vue-tsc 不认识别名。两处必须一致,只配一处是最常见的"运行正常但编辑器爆红"。

七、一次 dev 请求的完整链路

浏览器请求 /index.html
→ Vite 返回 HTML,注入 /src/main.ts
→ 浏览器以原生 ESM 请求 main.ts
→ 请求 App.vue,被 @vitejs/plugin-vue 拆成三块:
   · 编译后的 JS(render 函数)
   · 样式(注入 <style>)
   · 类型/作用域信息
→ 浏览器执行并挂载

由此可以推出三件事:

  • 为什么冷启动快:不打包整包,只编译被请求到的模块;依赖用 esbuild 预构建缓存。
  • 为什么 HMR 快:只替换改动模块的边界,不刷新整页;但组件内的普通变量状态会丢,ref 状态通常能保留。
  • 为什么生产必须 build:浏览器不认识 .vue,也不该在生产环境现场编译模板。

八、"改了没生效"的排查顺序

现象常见原因验证方式
改了代码页面没变文件没保存;HMR 断开;改的是 dist/ 或另一个副本看终端是否有 hmr update 日志
只有样式没变样式被更高优先级覆盖;scoped 选择器没命中DevTools 看元素上的 data-v- 与最终样式
刷新后全白运行时错误看 Console 第一行报错,而不是先改代码
本地正常、构建后报错用了 public 路径、大小写不一致、动态 import 路径拼错跑 build + preview 复现

排查顺序固定为:先看终端 → 再看 Console → 最后看代码。跳过前两步,绝大多数时间会浪费在猜。

九、常见坑

  1. Node 版本过低,vite 直接启动失败,却以为是代码问题。
  2. 同时装着 Vetur 与 Volar,模板报一堆假错误。
  3. index.html 里手写了 <script src="/src/main.ts">,又在 main.ts 里挂载一次,导致重复挂载。
  4. 把 src/assets 的图片写成绝对路径字符串,构建后 404。
  5. 直接双击打开 dist/index.html,file:// 协议禁止加载 ESM,页面全白。
  6. 配了别名只在 vite.config 里,tsconfig 没配,编辑器一片红。

十、动手任务:自写 → AI 写 → 对比 → 取舍

四步不跳:① 自己写(限时,关掉补全)→ ② 让 AI 写同一题 → ③ 逐条比对 → ④ 说出保留谁的写法。 完整动作与记录模板见《AI 协作与代码评审》。

① 自己写

  1. 用脚手架起一个最小项目,删掉示例组件,只保留 App.vue 与一个 Counter.vue。
  2. 配好 @ 别名,并在 App.vue 里用它引入 Counter。
  3. 在 dev 模式下分别改 script 里的普通变量与 template,观察 HMR 行为差异并记录。
  4. 跑一次 build 与 preview,确认产物可用;再故意把一张图片放进 public 与 assets 各引用一次,对比构建后的路径。

② 让 AI 写同一题

把第 2 题(组件 + 请求)交给 AI,补一句 Vue 3 + TS + Vite,用 <script setup>,不新增依赖。

③ 对比要盯的点

  • 状态它是用 ref 逐个声明,还是 reactive 包一个大对象?后者一解构就丢响应性。
  • 列表渲染它有没有给 :key?给了的话用的是 id 还是 index?
  • 它给的目录结构能不能直接当项目起点,还是三分钟后就得分层?

④ 一句话取舍

它的版本能跑,但哪一处会在你解构 props 或解构 state 时静默失去响应性?

十一、自测

  1. dev 与 build 的本质区别是什么?
  2. 为什么 public 里的资源不需要 import?
  3. @ 别名要配哪两个地方?
  4. HMR 为什么比整页刷新快,代价是什么?
  5. 构建后白屏,应该先看哪里?

参考答案要点:按需编译 vs 预编译产物;原样拷贝不参与构建;vite.config 与 tsconfig paths;只替换模块边界但会丢局部状态;Console 的第一个报错。

配套题目

★ 越多越该先背;点题目可看完整答案与深入版。

提示:读完可以顺手做几道对应题目,检验是否真的理解