一、setup
- 理解:vue3.0中一个新的配置项,值为一个函数
- setup是所有Composition API(组合api)“表演的舞台”
- 组件中所用的到:数据、方法等等,均要配置在setup中
- setup函数的两种返回值:
(1)若返回一个对象,则对象中的属性、方法,在模板中均可以直接使用。
(2)若返回一个渲染函数,则可以自定义渲染内容
import {h} from 'vue'
setup() {
// 数据
let name = 'xxx'
let age = 18
// 方法
function sayHello() {
alert(`${name}${age}岁`)
}
// 返回一个对象(常用)
return {
name,
age,
sayHello
}
// 返回一个渲染函数
return () => {h('h1', 'xxxxxx')}
}
- setup执行的时机
在beforeCreate之前执行一vi,this是undefined - setup的参数
(1)props:值为对象,包含组件外部传递过来且组件内部声明接收了的属性
(2)context:上下文对象
attrs:值为对象,包含组件外补传递过来,但没有在peops配置中声明的属性,相当于this.$attrs
slots:收到的插槽内容,相当于this.$slots
emit:分发自定义事件的函数,相当于this.$emit
二、ref函数
- 作用:定义一个响应式的数据
- 语法:
const xxx = ref(initValue)
(1)创建一个包含响应式数据的引用对象(reference对象)
(2)js中操作数据:xxx.value
(3)模板中读取数据:不需要.value,直接<div>{{xxx}}</div>
- 其他:
(1)接收的数据可以是:基本类型或者对象类型
(2)基本类型的数据:响应式依然是靠Object.defineProperty()
的get
和set
完成的
(3)对象类型的数据:内部“求助”了vue3.0中的一个新函数——reactive
函数
import {ref} from 'vue'
setup() {
// 数据
let name = ref('xxx')
let age = ref(18)
let job = ref({
type: 'xxxx',
salary: 20
})
// 方法
function changeInfo() {
name.value = 'yyy'
name.age = 22
job.value.type = 'yyyy'
job.value.salary = 30
}
// 返回一个对象(常用)
return {
name,
age,
job,
change Info
}
三、reactive函数
- 作用:定义一个对象类型的响应式数据(基本类型不要用它,要用ref函数)
- 语法:
const 代理对象 = reactive(源对象)
接收一个对象(或数组),返回一个代理对象(proxy的实例对象) - reactive定义的响应式把数据是深层次的
- 内部基于ES6的Proxy实现,通过代理对象操作元对象内部数据进行操作
import {reactive} from 'vue'
setup() {
// 数据
let job = reactive({
type: 'xxxx',
salary: 20
})
// 方法
function changeInfo() {
job.type = 'yyyy'
job.salary = 30
}
// 返回一个对象(常用)
return {
job,
change Info
}
四、reactive对比ref
- 从定义数据角度对比:
- ref用来定义:基本类型数据
- reactive用来定义:对象(或数组)类型数据
- 备注:ref也可以用来定义对象(或数组)类型数据,它内部会自动通过reactive转为代理对象
- 从原理角度对比:
- ref通过
Object.defineProperty()
的get和set来实现响应式(数据劫持) - reactive通过使用
Proxy
来实现响应式(数据劫持),并通过Reflect
操作源对象内部的数据
- 从使用角度对比:
- ref定义的数据:操作数据需要
.value
,读取数据时模板中直接读取不需要.value
- reactive定义的数据:操作数据与读取数据均不需要
.value
五、vue3.0中的响应式原理
vue2.x的响应式:
- 实现原理:
(1)对象类型:通过Object.defineProperty()
对属性的读取、修改进行拦截(数据劫持)
(2)数组类型:通过重写更新数组的一系列方法类实现拦截 - 存在问题:
(1)新增属性、删除属性,界面不会更新
(2)直接通过下标修改数组,界面不会自动更新
vue3.0的响应式:
- 实现原理:
(1)通过Proxy(代理):拦截对象中任意属性的变化,包括属性值的读写、属性的添加、属性的删除等
(2)通过Reflect(反射):对源对象的属性进行操作
new Proxy(data, {
// 拦截读取属性值
get(target, prop) {
return Reflect.get(target, prop);
}
// 拦截设置属性值或添加新属性
set(target, prop, value) {
return Reflect.set(target, prop, value);
}
// 拦截删除属性
deleteProperty(target, prop) {
return Reflect.deleteProperty(target, prop);
}
})
六、计算属性与监视
- computed函数
import {computed} from 'vue'
setup() {
let fullName = computed(() => {
return person.firstName + '-' + person.LastName
})
}
- watch函数
import {ref, reactive, watch} from 'vue'
setup() {
let sum = ref(0)
let msg = ref('xxx')
let person = reactive({
name: 'aaa',
age: 18,
job: {
j1: {
salary: 20
}
}
})
// 情况一:监视ref所定义的一个响应式数据
watch(sum, (newValue, oldValue) = > {
// xxxx
}, {immediate: true})
// 情况二:监视ref所定义的多个响应式数据
watch([sum, age], (newValue, oldValue) => {
// xxxx
})
// 情况三:监视reactive所定义的一个响应式数据的全部属性
// 此处无法正确的获取oldValue;强制开启了深度监视(deep配置无效)
watch(person, (newValue, oldValue) => {
// xxxx
}, {deep: false}) //次数的deep配置无效
// 情况四:监视reactive所定义的一个响应式数据中的某个属性
watch(() => person.name, (newValue, oldValue) => {
// xxxx
})
// 情况五:监视reactive所定义的一个响应式数据中的某些属性
watch([() => person.name, () => person.age], (newValue, oldValue) => {
// xxxx
})
// 特殊情况
watch(() => person.job, (newValue, oldValue) => {
// xxxx
}, {deep: true}) // 此处由于监视的是reactive定义的对象中的某个属性,所以deep配置有效
return {
sum,
msg,
person
}
}
七、watchEffect函数
- watch的套路是:既要指明监视的属性,也要指明监视的回调
- watchEffect的套路是:不用指明监视哪个属性,监视的回调中用到哪个属性,那就监视哪个属性
- watchEffect有点像computed:
但computed注重的是计算出来的值(回调函数的返回值),所以必须要写返回值
而watchEffect更注重的是过程(回调函数的函数体),所以不用写返回值
watchEffect(() => {
const x1 = sum.value
const x2 = person.age
console.log('watchEffect配置的回调执行了')
})
八、生命周期
-
vue3.0中可以继续使用vue2.x中的生命周期钩子,但有两个被更名:
beforeDestroy改名为beforeUnmount
destroyed改名为unmounted
(可以使用配置项的形式使用生命周期钩子,和setup同级) -
vue3.0也提供了Composition API形式的生命周期钩子,与vue2.x中钩子对应关系如下:
beforeCreate ⇒ setup()
created ⇒ setup()
beforeMount ⇒ onBeforeMount
mounted ⇒ onMounted
beforeUpdate ⇒ onBeforeUpdate
updated ⇒ onUpdated
beforeUnmount ⇒ onBeforeUnmount
unmounted ⇒ onUnmounted
九、自定义hook函数
- 本质是一个函数,把setup函数中使用的Composition API进行了封装
- 类似于vue2.x中的mixins
- 自定义hook的优势:复用代码,让setup中的逻辑更清楚易懂
十、toRef
- 作用:创建一个ref对象,其value值指向另一个对象中的某个属性
- 语法:
const name = toRef(person, 'name')
- 应用:要将响应式对象中的某个属性单独提供给外部使用时
- 扩展:
toRefs
与toRef
功能一致,但可以批量创建多个ref对象,语法为toRefs(Person)
原来:
模板中{{person.name}} {{person.job.j1}}
import {ref, reactive} from 'vue'
setup() {
let person = reactive({
name: 'aaa',
age: 18,
job: {
j1: {
salary: 20
}
}
})
return {
person
}
使用toRef:
模板中{{name}} {{age}} {{salary}}
import {ref, reactive, toRef, toRefs} from 'vue'
setup() {
let person = reactive({
name: 'aaa',
age: 18,
job: {
j1: {
salary: 20
}
}
})
return {
name: toRef(person, 'name'),
age: toRef(person, 'age'),
salary: toRef(person.job.j1, 'salary')
}
使用toRefs:
模板中{{name}} {{age}} {{job.jj1.salary}}
import {ref, reactive, toRef, toRefs} from 'vue'
setup() {
let person = reactive({
name: 'aaa',
age: 18,
job: {
j1: {
salary: 20
}
}
})
return {
...toRefs(person)
}
十一、provide与inject
- 作用:实现祖与后代组件通信,一般用于实现祖孙组件通信
- 套路:父组件有一个
provide
选项来提供数据,后代组件有一个inject
选项来开始使用这些数据 - 具体写法:
祖组件中:
setup() {
....
let car = reactive({
name: 'xxx',
price: '10w'
})
provide('car', car)
}
后代组件中:
setup() {
....
const car = inject('car')
return {car}
}