Vue 快速入门 ⭐
了解 Vue 并搭建开发环境
学习目标
- 理解 Vue 是什么及核心特点
- 搭建 Vue 开发环境
- 创建第一个 Vue 组件
- 掌握模板语法、条件渲染、列表渲染和事件处理
生活化比喻
Vue 就像"智能装修队":
比喻对应:
┌──────────────────────────────────────────────────────┐
│ 智能装修队 │
├──────────────────────────────────────────────────────┤
│ │
│ 传统 JS = 自己动手装修 │
│ ───────────── │
│ 你要自己量尺寸、买材料、刷漆、接线 │
│ 每一步都要手动操作 │
│ │
│ Vue = 请装修队 + 智能家居 │
│ ───────────── │
│ 你只说"我要什么样的"(声明式) │
│ 装修队自动帮你搞定所有细节 │
│ │
│ 响应式 = 智能感应灯 │
│ ───────────── │
│ 人来了自动亮,人走了自动灭 │
│ 你不需要手动开关 │
│ │
│ 组件 = 预制模块 │
│ ───────────── │
│ 厨房模块、卫生间模块、卧室模块 │
│ 每个模块独立,可以重复使用 │
│ │
│ 单文件组件 = 一个房间的完整设计图 │
│ ───────────── │
│ 结构(HTML)、样式(CSS)、功能(JS) 都在一张图上 │
│ │
└──────────────────────────────────────────────────────┘L1 理解层:会用
Vue 基础
最简示例:
vue
<script setup>
import { ref } from 'vue'
const message = ref('Hello Vue!')
</script>
<template>
<h1>{{ message }}</h1>
<button @click="message = 'Clicked!'">点击</button>
</template>详细示例:
vue
<!-- src/main.js — 入口文件 -->
<script>
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
</script>vue
<!-- src/App.vue — 单文件组件 -->
<script setup>
import { ref } from 'vue'
const message = ref('Hello Vue!')
const count = ref(0)
const handleClick = () => { count.value++ }
</script>
<template>
<div class="app">
<h1>{{ message }}</h1>
<p>点击次数:{{ count }}</p>
<button @click="handleClick">+1</button>
</div>
</template>
<style scoped>
.app { padding: 20px; }
h1 { color: #42b883; }
</style>模板语法
语法结构图:
模板语法:
{{ 表达式 }} → 文本插值
v-bind:attr="值" → 属性绑定(简写 :attr)
v-on:click="方法" → 事件绑定(简写 @click)
v-model="数据" → 双向绑定
v-if / v-else / v-show → 条件渲染
v-for="item in 列表" → 列表渲染最简示例(1-3行):
vue
<template>
<h1>{{ title }}</h1>
<img :src="url" @click="handleClick">
<input v-model="message">
</template>详细示例:
vue
<template>
<div>
<!-- 插值:支持表达式 -->
<p>{{ count + 1 }}</p>
<p>{{ message.toUpperCase() }}</p>
<p>{{ isActive ? '开' : '关' }}</p>
<!-- 属性绑定 -->
<img :src="imageUrl" :alt="title" />
<div :class="{ active: isActive }">内容</div>
<div :style="{ color: textColor }">内容</div>
<!-- 双向绑定 -->
<input v-model="message" />
<input v-model.number="age" /> <!-- 自动转数字 -->
<input v-model.trim="text" /> <!-- 自动去空格 -->
<!-- 条件渲染 -->
<div v-if="type === 'A'">A</div>
<div v-else-if="type === 'B'">B</div>
<div v-else>其他</div>
<div v-show="isVisible">显示/隐藏</div>
<!-- 列表渲染 -->
<li v-for="item in items" :key="item.id">
{{ item.name }}
</li>
</div>
</template>
<script setup>
import { ref } from 'vue'
const count = ref(5)
const message = ref('hello')
const isActive = ref(true)
const imageUrl = ref('photo.jpg')
const type = ref('A')
const isVisible = ref(true)
const items = ref([
{ id: 1, name: '苹果' },
{ id: 2, name: '香蕉' }
])
</script>事件处理
最简示例:
vue
<template>
<button @click="count++">{{ count }}</button>
<form @submit.prevent="handleSubmit">
<button type="submit">提交</button>
</form>
</template>详细示例:
vue
<template>
<div>
<!-- 内联处理 -->
<button @click="count++">+1</button>
<!-- 方法调用 -->
<button @click="handleClick('参数')">点击</button>
<!-- 访问事件对象 -->
<button @click="handleEvent($event)">点击</button>
<!-- 常用修饰符 -->
<form @submit.prevent="onSubmit"></form> <!-- 阻止默认 -->
<div @click.stop="onClick"></div> <!-- 阻止冒泡 -->
<button @click.once="onClick"></button> <!-- 只一次 -->
<input @keyup.enter="onEnter" /> <!-- 回车键 -->
</div>
</template>L2 实践层:用好
v-if vs v-show 选择
| 特性 | v-if | v-show |
|---|---|---|
| 渲染方式 | 条件为真才创建 | 总是创建,切换 display |
| 切换开销 | 高 | 低 |
| 适用场景 | 条件很少改变 | 频繁切换 |
反模式:不要这样做
vue
<!-- ❌ 错误:用 index 作为 key(列表顺序变化时出问题) -->
<li v-for="(item, index) in items" :key="index">
<!-- ✅ 正确:用唯一 ID -->
<li v-for="item in items" :key="item.id">vue
<!-- ❌ 错误:v-if 和 v-for 在同一元素上 -->
<li v-for="item in items" v-if="item.active">
<!-- ✅ 正确:用 computed 过滤或嵌套元素 -->
<template v-for="item in items" :key="item.id">
<li v-if="item.active">{{ item.name }}</li>
</template>适用场景
| 场景 | 推荐方案 | 原因 |
|---|---|---|
| 表单输入 | v-model | 双向绑定最简洁 |
| 列表渲染 | v-for + 唯一 key | 高效更新 DOM |
| 偶尔显示/隐藏 | v-if | 减少初始渲染 |
| 频繁切换 | v-show | CSS 切换开销低 |
| 复杂属性绑定 | :class / :style | 动态样式 |
L3 专家层:深入
响应式系统原理
Vue 响应式工作流程:
1. 数据层:ref/reactive 通过 Proxy 拦截读写
2. 依赖收集:模板渲染时自动收集使用了哪些数据
3. 触发更新:数据变化时,重新渲染使用它的部分
响应式链:
ref(0) → Proxy 代理 → 收集依赖 → 变化 → 触发更新 → 重新渲染知识关联
Vue 快速入门关联:
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ 环境搭建 │────→│ 模板语法 │────→│ 事件处理 │
│ Vite/ │ │ 插值/ │ │ @click/ │
│ SFC │ │ 绑定/ │ │ 修饰符 │
└─────────────┘ │ 条件/ │ └─────────────┘
│ 列表 │
└─────────────┘
│
↓
┌─────────────┐
│ 双向绑定 │
│ v-model │
└─────────────┘术语定义
| 术语 | 定义 | 示例 |
|---|---|---|
| SFC | 单文件组件,.vue 文件,包含 template/script/style | App.vue |
| 响应式 | 数据变化时视图自动更新 | ref(0) |
| 插值 | 在模板中嵌入 JavaScript 表达式 | 双花括号语法 |
| 指令 | 以 v- 开头的特殊属性 | v-if, v-for, v-model |
| 事件修饰符 | 改变事件行为的后缀 | @click.stop, @submit.prevent |
| 声明式 | 描述"要什么"而非"怎么做" | <div v-if="show"> |
实践练习
练习:Todo 列表
vue
<script setup>
import { ref } from 'vue'
const todos = ref([])
const newTodo = ref('')
const addTodo = () => {
if (newTodo.value.trim()) {
todos.value.push({
id: Date.now(),
text: newTodo.value,
done: false
})
newTodo.value = ''
}
}
const removeTodo = (id) => {
todos.value = todos.value.filter(t => t.id !== id)
}
const remaining = () => todos.value.filter(t => !t.done).length
</script>
<template>
<div>
<h1>Todo 列表</h1>
<form @submit.prevent="addTodo">
<input v-model.trim="newTodo" placeholder="输入任务..." />
<button type="submit">添加</button>
</form>
<ul>
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" v-model="todo.done" />
<span :class="{ done: todo.done }">{{ todo.text }}</span>
<button @click="removeTodo(todo.id)">删除</button>
</li>
</ul>
<p v-if="todos.length">剩余:{{ remaining() }}</p>
</div>
</template>
<style scoped>
.done { text-decoration: line-through; color: #999; }
</style>常见问题
Q1:Vue 2 和 Vue 3 有什么区别?
| 特性 | Vue 2 | Vue 3 |
|---|---|---|
| 响应式 | Object.defineProperty | Proxy |
| API | Options API | Composition API(推荐) |
| TS 支持 | 一般 | 优秀 |
| 性能 | 基准 | 提升 1.5-2x |
新项目直接用 Vue 3 + Composition API + <script setup>。
Q2:为什么 v-for 需要 key?
key 帮助 Vue 识别哪些元素变了,从而最小化 DOM 操作:
vue
<!-- ❌ 用 index 做 key,列表重排时会出错 -->
<li v-for="(item, i) in items" :key="i">
<!-- ✅ 用唯一 ID -->
<li v-for="item in items" :key="item.id">Q3:ref 和 reactive 选哪个?
推荐优先用 ref,统一且灵活:
vue
<!-- ref:统一用 .value,可整体替换 -->
const count = ref(0)
count.value++
<!-- reactive:直接访问,但不能整体替换 -->
const user = reactive({ name: '张三' })
user.name = '李四' // ✅
user = { name: '王五' } // ❌ 丢失响应式学习资源
- Vue 官方文档 ⭐ 必读
- Vite 官方文档
- Vue Playground - 在线练习