Vue3 组合式 API 深度解析:ref/reactive 响应式,计算属性与侦听器

一、setup 执行时机

1.1 setup 是什么

setup 是组合式 API 的入口,在组件创建时(beforeCreate 之前)执行一次:

ts 复制代码
import { ref } from "vue";

export default {
  props: { title: String },
  setup(props, ctx) {
    const count = ref(0);        // 声明响应式数据
    // props:父组件传入的 props(只读)
    // ctx.emit / ctx.attrs / ctx.slots
    return { count };            // 返回对象暴露给模板
  },
};

1.2 script setup 语法糖

实际项目中几乎都用 <script setup>,无需 return,变量/函数直接可用:

vue 复制代码
<script setup lang="ts">
import { ref } from "vue";
const count = ref(0);
</script>

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

注意setup 中拿不到 this;需要访问组件实例的场景用 getCurrentInstance()


二、ref 与 reactive:两种响应式

2.1 ref:基础类型响应式

ts 复制代码
import { ref } from "vue";

const count = ref(0);       // 返回一个 { value } 对象
count.value = 1;            // JS 中必须 .value 访问

// 模板中自动解包,直接写 count
// <p>{{ count }}</p>

ref 也可以装对象/数组 (内部会用 reactive 代理),但访问仍需 .value

ts 复制代码
const user = ref({ name: "张三" });
user.value.name = "李四";

2.2 reactive:对象响应式

ts 复制代码
import { reactive } from "vue";

const state = reactive({ name: "张三", age: 18 });
state.name = "李四";         // 直接访问属性,无需 .value

2.3 选用区别

对比项 ref reactive
支持类型 任意类型(含基础类型) 仅对象/数组
访问方式 .value(模板自动解包) 直接访问属性
解构 安全(每层是 ref) 解构丢失响应
整体替换 obj.value = newObj obj = newObj ❌ 失效
适用 单值、通用场景 深嵌套对象、集中状态

推荐 :业务代码统一用 ref(心智简单);reactive 适合需要深层次集中管理的对象。


三、computed 计算属性

3.1 只读 computed

ts 复制代码
import { ref, computed } from "vue";

const firstName = ref("张");
const lastName = ref("三");

const fullName = computed(() => firstName.value + lastName.value);
// 依赖变化自动重算,带缓存;只读,不能赋值

3.2 可写 computed(get/set)

ts 复制代码
const fullName = computed({
  get: () => firstName.value + lastName.value,
  set: (val) => {
    const [a, b] = val.split(" ");
    firstName.value = a;
    lastName.value = b;
  },
});

computed 要求同步纯函数;异步或复杂副作用交给 watch。


四、watch 与 watchEffect 侦听器

4.1 watch:显式监听

ts 复制代码
import { ref, watch } from "vue";

const keyword = ref("");

// 监听 ref
watch(keyword, (newVal, oldVal) => {
  console.log(oldVal, "→", newVal);
});

// 监听 reactive 属性(getter 形式)
watch(
  () => state.name,
  (val) => console.log("name 变化:", val)
);

// 监听多个来源
watch([a, b], ([newA, newB]) => {});

// 深度监听
watch(state, (val) => {}, { deep: true });

4.2 watchEffect:自动收集依赖

ts 复制代码
import { watchEffect } from "vue";

// 函数内用到的响应式数据变化都会触发;默认立即执行一次
watchEffect(() => {
  console.log("当前关键词:", keyword.value);   // 自动追踪依赖
});

4.3 两者对比

对比项 watch watchEffect
依赖声明 显式指定 自动收集
立即执行 默认否(加 immediate 默认是
旧值获取
适用 精确监听某数据 副作用与多个数据相关时

五、高频踩坑:响应式失效

现象 解决
reactive 整体赋值 state = newObj 后页面不更新(引用被替换) Object.assign(state, newObj) 或改用 ref
解构 reactive const { name } = state 后修改不响应 toRefs(state) / toRef(state, "name")
ref 在 JS 中忘写 .value 修改无效、类型报错 统一规范:JS 用 .value,模板裸用
watch reactive 不触发 直接监听对象内部属性变化未生效 getter 形式:() => state.namedeep: true
reactive 里塞基础类型 报错/无响应 基础类型用 ref
在函数外解构 模板中变量不更新 组合式函数内返回 toRefs
ts 复制代码
// 正确示例
const { name } = toRefs(state);    // 解构后仍是响应式
state.name = "李四";               // 或 name.value = "李四"

六、简单业务 Demo:搜索防抖

vue 复制代码
<script setup lang="ts">
import { ref, watch } from "vue";

const keyword = ref("");
const results = ref<string[]>([]);
let timer: number | undefined;

watch(keyword, (val) => {
  clearTimeout(timer);
  timer = setTimeout(() => {
    // 模拟请求
    results.value = [`${val} 的搜索结果 1`, `${val} 的搜索结果 2`];
  }, 300);
});
</script>

<template>
  <input v-model="keyword" placeholder="输入搜索关键词" />
  <ul>
    <li v-for="(item, i) in results" :key="i">{{ item }}</li>
  </ul>
</template>

七、总结

  • setup :组件入口,<script setup> 免 return;无 this
  • ref vs reactive:ref 通用(.value + 可整体替换),reactive 只认对象(不可解构、不可整体赋值);
  • computed:同步派生值 + 缓存,可写用 get/set;
  • watch :显式精确监听(可拿旧值、可 deep);watchEffect:自动收集依赖 + 立即执行;
  • 踩坑Object.assign 替代整体赋值、toRefs 保解构响应、watch 用 getter 形式。
相关推荐
万敏1 小时前
Vue3 全栈实战:第一阶段复盘(第1-8周)
vue.js·node.js·全栈
暖焰核心1 小时前
继承全解——继承、默认成员函数、切片、隐藏与虚继承
java·前端·javascript
by组态1 小时前
Ricon组态系统API参考手册
前端·后端·物联网
前端逗比逗1 小时前
AI 前端落地实战:SSE 流式输出、断点续传、打字机渲染
前端·webassembly
晴天162 小时前
前端跨域方案解析:JSONP 的原理、实战与演进
前端·状态模式
web打印社区2 小时前
浏览器静默打印?先别装第三个库了
前端·vue.js·chrome·electron·pdf
艾伦野鸽ggg2 小时前
前端异步请求的状态竞争(请求竞态)问题
前端·javascript·axios
是立不是利2 小时前
前端交互基石:深入剖析JavaScript三级联动背后的设计哲学
开发语言·前端·javascript