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章:Vue3 核心基础(一)知识体系 Vue3 组件 Composition API setup 函数 ref / reactive 模板语法 {{ 插值 }} v-bind / v-for 事件处理 @click 指令 事件修饰符 💡 三者关系:setup 定义数据 → 模板语法渲染数据 → 事件处理修改数据 形成一个完整的「数据 → 视图 → 交互」闭环

3.6 小结与实战建议

这一章的内容,说白了就是 Vue3 的「三板斧」:用 ref / reactive 定义数据,用模板语法把数据渲染到页面上,用事件处理让页面动起来。你想想看,实时看板里的每一个指标卡片,不都是这样吗?

最后给你三个实战建议:

  • 优先用 ref:除非你确定这个对象永远不会被整体替换,否则 ref 更安全,心智负担也更低。
  • 模板里少写逻辑:{{ }} 里只放简单的变量或计算属性,复杂的逻辑抽到 computed 或函数里。
  • 事件修饰符用起来:.stop、.prevent、.enter 这些能让你少写很多 event 操作代码。