setup
- setup 是 Vue3 中一个新的配置项,值为函数
- 组件中使用的数据、方法等都要配置在 setup 中
- setup 函数两种返回值:
- 返回一个对象,对象中的属性、方法可在模板中直接使用
- 返回一个渲染函数,可自定义渲染内容
- setup 函数的参数:
- props:值为对象,包含了组件外部传进来,且组件内部声明接收的属性
- context:上下文对象
- attrs:值为对象,包含了组件外部传进来,且组件内部没有声明接收的属性,相当于
this.$attrs - slots:收到的插槽内容,相当于
this.$slots - emit:触发自定义事件的函数,相当于
this.$emit
// 没错,渲染函数就叫 h
import { h } from 'vue'
export default {
name: 'App',
props: ['title'],
// Vue3 需要声明自定义事件,虽然不声明也能运行
emits: ['changeCount'],
// 返回函数
/*
setup() {
return () => h('h1', 'Hello')
},
*/
// 返回对象
setup(props, context) {
let name = 'Vue3'
function sayHello() {}
function test() {
context.emit('changeCount', 888)
}
return {
name,
sayHello,
test
}
}
}
注意:
- setup 在
beforeCreate钩子之前执行,this为undefined - setup 不要和 Vue2 配置混用。Vue2 的配置可以访问到 setup 的属性方法,反过来不行;如有重名,setup 优先
- setup 不能是 async 函数,因为 async 函数返回的是 promise 不是对象,会导致模板无法访问属性方法
- 若要返回 promise 实例,需要
Suspense和异步组件的配合
ref 函数
作用:定义响应式数据
语法:const name = ref(initValue)
ref函数返回一个RefImpl(reference implement)实例对象,全称引用实现的实例对象- 它包含响应式数据,简称引用对象、reference 对象、ref 对象
- JS 访问数据:
name.value - 模板访问数据:
<div></div>注意事项: ref函数可以接收基本数据类型和引用数据类型- 基本类型数据的响应式还是靠
Object.defineProperty() 完成 - 对象类型数据使用 ES6 的
Proxy实现响应式,Vue3 把相关操作封装在reactive函数中 - 按照之前的办法,对于对象数据,应该遍历每一层的属性添加
getter、setter,但 Vue3 使用Proxy把内部数据一口气监测了
<h2>{{ name }}</h2>
<p>{{ jobInfo.type }}</p>
import { ref } from 'vue'
export default {
setup() {
let name = ref('Vue3')
let jobInfo = ref({
type: 'frontend',
salary: '40w'
})
function changeInfo() {
name.value = '鱿鱼丝'
// jobInfo 是 RefImpl 实例
// jobInfo.value 是 Proxy 实例对象
jobInfo.value.salary = '50w'
}
return {
name,
jobInfo,
changeInfo
}
}
}
reactive 函数
- 定义引用类型的响应式数据,不可用于基本数据类型
const 代理对象 = reactive(源对象)接收对象或数组,返回代理对象(Proxy 的实例对象)reactive的响应式是深度的- 基于 ES6 的 Proxy 实现,通过代理对象操作源对象内部数据
import { reactive } from 'vue'
export default {
setup() {
let person = reactive({
name: 'Vue3',
sex: 'unknown',
info: {
school: 'Oxford',
major: 'computer'
}
})
let color = reactive(['red', 'green', 'blue'])
function changeInfo() {
person.info.major = 'art'
color[0] = 'yellow'
}
return {
person,
color,
changeInfo
}
}
}
computed 函数
import { reactive, computed } from 'vue'
export default {
setup() {
let person = reactive({
firstName: 'Cai',
lastName: 'QP'
})
// 计算属性简写形式
person.fullName = computed(() => {
return person.firstName + '-' + person.lastName
})
// 计算属性完整形式
person.fullName = computed({
get() {
return person.firstName + '-' + person.lastName
},
set(value) {
const arr = value.split('-')
person.firstName = arr[0]
person.lastName = arr[1]
}
})
return {
person
}
}
}
watch 函数
侦听 ref 定义的响应式数据:
- 注意不要写成
sum.value
// 参数:侦听的数据,回调,其他配置
watch(
sum,
(newVal, oldVal) => {
console.log(newVal, oldVal)
},
{ immediate: true }
)
侦听多个 ref 定义的响应式数据:
// newVal,oldVal 也是数组
watch([sum, msg], (newVal, oldVal) => {
console.log(newVal, oldVal)
})
侦听 ref 定义的对象类型数据
// 用 ref 定义对象类型数据
let person = ref({
name: 'Vue3',
age: 18,
info: {
job: {
salary: 40,
},
},
})
// 开启深度监听才有效,此时监听的是 RefImpl 实例
// Ref 实例的 value 是 Proxy 对象,存的是地址
// 因此无法监听 person 内部属性的变化
watch(person, (newVal, oldVal) => { ... }, { deep:true })
// 这个和 “侦听 reactive 函数直接返回的那一整坨响应式数据” 效果一致
watch(person.value, (newVal, oldVal) => {...})
侦听 reactive 函数直接返回的那一整坨响应式数据:
- oldVal 是错误的!和 newVal 的值一样
- 强制开启了深度侦听,
deep配置不生效!
watch(
person,
(newVal, oldVal) => {
console.log(newVal, oldVal)
},
{ immediate: true, deep: false }
)
侦听 reactive 定义的响应式数据某个属性:
- 如果是
() => person.infooldVal 也是错误的! () => person.nameoldVal 是正确的,何时对何时错自己琢磨吧!- 此处没有强制开启深度监听
// 如果监视的属性还是对象,则需要开启深度监听
watch(
() => person.info,
(newVal, oldVal) => {
console.log(newVal, oldVal)
},
{ deep: true }
)
侦听 reactive 定义的响应式数据多个属性:
watch(
[() => person.name, () => person.info],
(newVal, oldVal) => {
console.log(newVal, oldVal)
},
{ deep: true }
)
watchEffect 函数
- watchEffect 不需要指明监听哪个属性,回调里用到哪个属性,就自动监听哪个属性
- computed 注重计算的值,即回调函数的返回值,因此必须有返回值
- watchEffect 更注重过程,即回调函数的函数体,因此可没有返回值
- watchEffect 没有开启深度监听,也不能开启深度监听!
- watchEffect 内部自行修改数据,不会重新调用回调,因此不会出现递归调用
// 回调中用到的数据只要发生变化,则直接重新执行回调
watchEffect(() => {
let total = sum.value
let p = person
console.log('watchEffect...')
})
toRef 函数
- 创建一个
RefImpl实例对象,其value值指向另一个对象的某个属性,修改value值会修改源对象对应的属性 - 应用:需要把响应式对象的某个属性单独提供给外部使用
- 批量创建:
toRefs
import {reactive, toRef, toRefs} from 'vue'
...
setup() {
let person = reactive({
name: 'Vue3',
age: 18,
info: {
job: {
salary: 40,
},
},
})
return {
// 注意不能写成 ref(person.name),这和源对象是割裂开的
name: toRef(person, 'name'),
salary: toRef(person.info.job, 'salary')
// or
...toRefs(person)
}
}