Vue3核心语法超详细入门,一篇搞懂 setup、ref、reactive、watch、props、生命周期、hook
大家好这篇文章我们正式进入Vue3 最核心的一章核心语法。如果说前面创建工程只是“把环境搭起来”那么从这一章开始才算真正进入 Vue3 的开发世界。尤其是setup、ref、reactive、computed、watch、props、生命周期这些内容几乎就是以后写 Vue3 项目每天都要打交道的东西。这篇文章我会尽量按照“是什么 → 为什么 → 怎么用 → 注意什么”的思路来讲争取让你不仅会写还能讲明白。一、Options API 与 Composition API在 Vue2 里我们最熟悉的是Options API也就是把代码分别写到datamethodscomputedwatch这些配置项里面。而到了 Vue3官方更推荐使用Composition API组合式 API。1.1 Options API 的问题Options API 最大的问题不是不能用而是同一个功能的代码会被拆散。比如一个“用户信息模块”它的数据写在data修改逻辑写在methods派生结果写在computed监听逻辑写在watch这样项目一大维护起来就会比较痛苦。你想改一个功能经常要在多个地方来回跳。1.2 Composition API 的优势Composition API 的核心思想就是把同一个功能相关的代码组织到一起。这样一来代码逻辑会更集中更适合大型项目也更方便复用。你可以把它理解成Vue2 更像“按类型分类”Vue3 更像“按功能分类”这就是 Vue3 为什么越来越适合中大型项目开发的原因之一。二、setupVue3 组合式 API 的舞台在 Vue3 中setup是非常重要的一个配置项。可以说组合式 API 的大部分内容都是围绕它展开的。2.1 setup 是什么setup本质上是一个函数。组件中用到的数据方法计算属性监视器生命周期钩子都可以写在setup里面。2.2 setup 的几个特点第一setup 返回的内容可以直接给模板用template h2{{ name }}/h2 button clickchangeName修改名字/button /template script langts export default { setup() { let name 张三 function changeName() { console.log(修改名字) } return { name, changeName } } } /script这里return出去的name和changeName模板都能直接用。第二在 setup 中拿不到 thisconsole.log(this) // undefined这是很多初学者最不习惯的地方。因为在组合式 API 里已经不再依赖this去访问组件实例而是更直接地使用变量本身。第三setup 执行得很早setup会在beforeCreate之前执行所以它几乎是组件初始化阶段最早触发的逻辑。三、为什么 setup 里直接写变量页面却不更新先看一个例子template div h2姓名{{ name }}/h2 button clickchangeName修改名字/button /div /template script langts export default { setup() { let name 张三 function changeName() { name 李四 console.log(name) } return { name, changeName } } } /script点击按钮后控制台会输出新值但页面不会变。为什么因为这里的name只是普通变量不是响应式数据。这也就引出了 Vue3 最重要的两个响应式 APIrefreactive四、ref创建基本类型的响应式数据4.1 ref 的作用ref用来定义响应式变量。最常见的是处理字符串数字布尔值等基本类型数据。4.2 基本写法script setup langts import { ref } from vue let name ref(张三) let age ref(18) function changeName() { name.value 李四 } function changeAge() { age.value 1 } /script4.3 必须记住的点在 JS 里操作 ref要加.valuename.value 王五 age.value在模板里不用写.valueh2{{ name }}/h2 h2{{ age }}/h2Vue 会自动帮你解包。4.4 ref 的本质理解对于这句代码let name ref(张三)真正具有响应式能力的不是name这个变量本身而是name.value所以你要理解成name是一个包裹响应式值的对象。五、reactive创建对象类型的响应式数据5.1 reactive 的作用reactive专门用来处理对象类型数据比如普通对象数组多层嵌套对象而且它默认是深层响应式。5.2 基本写法script setup langts import { reactive } from vue let car reactive({ brand: 奔驰, price: 100 }) let games reactive([ { id: 1, name: 英雄联盟 }, { id: 2, name: 王者荣耀 } ]) function changeCarPrice() { car.price 10 } function changeFirstGame() { games[0].name 原神 } /script5.3 为什么 reactive 很方便因为用它定义出来的数据直接改属性就行car.price 10 games[0].name 流星蝴蝶剑不需要.value。六、ref 和 reactive 到底怎么选这是 Vue3 学习里特别高频的问题。资料中给出的结论很清晰6.1 宏观结论ref既可以定义基本类型也可以定义对象类型reactive主要定义对象类型6.2 主要区别ref操作时通常需要.value既能包基本类型也能包对象reactive使用起来更像普通对象重新整体赋值时容易丢失响应式比如下面这种写法就不推荐person { name: 李四, age: 20 }因为会把原来的响应式对象直接替换掉。这时候更推荐Object.assign(person, { name: 李四, age: 20 })6.3 实战建议用 ref 的场景数字计数器字符串输入框布尔开关状态用 reactive 的场景表单对象列表数组配置对象多层嵌套数据一句话总结简单值优先 ref对象结构优先 reactive。七、toRefs 与 toRef把对象属性拆出来还能保持响应式这个知识点在实际开发里非常常用。7.1 为什么需要它假设有这样一个响应式对象let person reactive({ name: 张三, age: 18, gender: 男 })如果你直接解构let { name, age } person那这两个变量就失去响应式了。这时候就要用toRefstoRef7.2 示例script setup langts import { reactive, toRefs, toRef } from vue let person reactive({ name: 张三, age: 18, gender: 男 }) let { name, gender } toRefs(person) let age toRef(person, age) function changeName() { name.value ~ } function changeAge() { age.value 1 } /script7.3 区别toRefs(person)批量把所有属性转成 reftoRef(person, age)只取某一个属性这两个 API 的意义就是既想拆开用又不想丢响应式。八、computed计算属性8.1 computed 是干什么的它的作用是根据已有数据计算出一个新数据。最经典的例子就是“姓 名 全名”。8.2 只读写法script setup langts import { ref, computed } from vue let firstName ref(zhang) let lastName ref(san) let fullName computed(() { return firstName.value - lastName.value }) /script8.3 可读可写写法script setup langts import { ref, computed } from vue let firstName ref(zhang) let lastName ref(san) let fullName computed({ get() { return firstName.value - lastName.value }, set(val) { firstName.value val.split(-)[0] lastName.value val.split(-)[1] } }) function changeFullName() { fullName.value li-si } /script8.4 computed 和 methods 的区别很多同学会问这不就是函数吗为什么还要 computed因为 computed 有缓存。只要依赖没变它就不会重复计算。而 methods 每次模板渲染都会重新执行。所以有明确依赖关系的派生值→ 用computed普通函数逻辑→ 用methods/ 普通函数九、watch精准监视数据变化watch的作用是监视响应式数据的变化并在变化时执行回调。Vue3 中watch可以监视这几类东西ref定义的数据reactive定义的数据getter 函数上述内容组成的数组十、watch 的五种典型写法这一部分是重点也是面试和开发最常见的内容。10.1 情况一监视 ref 定义的基本类型script setup langts import { ref, watch } from vue let sum ref(0) watch(sum, (newValue, oldValue) { console.log(sum变化了, newValue, oldValue) }) /script直接写sum就行因为它监视的是sum.value的变化。10.2 情况二监视 ref 定义的对象类型script setup langts import { ref, watch } from vue let person ref({ name: 张三, age: 18 }) watch(person, (newValue, oldValue) { console.log(person变化了, newValue, oldValue) }, { deep: true }) /script这里注意直接监视person默认主要看地址值变化如果还想监视对象内部属性变化要加{ deep: true }另外如果改的是内部属性newValue和oldValue往往是同一个对象引用所以看起来可能“都一样”。10.3 情况三监视 reactive 对象script setup langts import { reactive, watch } from vue let person reactive({ name: 张三, age: 18 }) watch(person, (newValue, oldValue) { console.log(person变化了, newValue, oldValue) }) /scriptreactive对象的监视默认就是深度监视。不过这里也要注意一个点newValue 和 oldValue 往往还是同一个对象引用。所以很多时候你更适合监视具体属性而不是整个对象。10.4 情况四监视对象中的某个属性这是开发中最常见的写法。watch(() person.name, (newValue, oldValue) { console.log(name变化了, newValue, oldValue) })为什么要写成函数因为这样才是在“告诉 Vue我要监视这个属性的值”。如果监视的是对象属性而且属性本身又是对象也推荐写函数watch(() person.car, (newValue, oldValue) { console.log(car变化了, newValue, oldValue) }, { deep: true })10.5 情况五同时监视多个数据watch( [() person.name, person.car], (newValue, oldValue) { console.log(多个数据变化了, newValue, oldValue) }, { deep: true } )这在项目中很有用尤其适合多条件联动组合表单校验多字段变化后统一处理逻辑十一、watchEffect自动收集依赖的监听器11.1 它是什么watchEffect会立即执行一次函数并自动追踪函数里用到的响应式数据。只要这些依赖变了它就会重新执行。11.2 和 watch 的区别watch你得明确写出“我要监视谁”watchEffect你不用写函数里用到了谁它就跟踪谁11.3 示例script setup langts import { ref, watchEffect } from vue let temp ref(0) let height ref(0) const stopWatch watchEffect(() { if (temp.value 50 || height.value 20) { console.log(联系服务器) } if (temp.value 100 || height.value 50) { stopWatch() } }) /script11.4 什么时候用它当你的副作用逻辑依赖多个响应式变量而且不想一个个手写监听源时watchEffect非常省事。比如自动请求接口联动 DOM 操作自动同步缓存十二、标签上的 ref获取 DOM 或组件实例资料里这里说的是“标签的 ref 属性”。12.1 用在普通 DOM 上template h1 reftitle1尚硅谷/h1 button clickshowLog点我打印/button /template script setup langts import { ref } from vue let title1 ref() function showLog() { console.log(title1.value) } /script此时拿到的是 DOM 节点。12.2 用在组件上Person refren /此时拿到的是组件实例。但在script setup语法下子组件内部默认不会把所有内容暴露给父组件所以要用defineExpose({ name, age })这样父组件才能通过ren.value.name去访问。十三、props父子组件通信的基础props是 Vue 组件通信中使用频率最高的一种方式。13.1 父传子父组件Person :listpersons /子组件script setup langts defineProps([list]) /script13.2 TypeScript 下更规范的写法defineProps{ list: Persons }()甚至还能配默认值let props withDefaults(defineProps{ list?: Persons }(), { list: () [{ id: 1, name: 小猪佩奇, age: 18 }] })这类写法在 Vue3 TS 项目里非常常见。13.3 props 的本质理解父组件把数据传给子组件子组件通过props接收。所以你可以把它理解成父组件给子组件“传参”。十四、生命周期组件从出生到销毁会经历什么Vue 组件不是一加载就完事它其实会经历一整套过程创建挂载更新卸载这些关键阶段里Vue 会在合适的时机调用某些函数这些函数就是生命周期钩子。14.1 Vue3 常见生命周期在 Vue3 中setuponBeforeMountonMountedonBeforeUpdateonUpdatedonBeforeUnmountonUnmounted是最常见的钩子。14.2 常用的几个onMounted组件挂载完成后执行常用于发请求获取 DOM初始化第三方库onUpdated组件更新完成后执行常用于更新后读取最新 DOMonBeforeUnmount组件销毁前执行常用于清除定时器解绑事件销毁实例14.3 示例script setup langts import { ref, onBeforeMount, onMounted, onBeforeUpdate, onUpdated, onBeforeUnmount, onUnmounted } from vue let sum ref(0) function changeSum() { sum.value 1 } console.log(setup) onBeforeMount(() { console.log(挂载之前) }) onMounted(() { console.log(挂载完毕) }) onBeforeUpdate(() { console.log(更新之前) }) onUpdated(() { console.log(更新完毕) }) onBeforeUnmount(() { console.log(卸载之前) }) onUnmounted(() { console.log(卸载完毕) }) /script十五、自定义 hook把组合式逻辑封装起来复用15.1 什么是 hook在 Vue3 里hook 本质上就是把 setup 中可复用的逻辑提取到一个函数里。这和 Vue2 里的mixin有点像但 hook 更清晰、更灵活。15.2 为什么要封装 hook因为真实项目里很多逻辑不是只写一次计数逻辑请求逻辑表单逻辑分页逻辑鼠标拖拽逻辑都可能在多个组件里复用。15.3 示例useSumimport { ref, onMounted } from vue export default function () { let sum ref(0) const increment () { sum.value 1 } const decrement () { sum.value - 1 } onMounted(() { increment() }) return { sum, increment, decrement } }15.4 组件中使用script setup langts import useSum from ./hooks/useSum let { sum, increment, decrement } useSum() /script15.5 hook 的核心价值一句话把逻辑抽出去组件里只负责“用”。这样组件会更轻结构更清楚也更利于维护。十六、本章重点总结这一章内容很多但你真正要抓住的核心其实就这几个1.setup 是 Vue3 组合式 API 的入口以后大部分逻辑都写在这里。2.ref 和 reactive 是响应式核心基本类型常用ref对象类型常用reactive3.computed 用来做派生数据适合“根据已有值算出新值”。4.watch 和 watchEffect 用来监听变化watch更精准watchEffect更自动5.props 是父传子的基础方式组件通信必须会。6.生命周期钩子要会用尤其是onMounted、onUpdated、onBeforeUnmount。7.hook 是 Vue3 项目进阶能力会封装代码就会越来越像真正的工程项目。十七、最后说几句建议你把下面这些内容一定亲手敲一遍setuprefreactivecomputedwatchwatchEffectprops生命周期自定义 hook因为这些东西不是“看会”的是“敲会”的。