vue3
回顾 vue2.x 生命周期钩子函数:
- beforeCreate
- created
- beforeMount
- mounted
- beforeUpdate
- updated
- beforeDestroy
- destroyed
认识 vue3.0 生命周期钩子函数
- setup 创建实例前
- onBeforeMount 挂载 DOM 前
- onMounted 挂载 DOM 后
- onBeforeUpdate 更新组件前
- onUpdated 更新组件后
- onBeforeUnmount 卸载销毁前
- onUnmounted 卸载销毁后
setup 函数
- setup 函数是一个新的组件选项, 作为组件中 compositionAPI 的起点
- 从生命周期角度来看, setup 会在 beforeCreate 钩子函数之前执行
- setup 中不能使用 this, this 指向 undefined
- 在模版中需要使用的数据和函数,需要在 setup 返回。
reactive 函数
- 作用: 传入一个复杂数据类型,将复杂类型数据, 转换成响应式数据 (返回该对象的响应式代理)
- 总结: 通常是用来定义响应式 对象数据
ref 函数
-
reactive 处理的数据, 必须是复杂类型, 如果是简单类型无法处理成响应式, 所以有 ref 函数!
-
作用: 对传入的数据(一般简单数据类型),包裹一层对象, 转换成响应式。
-
ref 函数接收一个的值, 返回一个 ref 响应式对象, 有唯一的属性 value
-
在 setup 函数中, 通过 返回值 ref 对象的 value 属性, 可以访问到值
-
在模板中, ref 属性会自动解套, 不需要额外的 .value
-
ref 函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
computed
<script setup lang="ts">
import { computed, ref } from 'vue'
const age = ref<number>(18)
const newage = computed(() => {
return +age.value + 1
})
const nextNewage = computed({
get() {
return age.value + 2
},
set(value) {
age.value = value - 2
},
})
</script>
<template>
<div>今年年龄:<input v-model.number="age" /></div>
<div>明年年龄:{{ newage }}</div>
<div>后年的年龄:<input type="text" v-model.number="nextNewage" /></div>
</template>
<style scoped></style>
watch
watch监视, 接收三个参数
- 参数1: 监视的数据源
- 参数2: 回调函数
- 参数3: 额外的配置
监视 ref 简单数据
监听单个 ref
// 监听单个ref
const money = ref(100)
watch(money, (value, oldValue) => {
console.log(value)
})
监听多个 ref
// 监听多个ref
const money = ref(100)
const count = ref(0)
watch([money, count], value => {
console.log(value)
})
监听 ref 复杂数据
监听 ref 复杂数据,需要通过 watch 第三个参数进行配置深度监听
// 监听ref复杂数据
const user = ref({
name: 'zs',
age: 18
})
watch(
user,
value => {
console.log('user变化了', value)
},
{
// 深度监听,,,当ref的值是一个复杂数据类型,需要深度监听
deep: true,
immediate: true
}
)
监听对象的某个属性变化
const user = ref({
name: 'zs',
age: 18
})
watch(
() => user.value.name,
value => {
console.log(value)
}
)
监听 reactive 数据
监视单个 reactive,不需要开启深度监听
const user = reactive({
name: 'zs',
age: 18
})
const count = ref(0)
watch(user, value => {
console.log('user变化了', value)
})
监视 reactive 某个属性
const user = reactive({
name: 'zs',
age: 18
})
const count = ref(0)
watch(
() => user.name,
value => {
console.log('user变化了', value)
}
)
监视多个 reactive 和 ref 使用数组
组件通讯-父传子
- 父组件提供数据
- 父组件将数据通过属性传递给子组件
- 子组件通过 defineProps 进行接收
- 子组件渲染父组件传递的数据
父组件:
<script setup>
import { ref } from 'vue'
// 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册
import Son from './components/Son.vue'
const money = ref(100)
const car = ref('玛莎拉蒂')
</script>
<template>
<div>
<h1>我是父组件</h1>
<div>金钱:{{ money }}</div>
<div>车辆:{{ car }}</div>
<hr />
<Son :money="money" :car="car"></Son>
</div>
</template>
<style lang="less" scoped></style>
子组件:
<template>
<div>
<h3>我是子组件</h3>
<div>{{ money }} --- {{ car }}</div>
</div>
</template>
<script setup>
// defineProps: 接收父组件传递的数据
defineProps({
money: Number,
car: String,
})
</script>
注意:如果使用 defineProps 接收数据,这个数据只能在模板中渲染,如果想要在 script 中也操作 props 属性,应该接收返回值。
// defineProps: 接收父组件传递的数据
const props = defineProps({
money: Number,
car: String
})
console.log(props.money)
console.log(props.car)
组件通讯-子传父
- 子组件通过 defineEmit 获取 emit 对象(因为没有 this)
- 子组件通过 emit 触发事件,并且传递数据
- 父组件提供方法
- 父组件通过自定义事件的方式给子组件注册事件
子组件:
<script setup>
defineProps({
money: Number,
car: String
})
// 参数数组中进行声明,可以触发哪些自定义事件
const emit = defineEmits(['changeMoney'])
const change = () => {
emit('changeMoney', 10)
}
</script>
父组件:
<script setup>
import { ref } from 'vue'
// 在setup语法中,组件导入之后就能够直接使用,不需要使用components进行局部注册
import Son from './components/Son.vue'
const money = ref(100)
const car = ref('玛莎拉蒂')
const changeMoney = num => {
money.value = money.value - num
}
</script>
<Son :money="money" :car="car" @changeMoney="changeMoney"></Son>
依赖注入 - provide 和 inject
依赖注入, 可以非常方便的实现 跨层级的 组件通信
传递单个数据
父组件利用 provide 提供数据
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
provide('money', money)
</script>
<template>
<div>
<h1>我是父组件</h1>
<div>金钱:{{ money }}</div>
<hr />
<Son></Son>
</div>
</template>
<style lang="less" scoped></style>
子组件 (子孙后代, 都可以拿到这个数据)
<script setup>
import { inject } from 'vue'
const money = inject('money')
</script>
<template>
<div>
<h3>我是子组件--{{ money }}</h3>
<button>修改数据</button>
</div>
</template>
<style lang="less" scoped></style>
传递多个数据
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const house = ref('大别墅123')
provide('parentData', {
money,
house
})
</script>
可以多次传递和接收
根组件:
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
const house = ref('大别墅')
provide('money', money)
provide('house', house)
</script>
子组件:
<script setup>
import { inject } from 'vue'
const money = inject('money')
const house = inject('house')
</script>
子组件跨组建向上传
父组件提供方法
<script setup>
import { provide, ref } from 'vue'
import Son from './components/Son.vue'
const money = ref(100)
// 接收子组件传递过来的数据,并修改money
const changeMoney = val => {
console.log(val)
money.value = val
}
provide('changeMoney', changeMoney)
</script>
子组件调用方法通过参数传递数据
<script setup>
import { inject } from 'vue'
const changeMoney = inject('changeMoney')
</script>
<template>
<div>
<h3>我是子组件</h3>
<button @click="changeMoney(100000)">修改数据</button>
</div>
</template>
模板中 ref 的使用
获取模板的元素(dom 元素,组件)
ref 操作 dom
<script setup>
import { ref } from 'vue'
const hRef = ref(null)
const clickFn = () => {
hRef.value.innerText = '我不是标题'
}
</script>
<template>
<div>
<h1 ref="hRef">我是标题</h1>
<button @click="clickFn">操作DOM</button>
</div>
</template>
ref 操作组件
<template>
<h1>父组件:</h1>
<Form ref="formRef"></Form>
<button @click="getForm">获取表单组件</button>
</template>
<script setup>
import {ref} from 'vue'
import Form from './components/Form.vue'
const formRef = ref(null)
const getForm = () => {
formRef.value.validate()
}
</script>
<style lang='less' scoped></style>
需要配合 defineExpose
<template>我是Form表单组件</template>
<script setup>
const validate = () => {
console.log('进行表单校验')
}
defineExpose({
validate
})
</script>
<style lang="less" scoped></style>
vue3 中废弃了过滤器
vue3.0 中不能使用过滤器,直接使用函数进行替代
补充-toRefs 函数
使用场景: 如果对一个响应数据, 进行解构 或者 展开, 会丢失他的响应式特性!
原因: vue3 底层是对 对象 进行监听劫持
作用: 对一个响应式对象的所有内部属性, 都做响应式处理
- reactive/ref 的响应式功能是赋值给对象的, 如果给对象解构或者展开, 会让数据丢失响应式的能力
- 使用 toRefs 可以保证该对象展开的每个属性都是响应式的
<template>
<div>{{ name }}</div>
<div>{{ age }}</div>
<button @click="age++">改值</button>
</template>
<script setup>
import { ref, toRefs } from 'vue'
const user = ref({
name: 'zs',
age: 18
})
const { name, age } = toRefs(user.value)
</script>
问题小结: toRefs 函数的作用是什么 ?
作用: 对一个 响应式对象 的内部属性,保证展开或者解构出的数据也是响应式的