Vue3 核心语法
Vue3 核心语法围绕响应式系统、组件化开发和组合式 API 创建,通过 <script setup>
简化模板编写,利用 ref
和 reactive
实现数据的响应式绑定,借助 computed
和 watch
处理派生数据和副作用。此外,Vue3 引入了更强大的生命周期钩子,大幅提升了开发效率和应用性能。
OptionsAPI 与 CompositionAPI
Vue2
的API
设计是Options
(配置)风格的Vue3
的API
设计师Composition
(组合)风格的
Options API
用于 Vue2 及之前的版本,通常用于组织 Vue 组件的代码。Options API
将一个组件分割成了几个部分,如data
、computed
、methods
、watch
等,使得组件的逻辑可以分而治之,易于理解和维护。但随着项目的复杂度增加,Options API
逐渐显示出一些不足之处,如代码重复、逻辑难以复用等。
Composition API
是 Vue3 引入的全新特性,它使用一种全新的方式来组织 Vue 组件的代码。与 Options API
相比,Composition API
更加灵活和可复用,可以将相关逻辑集中在一起,实现更好的代码组织和逻辑封装。通过 Composition API
,我们可以更容易地实现代码复用和组件解耦,提高了组件的可维护性和可读性。
setup
学习 Vue3
中的第一步。组合式 API
需要写进 setup
里面,组件中所用到的:数据、方法、计算属性、监视等,均配置在 setup
中。
Vue3 的 setup 和 正常写 data、methods 有什么区别呢?
1、data 和 methods 可以和 setup 同时存在
2、data 和 methods 可以读取 setup 里面的数据,需使用
this
关键字
1 | <script setup lang='ts'> |
响应式框架
vue2 的响应式数据
1 | // Vue2 |
Vue3 有两种方式:ref
和 reactive
ref 定义基本类型响应式数据
定义响应式变量,哪个是响应式就给哪个加
1 | let name = ref('张三') |
在方法里面需要添加 .value
1 | function changeName() { |
reactive 定义对象类型的响应式数据
定义对象类型的响应式数据
1 | import {reactive} from 'vue' |
ref 定义对象类型的响应式数据
ref
也可以定义对象类型的响应式数据,ref
遇到对象类型时,底层找的是 reactive
。
1 | import {ref} from 'vue' |
ref 对比 reactive
ref
创建的对象必须使用.value
(可以使用volar
插件自动添加.value
)reactive
重新分配一个新对象,会失去响应式(可以使用Object.assign
去整体替换)
1 | let car = reactive({brand: '奔驰', price: 100}) |
- 若需要一个基本类型的响应式数据,必须使用
ref
- 若需要一个响应式对象,层级不深,
ref
和reactive
都可以 - 若需要一个响应式对象,且层级较深,推荐使用
reactive
toRefs 和 toRef
toRef
:复制 Reactive 里的单个属性并转换成ref
toRefs
:复制 Reactive 里的所有属性并转换成ref
1 | import {reactive, toRefs} |
toRefs
的作用是把 Reactive 对象转换为 Ref 类型的响应式数据,解构数据使其具备响应式能力。
应用场景:移动鼠标并实时显示鼠标的位置
1 | import { reactive, toRefs } from 'vue' |
案例中将提前封装好的 usePosition
函数通过 toRefs
返回一个响应式数据,然后直接拿来就用。在需要的地方引入进来即可,无需再重复声明。
计算属性
当我们需要将模板中的某一个数据进行一系列处理后得到一个新的值,虽然 Vue 的模板能够支持我们写一些表达式,但这样会使我们的模板变得更臃肿且不够灵活定制化。因此,Vue 推荐使用计算属性(Computed)
来描述响应式状态的复杂逻辑。
Computed()
接受一个getter 函数
,返回一个只读的响应式 ref 对象
。
1 | import {ref, computed} from 'vue' |
计算属性所依赖的数据发生变化它就重新计算。
计算属性是有缓存的,方法是没有缓存的
Watch
- 作用:监视数据的变化。
- 只能监视以下四种数据:
ref
定义的数据reactive
定义的数据- 函数返回一个值
- 一个包含上述内容的数组
ref
监视代码:
1 | import {ref,watch} from 'vue' |
reactive
监视代码:默认是深度监视
1 | import {reactive,watch} from 'vue' |
ref
或reactive
定义的【对象类型】数据中的某个属性
getter
函数:能放回一个值的函数
若该属性不是对象类型,则需要写成 函数
形式
若该属性依然是对象类型,建议写成 函数
形式
1 | import {reactive, watch} from 'vue' |
- 监视上述多个数据
1 | watch([()=>person.name, ()=>person.car.c1], (newValue, oldValue)=>{ |
回调函数
在 Vue3
的组合式 API 中,回调函数 ()=>{}
是 JavaScript
的箭头函数语法,用于定义匿名函数。这些函数可以在各种上下文中使用,比如事件处理、定时器、watch
监听器等。watch
函数的回调是用来响应被监视数据的变化。
1 | watch(() => person.name, () => { |
watch
的第一个参数- 第一个参数
()=>person.name
是一个箭头函数,它没有参数列表(用空括号表示),并且直接返回person.name
的当前值 - 这个函数会在每次
person.name
变化时被watch
调用来获取最新的值
- 第一个参数
watch
的第二个参数watch
的第二个参数是当被监视的数据发生变化时将执行的回调函数。这个回调函数接收两个参数:新的值和旧的值(如果你需要访问旧的值的话)。在例子中,回调函数并没有显式地声明参数,而是使用空的参数列表()
并且只是简单打印了一条消息到控制台。- 如果想访问新旧值,可以这么写:
1 | (newValue, oldValue) => { |
每当 person.name
发生变化时,相应的 watch
监听器就会触发,并执行对应的回调函数。这使得你可以对这些变化作出反应,比如更新用户界面、发送网络请求、记录日志等。
- 箭头函数
()=>{}
在这里用作watch
的参数,分别作为获取被监视数据的getter
和当数据变化时执行的回调 - 回调函数允许你在数据变化时执行特定的逻辑,而不需要明确知道是什么导致了变化
- 使用
deep:true
可以确保即使对象内部的属性发生变化,也会触发监听器。
WatchEffect
响应式追踪其依赖
需求:当水温达到 60,或水位达到 80 时,向服务器发送请求
- 使用 Watch 监视
1 | watch([temp, height], (value)=>{ |
- 使用 WatchEffect 监视
1 | watchEffect(()=>{ |
标签的 ref 属性
引用 <template>
里面的标签,用于注册模板引用
1 | let title2 = ref() |
- 用在普通
DOM
标签上,获取的是DOM
节点 - 用在组件标签上,获取的是组件实例对象
接口(Interfaces)和类型(Type)
在 TypeScript 中,接口用于定义对象的形状(shape),即对象的属性和数据类型。接口可以保证对象符合特定的结构,可以用于代码提示和错误检查。
除了接口,TypeScript 还提供了类型别名(type aliases),它可以用于定义新的类型名称,包括原始类型、联合类型、元组、对象类型等。
以下的代码定义了一个名为 PersonInter
的接口和一个名为 Persons
的类型别名。
接口:有
id
、name
、age
三个属性和对应的数据类型,使用export
关键字将接口导出,使得它可以被其它模块导入和使用。类型别名:将
Persons
定义为PersonInter
对象的数组,一个或包含多个PersonInter
对象的数组类型,适用于表示多个人的信息,同样使用export
关键字将类型别名导出,以便在其它模块中使用。
1 | // 定义一个接口,用于限制 person 对象的具体属性 |
1 | import {type PersonInter} from '@/types' |
Props
在 Vue3 中,Props
是用于从父组件向子组件传递数据的机制。子组件通过 defineProps
来声明它期望接收的 props,并且可以对这些 props 进行类型检查、设置默认值和指定是否为必填项。
defineProps
:用于声明组件期望接收的 props。可以通过数组或对象的形式指定 props 的名称和类型withDefaults
:用于为 props 设置默认值,特别是当 props 是可选时特别好用- 类型:通过 TypeScript 的类型系统,可以确保传递给组件的 props 符合预期的类型,减少运行时错误
- 响应式:使用
reactive
创建的personList
是响应式的,当它发生变化时,依赖它的组件会自动更新
父组件
1 | <!-- APP.vue --> |
- 自定义一个子组件
<Person>
a="哈哈"
:传递一个名为a
的 prop,其值为字符串"哈哈"
:list="personList"
:传递一个名为list
的 prop,其值为personList
变量的内容。注意这里的冒号:
表示使用 v-bind 指令,即动态绑定属性。
定义 personList
变量
1 | let personList = reactive<Persons>)([ |
定义一个 Persons 类型的数组,关于 Persons 类型,在上一节的接口和类型中有定义,Persons
是 PersonInter
类型的别名,因此,personList
是由多个 PersonInter
对象组成的数组。
子组件
- 方法 1:只接收
list
1 | // 只接收 list |
- 方法 2:接收
list
并限制类型
1 | // 接收 List + 限制类型 |
- 方法 3:接收
list
并限制类型、必要性及默认值
1 | // 接收 list + 限制类型 + 限制必要性 + 指定默认值 |
- 方法 4:接收多个 props 并打印
1 | let x = defineProps(['a', 'list']) |
生命周期
Vue3 的生命周期是组件从创建到销毁过程中的一系列钩子函数,这些钩子函数允许开发者在组件的不同阶段执行特定的操作。
- 生命周期、生命周期函数、生命周期钩子;
- 引入了
setup
函数作为新的入口点,允许开发者更灵活地组织逻辑; - 生命周期钩子需要通过导入相应的方法来使用,如
onMounted
、onUnmounted
等; - 提供了更好的逻辑复用机制(Composables),可以将相关逻辑封装成独立的函数,提升代码的可维护性和可测试性。
创建
使用 setup
模拟,代替了 Vue2 中的 beforeCreate
和 Created
钩子。setup
函数在组件实例创建后立即被调用,此时可以访问到组件的响应式状态。
1 | console.log('创建') |
挂载
onBeforeMount()
:在组件挂载前被调用,此时模板已编译,但尚未插入到 DOM 中。
1 | onBeforeMount(()=>{ |
onMounted()
:在组件被挂载到 DOM 后立即调用,此时可以安全地进行 DOM 操作
1 | onMount(()=>{ |
更新
onBeforeUpdate()
:在数据更新后,DOM 更新之前被调用,此时可以访问旧的 DOM 状态。
1 | onBeforeUpdate(()=>{ |
onUpdated()
:在组件更新后被调用。此时 DOM 已完成更新
1 | onUpdated(()=>{ |
卸载
onBeforeUnmount()
:在组件实例被卸载之前调用,此时组件仍然可以访问。
1 | onBeforeUnmount(()=>{ |
onUnmounted()
:在组件实例被卸载后调用,此时组件的所有指令与事件监听器已被解绑。
1 | onUnmounted(()=>{ |
hooks
“Hooks” 通常指的是 组合式API(Composition API) 中的函数, 它们用于封装和复用有状态逻辑。在 Vue3 中,Hooks 是指通过 setup
函数或自定义组合函数(Composables)封装的逻辑,这些逻辑可以包含响应式状态、计算属性、生命周期钩子、事件处理等。Hooks 的主要目的是将相关的逻辑提取到独立的函数中,使得代码更加模块化、可复用,并且更容易维护。
自定义组合函数是 Vue3 中最常用的 Hook 形式。通常是前缀带有 use
的函数(如 useMouse
、useFetch
等),用于封装特定的逻辑,并返回需要暴露的状态或方法。
示例:useDogs
函数
1 | // src/hooks/useDogImages.ts |
接下来,创建一个 Vue 组件来使用 useDogImages
组合函数,并在模板中添加按钮和图片展示区域
1 | <template> |