第一个应用:用 Vite 跑起来
Vue 3 全景与第一个应用 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。
学习目标
这一节把「看懂」变成「跑通」:用 Vite 从零建一个 Vue 3 项目,写一个带状态的计数器组件,理解点击事件从模板到逻辑的完整链路,并知道开发服务器、热更新和构建产物分别对应什么命令。
问题场景
知道 Vue 大概长什么样了,但真让你从零跑一个项目还是会卡:create-vue 交互式问了一堆选项不知道选什么;生成的文件里 vite.config.js、index.html、main.js 各管什么说不清;改了代码页面没反应,分不清是热更新没生效还是自己写错了。
核心解释
先建项目。跑 npm create vue@latest 时,官方脚手架会问一组可选功能(TypeScript、JSX、Router、Pinia、Vitest、ESLint)。第一遍建议只开 TypeScript 和 ESLint,其余先不勾——每多一个选项就多一层还没学到的概念。建好后执行:
cd my-vue-app
npm install
npm run dev
npm run dev 启动 Vite 开发服务器,默认 http://localhost:5173。Vite 的特点是开发时不做整体打包:浏览器请求哪个模块就即时编译哪个,所以冷启动快、热更新(HMR)只替换改动的模块且通常保留组件状态。npm run build 才真正打包到 dist/,用 npm run preview 可以本地预览构建产物。
项目结构里只需要先认识四个文件:
index.html:Vite 的入口,里面有<div id="app"></div>,Vue 应用挂载在这里。src/main.js:创建应用、注册插件、挂载根组件。src/App.vue:根组件。vite.config.js:构建配置,插件(比如@vitejs/plugin-vue)在这里注册。
然后写组件。Vue 的组件就是一个 .vue 文件,下面这个计数器包含:一个响应式状态、一个方法、一个模板绑定:
<script setup>
import { ref } from "vue";
const count = ref(0);
function increment() {
count.value += 1;
}
</script>
<template>
<section>
<h2>计数器</h2>
<p>当前值:{{ count }}</p>
<button @click="increment">加一</button>
<button @click="count = 0">重置</button>
</section>
</template>
<style scoped>
section { padding: 16px; border: 1px solid #e5e7eb; border-radius: 8px; }
button { margin-right: 8px; }
</style>
关键点逐个看:ref(0) 创建响应式容器,脚本里用 .value 读写,模板里自动解包;@click 是 v-on:click 的简写,等号右边可以是方法引用(increment)也可以是内联语句(count = 0,模板里赋值会自动解包到 .value);{{ }} 插值把响应式值渲染成文本,值变则文本自动更新。
把组件挂到应用上只需要在 App.vue 里导入使用:
<script setup>
import Counter from "./components/Counter.vue";
</script>
<template>
<main>
<h1>我的第一个 Vue 应用</h1>
<Counter />
</main>
</template>
<script setup> 里 import 的组件自动注册,模板里直接写标签即可——这是编译期完成的,不需要 components: { Counter } 这种 Options API 写法。
示例拆解
把「点击 → 更新」的链路走一遍:
- 用户点击「加一」,浏览器触发 click 事件;
- 模板编译产物把事件绑定到
increment函数; - 函数执行
count.value += 1,ref 的 setter 被触发,Vue 记录「count 变了」; - 依赖追踪系统知道模板里的
{{ count }}读过 count,把该组件的更新排进微任务队列; - 微任务清空后组件重新渲染,文本从「当前值:0」变成「当前值:1」。
和 React 对比:React 是 setCount(count + 1) 后组件函数整体重跑、diff 出变化;Vue 是精确通知「读过 count 的渲染副作用」重跑。所以 Vue 里没有「闭包快照」问题——你在事件处理函数里读到的 count.value 永远是最新值。
验证方式:在 increment 里加一行 console.log(count.value),连点三次,控制台依次输出 1、2、3。再打开 Vue Devtools(浏览器扩展),在组件面板能看到 count 的实时值。
动手任务
- 给计数器加一个「减一」按钮,要求 count 不小于 0(到 0 时按钮禁用或点击无效)。
- 新增一个
step状态(初始 2),把加一改成「加 step」,页面上显示当前步长。 - 把
Counter.vue复制一份改名CounterCard.vue,在App.vue里同时渲染两个,确认两个实例的 count 互不影响——这验证了「组件状态是实例级的」。
常见错误
- 脚本里忘记
.value:count += 1实际是把 Ref 对象和数字相加,得到[object Object]1或 NaN。症状是页面显示异常值。 - 在
setup顶层用this:组合式 API 里没有this,this.count是 undefined。症状是点击报错「Cannot read properties of undefined」。 - 把方法写成
@click="increment()":多数场景没问题,但它会在渲染时立即执行一次(模板表达式求值),且无法拿到事件对象。规范写法是不带括号传函数引用。 - 改了
main.js的挂载点但没改index.html:mount("#app")和<div id="app">必须对应,否则控制台报「Failed to mount app: mount target selector returned null」。
检查与复盘
- 能不看文档从零建项目并跑起
npm run dev - 能画出「点击 → ref 更新 → 模板重渲染」的完整链路
- 能解释为什么两个计数器实例互不影响,以及这对应 React 的什么行为
参考资料
- Vue 官方文档「快速上手」:
create-vue各选项的含义 - Vue 官方文档「响应式基础」:ref 的读写与模板解包规则
- Vue 官方文档「事件处理」:
v-on与内联语句的写法 - Vite 官方文档:dev / build / preview 三个命令的区别
读完这一课了?
标记完成后,工作台会安排接下来的学习。