Vue3 组合API 的学习之路(一)
- 前言:本文的所有演示示例代码的写法都是以Vue3的setup语法糖来编写的
- Vue3中文官网
1. 生命周期
生命周期钩子函数
1.1 vue3 和 vue2 的生命周期函数对比情况
| Vue2的生命周期函数 | Vue3生命周期函数 | 描述 |
|---|---|---|
beforeCreate | setup | 创建实例前 |
created | setup | 创建实例 |
beforeMount | onBeforeMount | 挂载DOM前 |
mounted | onMounted | 挂载DOM后 |
beforeUpdate | onBeforeUpdate | 更新组件前 |
updated | onUpdated | 更新组件后 |
beforeDestroy | onBeforeUnmount | 卸载销毁前 |
destroyed | onUnmounted | 卸载销毁后 |
1.2 演示示例
<script setup>
import {
onMounted,
onBeforeMount,
onBeforeUpdate,
onUpdated,
onActivated,
onDeactivated,
onBeforeUnmount,
onUnmounted
} from 'vue'
console.log('--- setup')
onBeforeMount(() => {
console.log('--- onBeforeMount')
})
onMounted(() => {
console.log('--- onMounted')
})
onBeforeUpdate(() => {
console.log('--- onBeforeUpdate')
})
onUpdated(() => {
console.log('--- onUpdated')
})
onActivated(() => {
console.log('--- onActivated')
})
onDeactivated(() => {
console.log('--- onDeactivated')
})
onBeforeUnmount(() => {
console.log('--- onBeforeUnmount')
})
onUnmounted(() => {
console.log('--- onUnmounted')
})
</script>
2. setup 函数
作用:
- 组件实例创建前执行
- 函数中不能使用
this --> undefined- 模版中需要使用的数据和函数,需要在setup中返回
2.1 setup 的参数
-
props: 接受父组件传入的通过props声明的属性
-
comtext : 上下文
- attrs : 接受父组件传入的没有通过props声明过的属性
- slots :接收父组件传入的插槽内容的对象
- emit : 用来分发自定义事件的函数
2.2 演示示例
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
icon: {
type: String,
default: ''
}
})
const emit = defineEmits(['click'])
const click = () => {
emit('click', params)
}
</script>
3. reactive 函数
作用:定义响应式函数,通常定义复杂的数据类型,通常用来定义对象
3.1 演示示例
<template>
<div @click="updateName()">{{ obj.name }}</div>
</template>
<script setup>
import { reactive } from 'vue'
const obj = reactive({ name: '定义响应式对象', age: 20 })
const updateName = () => {
obj.name = '更新响应式数据'
}
</script>
4. toRef 函数
作用:转换
响应式对象中的某个属性为单独的响应式数据,并且值是关联的
注意:通过 toref 转换响应式数据包装成了对象,value 存放值的位置
4.1 演示示例
<template>
<div @click="updateName()">{{ name }}</div>
</template>
<script setup>
import { reactive, toRef } from 'vue'
const obj = reactive({ name: '定义响应式对象', age: 20 })
const name = toRef(obj, 'name')
const updateName = () => {
name.value = 'zs'
}
</script>
4.2 使用场景
- 有一个响应式对象数据,但在模块中只需要使用其中一项的数据
5. toRefs 函数
作用:转换
响应式中的所有属性为响应式数据,通常用于解构|展开reactive定义对象
注意:通过 toref 转换响应式数据包装成了对象,value 存放值的位置
5.1 演示示例
<template>
<div @click="updateName()">{{ name }} - {{ age }} </div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
const obj = reactive({ name: '定义响应式对象', age: 20 })
const { name, age } = toRefs(obj)
const updateName = () => {
name.value = 'zs'
}
</script>
5.2 使用场景
- 剥离响应式对象,想使用响应式对象中的多个或者所有属性作为响应式数据
6. ref 函数
作用:定义响应式函数
- 通常定义简单类型数据
- 其实也可以定义复杂类型的数据,在你得到数据类型是未知的情况下:可以这么定义: const data = ref(null) 注意:
- 在修改值或获取值的时候都需要.value
- 在模版中使用 ref 申明的响应式数据,可以省略.value
6.1 演示示例
<template>
<div @click="updateName()">{{ name }} - {{ age }} </div>
</template>
<script setup>
import { ref } from 'vue'
const name = ref('ls')
const age = ref(18)
const data = ref(null) // [] | {} | 0 | 'string' | true
const updateName = () => {
name.value = 'zs'
}
</script>
6.2 使用场景
- 当你明确知道需要的是一个响应式数据对象,那么就使用reactive
- 其他情况使用ref
7. computed 函数
作用:定义计算属性
7.1 简单用法:演示示例
<template>
<div>{{ age }} - {{ newAge }} </div>
</template>
<script setup>
import { ref, computed } from 'vue'
const age = ref(18)
const newAge = computed(() => age.value + 2)
</script>
7.2 高级用法:演示示例
<template>
<div>{{ age }} - {{ newAge }} </div>
<input v-model="newAge" />
</template>
<script setup>
import { ref, computed } from 'vue'
const age = ref(18)
const newAge = computed({
get() {
return age.value + 2
},
// 当你给计算属性设置值的时候触发,实现计算属性也支持v-model
set(value) {
age.value = value - 2
}
})
</script>
8. watch 函数、watchEffect 函数
作用: 定义监听器
- watch(): 指定监听数据
- 监听指定的一个或多个响应式数据,一旦数据变化,就自动执行监听回调
- 如果是监听reactive对象中的属性,必须通过函数来指定
- 监听多个数据,使用数组来指定
- 默认初始时不执行回调,但可以通过配置immebdiate:true,来指定初始时立即执行第一次
- 通过配置deep:true,来指定深度监视
- watchEffect(): 不指定监听数据
- 不用直接指定要监视的数据,回调函数中使用的那些响应式数据就监视那些响应式数据
- 默认初始时就会执行一次
8.1 watch用法:演示示例
- 监听一个数据
<template>
<div>{{ count }} </div>
<button @click="add">修改值</button>
</template>
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const add = () => { count.value++ }
watch(count, (newVal, oldVal) => {
console.log(newVal, oldVal)
})
</script>
- 监听多个数据
<template>
<div>{{ count }} </div>
<button @click="add">修改值</button>
<hr />
<div>{{ obj.name }} </div>
<div>{{ obj.age }} </div>
<button @click="updateName">修改值</button>
</template>
<script setup>
import { ref, reactive, watch } from 'vue'
const count = ref(0)
const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })
const add = () => { count.value++ }
const updateName = () => { obj.name = 'zs' }
watch([count, obj], () => {
console.log('监听多个数据变了')
})
</script>
- 监听对象中某一个属性的变化
<template>
<div>{{ obj.name }} </div>
<div>{{ obj.age }} </div>
<button @click="updateName">修改值</button>
<button @click="updateBrandName">修改值</button>
</template>
<script setup>
import { reactive, watch } from 'vue'
const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })
const updateName = () => { obj.name = 'zs' }
// 监听对象中某一个属性的变化,例如obj.name
watch(() => obj.name, () => {
console.log('监听obj.name数据变了')
})
</script>
- 深度监听
<template>
<div>{{ obj.name }} </div>
<div>{{ obj.age }} </div>
<button @click="updateName">修改值</button>
<button @click="updateBrandName">修改值</button>
</template>
<script setup>
import { reactive, watch } from 'vue'
const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })
const updateName = () => { obj.name = 'zs' }
const updateBrandName = () => { obj.brand.name = '奔驰' }
watch(obj, () => {
console.log('监听数据变了')
})
watch(() => obj.brand, () => {
console.log('监听brand数据变了')
}, {
immebdiate: true, // 立即监听
deep: true // 深度监听
})
</script>
8.2 watchEffect用法: 演示示例
<template>
<input v-mode="name">
<input v-mode="age">
<div>{{ full }}</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue'
const name = ref('ls')
const age = ref(18)
const full = ref(null)
watchEffect(() => { full.value = name.value + '--', age.value })
</script>
9. ref 属性
作用:通过 ref 绑定 DOM 元素
9.1 演示示例
- 单个 DOM 元素的绑定
<template>
<div ref="dom">DOM</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const dom = ref(null)
onMounted(() => {
console.log('ref-dom', dom.value)
})
</script>
- 被 v-for 便利的 DOM 元素(多个)
<template>
<ul>
<li v-for="item in 4" :ref="setDom">第{{ item }}个Li</li>
</ul>
</template>
<script setup>
const domList = []
const setDom = (el) => {
domList.push(el)
}
console.log('domList', domList)
</script>
10. 父子通讯
10.1 父传子
父组件 app.vue
<template>
<h1>父组件</h1>
<Som :msg="msg" />
</template>
<script setup>
import { ref } from 'vue'
import Som from './som.vue'
const msg = ref('props')
</script>
子组件 som.vue
<template>
<h1>子组件</h1>
<p>{{ props.msg }}</p>
</template>
<script setup>
import { defineProps } from 'vue'
const props = defineProps({
msg: {
type: String,
default: 'hello'
}
})
</script>
10.2 子传父
父组件 app.vue
<template>
<h1>父组件</h1>
<p>{{ msg }}</p>
<Som :msg="msg" @change="changeMsg" />
</template>
<script setup>
import { ref } from 'vue'
import Som from './som.vue'
const msg = ref('props')
const changeMsg = (newMsg) => { msg.value = newMsg }
</script>
子组件 som.vue
<template>
<h1>子组件</h1>
<p>{{ props.msg }}</p>
<button @click="change">改变了msg</button>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
msg: {
type: String,
default: 'hello'
}
})
const emit = defineEmits(['change'])
const change = () => { emit('change', '更新msg') }
</script>
10.3 扩展(对以上代码中绑定的事件和参数的简写版本)
父组件 app.vue
<template>
<h1>父组件</h1>
<p>{{ msg }}</p>
<Som v-model:msg="msg" />
</template>
<script setup>
import { ref } from 'vue'
import Som from './som.vue'
const msg = ref('props')
const changeMsg = (newMsg) => { msg.value = newMsg }
</script>
子组件 som.vue
<template>
<h1>子组件</h1>
<p>{{ props.msg }}</p>
<button @click="change">改变了msg</button>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
msg: {
type: String,
default: 'hello'
}
})
const emit = defineEmits(['change'])
// const change = () => { emit('change', '更新msg') }
const change = () => { emit('update:msg', '更新msg') }
</script>
11. 后代组件数据通讯 provide 函数和 inject 函数
作用:
- provide: 提供数据和函数给后代组件使用
- inject:给当前组件注入provide提供的数据和函数 注意:后代组件不能修改父组件的数据,遵循单向数据流的原则;数据是谁定义的,谁来修改
11.1 演示示例
父组件 app.vue
<template>
<h1>父组件</h1>
<p>{{ money }}</p>
<button @click="money = 2000">发钱</button>
<hr />
<Som />
</template>
<script setup>
import { ref, provide } from 'vue'
import Som from './som.vue'
const money = ref(100)
const changeMoney = (saleMoney) => {
money.value = money.value - saleMoney
}
// 将数据提供后代组件
provide('money', money)
// 将函数提供给后代组件
provide('changeMoney', changeMoney)
</script>
子组件 som.vue
<template>
<h2>子组件</h2>
<p>{{ money }}</p>
<hr />
<GrandSom />
</template>
<script setup>
import { inject } from 'vue'
import GrandSom from './GrandSom.vue'
// 接受父组件提供的数据
const money = inject('money')
</script>
孙组件 GrandSom.vue
<template>
<h3>孙组件</h3>
<p>{{ money }}</p>
<button @click="changeMoney(20)">消费20</button>
</template>
<script setup>
import { inject } from 'vue'
import GrandSom from './GrandSom.vue'
// 接受父组件提供的数据
const money = inject('money')
// 后代组件不能修改父组件的数据,遵循单向数据流的原则;数据是谁定义的,谁来修改
const changeMoney = inject('changeMoney')
</script>
11.2 使用场景
- 有一个父组件,里头有子组件,孙组件等很多后代组件;共享父组件的数据
12. v-model 语法糖
vue3 封装组件支持 v-model 的时候,父传子
:modelValue,子传父@update:modelValue
12.1 演示示例
父组件 app.vue
<template>
<h2>父组件</h2>
<p>{{ money }}</p>
<hr />
<Som v-model="money" />
</template>
<script setup>
import { ref } from 'vue'
import Som from './som.vue'
const money = ref(100)
</script>
子组件 som.vue
<template>
<h2>子组件</h2>
<p>{{ props.modelValue }}</p>
<button @click="change">改变数据</button>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
modelValue: {
type: Number,
default: 0
}
})
const emit = defineEmits(['change'])
// const change = () => { emit('change', '更新msg') }
const change = () => { emit('update:modelValue', 200) }
</script>
结语
希望文章中的内容能够帮助到大家!如果文章写的不好,请大家见谅,如果觉得还不错的话,请给我点点赞,您的赞是我最大的动力!