3. Vue3 核心基础(一):Composition API、模板语法、事件处理
好,我们正式开始 Vue3 的核心内容。这一章我打算把 Composition API 的入门、模板语法和事件处理串起来讲。为什么这么安排?因为你在写实时指标看板时,这三样东西几乎是同时用的——你总得定义数据、把数据渲染到页面上、再让用户能交互吧?
我个人习惯把 Vue3 的学习路径分成两条线:一条是「怎么写代码」,另一条是「为什么这么写」。这一章我们重点解决前者,先把手脚动起来。
3.1 Composition API 的起点:setup 函数
在 Vue2 里,我们写 data、methods、computed 是分开的选项。到了 Vue3,Composition API 把这些逻辑都收进了 setup 函数里。说白了,setup 就是组件的「入口函数」,它在组件创建之前执行,你在这里定义的所有东西,都要通过 return 暴露给模板使用。
核心要点:setup 函数在 beforeCreate 之前执行,此时组件实例还没完全创建好。所以你不能用 this。
来看一个最简单的例子:
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<p>{{ count }}</p>
</template>
嗯,这里要注意:<script setup> 是语法糖,它让 setup 函数里的顶层变量自动暴露给模板,省去了手动 return 的麻烦。我在项目中几乎全用这种写法,代码更干净。
3.2 ref 与 reactive:响应式数据的两种姿势
Vue3 的响应式核心就两个 API:ref 和 reactive。很多新手会纠结「到底用哪个」,我直接说结论:
| API | 适用场景 | 访问方式 | 注意点 |
|---|---|---|---|
ref |
基本类型(string、number、boolean)或需要重新赋值的对象 | 模板中自动解包,JS 中需 .value |
对象类型内部也是用 reactive 实现的 |
reactive |
深层嵌套的对象,且不需要整体替换 | 直接访问属性,无需 .value |
不能重新赋值整个对象,会丢失响应式 |
我曾经在做一个配置面板时,用 reactive 定义了一个大对象,后来因为要重置整个配置,直接写了 config = {},结果页面不更新了。排查了半天才发现——reactive 对象不能整体替换,得用 Object.assign 或者改用 ref。
避坑指南:如果你用 reactive 定义了一个对象,千万别直接赋值新对象。正确的做法是用 ref 包裹,或者用 Object.assign 逐个属性更新。
来看一个实际对比:
// ref 方式 —— 适合需要整体替换的场景
const user = ref({ name: '张三', age: 25 })
user.value = { name: '李四', age: 30 } // ✅ 没问题
// reactive 方式 —— 适合只修改属性的场景
const config = reactive({ theme: 'dark', fontSize: 14 })
config.theme = 'light' // ✅ 没问题
// config = { theme: 'light', fontSize: 16 } // ❌ 会丢失响应式
3.3 模板语法:{{ }} 与指令系统
Vue3 的模板语法和 Vue2 基本一致,核心就两样:插值表达式 {{ }} 和指令(v-bind、v-for、v-if 等)。
插值表达式里可以写任何 JavaScript 表达式,但不能写语句。比如 {{ count + 1 }} 可以,{{ if (count) { ... } }} 不行。
指令这块,我重点说几个你在看板项目里一定会用到的:
v-bind:动态绑定属性,简写:。比如:class="activeClass"、:style="styleObj"。v-for:列表渲染。注意要加:key,而且 key 最好是唯一 ID,不要用索引。v-if/v-show:条件渲染。频繁切换用v-show,否则用v-if。
我的习惯:在实时看板中,图表容器用 v-show 控制显隐,因为切换频率高;而加载状态、空数据提示用 v-if,因为条件变化不频繁,还能减少 DOM 节点数。
来看一个综合示例:
<template>
<div>
<h1>{{ title }}</h1>
<ul>
<li v-for="item in list" :key="item.id" :class="{ active: item.isActive }">
{{ item.name }}
</li>
</ul>
<p v-if="list.length === 0">暂无数据</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const title = ref('实时指标看板')
const list = ref([
{ id: 1, name: 'CPU 使用率', isActive: true },
{ id: 2, name: '内存占用', isActive: false }
])
</script>
3.4 事件处理:@click 与事件修饰符
事件处理在 Vue3 里用 v-on 指令,简写 @。比如 @click="handleClick"。你可以在方法里直接写逻辑,也可以传参。
这里有个细节:如果你在模板里直接写 @click="count++",Vue3 是支持的,但我建议还是抽成函数,方便维护和测试。
事件修饰符是 Vue 的亮点,能省不少代码:
| 修饰符 | 作用 | 示例 |
|---|---|---|
.stop |
阻止事件冒泡 | @click.stop="fn" |
.prevent |
阻止默认行为 | @submit.prevent="fn" |
.once |
事件只触发一次 | @click.once="fn" |
.enter |
按回车键触发 | @keyup.enter="fn" |
我记得有一次做搜索框,用户按回车要触发搜索,我本来写了 @keyup="handleKeyup",然后在函数里判断 event.key === 'Enter'。后来发现直接用 @keyup.enter 就搞定了,代码少了一半。
<template>
<div>
<button @click="increment">加 1</button>
<button @click.once="showOnce">只触发一次</button>
<input @keyup.enter="search" placeholder="按回车搜索" />
<form @submit.prevent="handleSubmit">
<button type="submit">提交</button>
</form>
</div>
</template>
<script setup>
const increment = () => {
console.log('点击了按钮')
}
const showOnce = () => {
console.log('这个只会打印一次')
}
const search = (e) => {
console.log('搜索内容:', e.target.value)
}
const handleSubmit = () => {
console.log('表单提交了')
}
</script>
3.5 本章知识体系总览
为了让你对本章内容有个整体印象,我画了一张结构图。你可以把它当作学习地图,随时回来对照。
3.6 小结与实战建议
这一章的内容,说白了就是 Vue3 的「三板斧」:用 ref / reactive 定义数据,用模板语法把数据渲染到页面上,用事件处理让页面动起来。你想想看,实时看板里的每一个指标卡片,不都是这样吗?
最后给你三个实战建议:
- 优先用
ref:除非你确定这个对象永远不会被整体替换,否则ref更安全,心智负担也更低。 - 模板里少写逻辑:
{{ }}里只放简单的变量或计算属性,复杂的逻辑抽到computed或函数里。 - 事件修饰符用起来:
.stop、.prevent、.enter这些能让你少写很多event操作代码。