1、关于setup的一些概念
- vue3里面自定义事件依然需要先定义后使用
- vue3不再使用this.emit来发射自定义事件,而是使用context.semit来发射自定义事件,this已经是昨日黄花
- 和vue2一样,emit里面有两个参数,一个是自定义事件名,一个是要发射过去的数据,数据可以是多个或者是数组
- 在setup里面定义的变量和函数一定要通过return返回出去
- vue3把钩子都作为函数放在‘vue’里需要从‘vue’中解构出来
- 在setup里面使用的变量要用ref或者reactive包裹一下,ref包裹基本数据类型,reactive包裹对象数据类型。包裹后就称为响应式的对象。
- 被包裹的基本数据类型是只读(readonly)的,不能直接修改要通过数据.value的形式进行修改
- 函数的使用通过赋值的形式使用
- 以上均在下例中进行了注释
子组件Test.vue
<template>
<h3>here is test component</h3>
<h1>{{count}}</h1>
<button @click="emitAddEvent">add</button>
</template>
<script>
import {watchEffect,watch,toRefs} from 'vue'
export default {
name: 'Test',
props: {
count:Number
},
//自定义事件注册在setup外面
emits:['add'],
//连个参数,一个props,一个context
setup(props,context){
//3和2在定义函数是不一样的,通过回调函数,将自定义事件发射出去
const emitAddEvent=()=>{
//发射事件不再使用thies.emit,而是用context,this在3里面不用了,
//emit函数和2里面一致,先注册后使用
context.emit('add',100)
}
return {
//用到那个变量或者函数一定要return出去
emitAddEvent
}
}
}
</script>
<style scoped>
</style>
父组件app.vue
<template>
<div>
<!--这里add是自定事件,addcount是自定义事件的回调函数 -->
<Test :count="mycount" @add="addcount"></Test>
</div>
</template>
<script>
import Test from './components/Test.vue'
//从vue中解构导出所需要的函数,包括钩子都是以函数的形式解构出来使用
import {reactive,ref,toRefs} from 'vue'
export default {
name: 'App',
setup(props,context){
//使用到的变量要ref包裹一下
const mycount=ref(1000)
//发射过来的emit事件是带参的,回调函数要用个形参来接收一下
const addcount=(plusdata)=>{
//被ref包裹的数据,由于响应式的问题,是只读的(readonly)不能修改,需要拆一下把value拆出来修改
mycount.value+=plusdata
}
return {
//变脸及回调函数都要return出去
mycount,
addcount
}
},
components: {
Test
}
}
</script>
<style>
</style>
2、reatvie
- 对应用类型数据进行包裹,使其称为响应式的数据,vue不建议使用原对象(应用类型数据),而直接使用包裹后的数据,因为包裹后就称为响应式数据。如果强行拆开,使用原对象,则丧失了响应式。
- reactive事项了原对象的拷贝,但他是深拷贝
<template>
<div>
spp
</div>
</template>
<script>
import {reactive,ref,toRefs} from 'vue'
export default {
name: 'App',
setup(props,context){
const proxyObj=reactive({
a:1,
b:2,
c:[
{d:3,e:4},{f:3,g:5}
]
})
console.log(proxyObj)
return {
}
},
components: {
}
}
</script>
<style>
</style>
- reactive的对象可以直接修改对象
- //使用reactive包裹的对象会自动展开,state.myref等效于state.value.myref
console.log(state.myref) - //也可以直接修改数据,因为实际是自动展开了
state.myref=200
console.log(state.myref)
3、ref
- 包裹基本数据类型,返回响应式的ref对象
- 修改的话需要使用数据.value来使用
- 如果包裹一个应用类型,但实际返回的是reactive类型,并支持deep拷贝
- return出去的被ref包裹的数据,会合并到render函数(h函数)里去。并自动的将里面的value展开,如{{count}},而不用{{count.value}}
- 如果一个ref包裹了一个已经ref包裹的数据,新的ref会覆盖老的ref的值
<template>
<div>
spp
</div>
</template>
<script>
import {reactive,ref,toRefs} from 'vue'
export default {
name: 'App',
setup(props,context){
const myref=ref(100)
const proxyObj=ref({
a:1,
b:2,
})
console.log(myref.value)
console.log(proxyObj.value.a)
const state=reactive({ myref })
//使用reactive包裹的对象会自动展开,state.myref等效于state.value.myref
console.log(state.myref)
//也可以直接修改数据
state.myref=200
console.log(state.myref)
return {
myref,
state
}
},
components: {
}
}
</script>
<style>
</style>
4、ref/unref
- ref是深度响应的。深度以为这嵌套的对象,深度响应表示嵌套的对象按照路径找下去是可以修改的
<template>
<div>
<h1>{{name}}</h1>
<h1>{{person.homeland.city}}</h1>
</div>
</template>
<script>
import {reactive,ref,toRefs} from 'vue'
export default {
name: 'App',
setup(props,context){
const name=ref('')
name.value='zhangsan'
setTimeout(()=>{
name.value='lisi'
console.log(name.value)
},2000)
const person=ref({
name:'wanger',
age:12,
homeland:{
province:'hebei',
city:'shijiazhuang'
}
})
setTimeout(()=>{
//非深度响应
person.value.name='mazouri'
//深度响应
person.value.homeland.city='baoding'
},3000)
console.log(person.value.name)
console.log(person.value.homeland.city)
return {
name,
person,
}
},
components: {
}
}
</script>
<style>
</style>
- unref如果参数数据是ref包裹,就返回inner value,如果不是ref包裹的数据,就返回数据本身
- isRef函数判断
<template>
<div>
</div>
</template>
<script>
import {reactive,ref,toRefs,isRef,unref} from 'vue'
export default {
name: 'App',
setup(props,context){
const myobj={
name:'zhangsan',
age:22,
address:'heibei'
}
const obj1=isRef(myobj) ? myobj.name : myobj
console.log(obj1)
const obj2=ref(myobj)
const obj3=isRef(obj2) ? myobj.name : myobj
console.log(obj3)
//unref实际就等于上面的obj3
const obj4=unref(obj2)
console.log(obj4)
const obj5=unref(myobj)
console.log(obj5)
return {
myobj
}
},
components: {
}
}
</script>
<style>
</style>
5、toRef/toRefs/isRef
1、toRef:建立reactive和ref的一种转换
可以把对象中的某个属性单独拿出来转成ref
<template>
<div>
{{myref}}
</div>
</template>
<script>
import {reactive,ref,toRef,isRef,unref} from 'vue'
export default {
name: 'App',
setup(props,context){
const state=reactive({
name:'zhangsan',
age:23
})
//指定转换的属性,这里将state的reactive包装对象的name属性传给myref
const myref=toRef(state,'name')
//修改转换后的值
myref.value='lisi'
console.log('11111',state.name)
//修改state.name和修改myref.value都会影响到对方
state.name='wanger'
console.log('22222',myref.value)
return {
myref,
}
},
components: {
}
}
</script>
<style>
</style>
2、toRefs:建立reactive包装对象和ref的一种转换
- 将一个reactive包装好的对象,整个转换成一个ref包装成的对象
- 这样对象的所有属性都转换过去,而不是像toRef那样值转换其中的一个属性,因此,只需要一个转换对象形参就可以
<template>
<div>
urname:{{name}}<br/>
urage:{{age}}
</div>
</template>
<script>
import {reactive,ref,toRef,toRefs,isRef,unref} from 'vue'
export default {
name: 'App',
setup(props,context){
const state=reactive({
name:'zhangsan',
age:23
})
//指定转换的属性,这里将state的reactive包装对象的name属性传给myref
//这里转成了一个普通对象
const myrefs=toRefs(state)
console.log(myrefs)
console.log({
...myrefs
})
console.log('11111',myrefs.name.value)//zhangsan
console.log('11111',myrefs.age.value)//23
//修改转换后的值
myrefs.name.value='lisi'
myrefs.age.value=33
//修改转换后的值会影响到原则,因为实际本质上还是引用的
console.log(state.name)//lisi
console.log(state.age)//33
//修改state.name和修改myref.value都会影响到对方
state.name='wanger'
state.age=44
console.log('22222',myrefs.name.value)
console.log('22222',myrefs.age.value)
return {
//直接展开转化后的myrefs,这样在template里面可以直接拿到展开后的name和age
...myrefs,
state
}
},
components: {
}
}
</script>
<style>
</style>
3、isRef判断是否为ref
参看前几例
6、cutomRef自定义ref
用于数据防抖
customRef需要有一个工厂函数做参数,track(追踪),trigger(触发),分别对应getter和setter
<template>
<div>
<span>{{text}}</span>
<input type="text" v-model="text">
</div>
</template>
<script>
import {reactive,customRef} from 'vue'
function useDebounce(value,delay=200){
let time=null
return customRef((track,trigger)=>{
return {
get(){
//跟踪旧值,返回旧值前要跟踪旧值,防止其发生变化
track()
return value
},
set(newvalue){
clearTimeout(time)
time=setTimeout(()=>{
value=newvalue
//更新数值和要去触发trigger
trigger()
},delay)
}
}
})
}
export default {
name: 'App',
setup(props,context){
const text=useDebounce('',500)
return {
text
}
},
components: {
}
}
</script>
<style>
</style>
7、shallowRef
浅ref,由于使用ref包装之后,数据就具有了响应式,如果使用shallowRef包装,转化后成为一个proxy对象(普通对象)
<template>
<div>
<span>{{myobj1}}</span>
<hr/>
<span>{{myobj2}}</span>
</div>
</template>
<script>
import {ref,reactive,customRef,shallowRef} from 'vue'
export default {
name: 'App',
setup () {
const myobj1=ref({
name:'zhangsan',
age:12
})
console.log(myobj1)//RefImpl
myobj1.value={
name:'lisi',
age:33
}
console.log(myobj1.value)//Proxy
const myobj2=shallowRef({
name:'zhangsan',
age:12
})
console.log(myobj2)//RefImpl
myobj2.value={
name:'wanger',
age:55
}
console.log(myobj2)//RefImpl
return {
myobj1,
myobj2
}
}
}
</script>
<style>
</style>
8、triggerRef
手动触发响应式操作
<template>
<div>
<span>{{myobj1}}</span>
<hr/>
<span>{{myobj2}}</span>
</div>
</template>
<script>
import {ref,reactive,customRef,shallowRef,watchEffect,triggerRef} from 'vue'
export default {
name: 'App',
setup () {
const myobj1=ref({
name:'zhangsan',
age:12
})
console.log(myobj1)//RefImpl
myobj1.value={
name:'lisi',
age:33
}
console.log(myobj1.value)//Proxy
const myobj2=shallowRef({
name:'zhangsan',
age:12
})
console.log(myobj2)//RefImpl
myobj2.value={
name:'wanger',
age:55
}
console.log(myobj2)//RefImpl
let name=''
watchEffect(()=>{
name=myobj2.value.name
console.log(name)
})
myobj2.value.name='zhaoqiusun'
triggerRef(myobj2)
return {
myobj1,
myobj2
}
}
}
</script>
<style>
</style>
9、computed
返回一个不可变的值
返回值也是一个ref
<template>
<div>
<span>{{superSentence}}</span>
</div>
</template>
<script>
import {ref,computed} from 'vue'
export default {
name: 'App',
setup (props,context) {
const textRef=ref('欢迎来到我的小屋')
const superSentence=computed(()=>{
return '小明'+textRef.value
})
console.log(superSentence.value)
return {
superSentence
}
}
}
</script>
<style>
</style>
实际机制其实也是一个get和set
<template>
<div>
<span>{{superSentence}}</span>
</div>
</template>
<script>
import {ref,computed} from 'vue'
export default {
name: 'App',
setup (props,context) {
const textRef=ref('欢迎来到我的小屋')
const superSentence=computed(
{
get(){
return '小明'+textRef.value
},
set(value){
console.log('我通过计算属性修改了值')
console.log(value)
}
}
)
console.log(superSentence.value)
superSentence.value='我一旦修改,就要触发set语句'
return {
superSentence
}
}
}
</script>
<style>
</style>