目录
Setup语法糖
setup是一个专门用于组合式API的特殊钩子函数,只在初始化时执行一次。
setup有两种返回值:
- 对象。 对象中的属性,方法在模板中都可以直接使用
- 函数。 自定义渲染内容,覆盖模板内容
注:不要与Vue2配置混用
- Vue2配置中:{data,methods,computer}中可以直接访问setup中的属性和方法
- 在setup中不能访问Vue2的配置 { data , methods , compoter... }
- 如果变量或函数出现重名,setup内的数据优先
注意
- 在setup语法糖里面,this指向是undefined,this.任何变量都无法获取值。setup是一个独立的封装函数
steup简写语法里面,不需要自己写async,当出现await时会自动在setup内部出现,但是带有async setup()的组件必须嵌套在<suspense>中才能呈现。
setup的细节
- setup(props,context) || setup(props,{attrs,slots,emit})
- props:值为对象,包含组件外部传递过来的,且组件内部声明接收了的属性。父给子传了参数,子没有接收就会报错。父没有传参数,子接收了就是undefined
- context:上下文对象,里面是三个值分别如下👇
- attrs:值为对象,包含组件外部传递过来的,但没有在props配置中声明的属性,相当于 this.$attrs。attrs就像vue2的$attrs是个捡漏的,传过来的参数没有被props接收,都会到attrs里面去
- emit:用来分发自定义事件的函数,相当于 this.$emit。用来触发父元素传给子元素的事件
- slots:收到的插槽内容,相当于 this.$slots
setup不能是一个async函数。一个函数如果被async修饰了,那么返回的就不再是一个普通对象,而是被promise包裹的对象,模板中看不到return对象中的属性
ref:定义一个数据的响应式
语法:
import ref from 'Vue'
const xxx = ref(initValue); RefImpl 引用的实现的实例对象
说明
- JS中操作数据:xxx.value
- 模板中操作数据:不需要.value,因为浏览器解析的时候已经把value给解析了
- 用ref包括的基本类型数据是RefImpl的实例对象
注意
- 在vue3中,想把普通的number,string,bollean编程响应式的数据,就必须要借助ref
- ref定义对象类型的数据,底层原理是利用了ES6的proxy实现的响应式,而在vue3中,对proxy的操作封装在了一个叫reactive的新函数中。
- Vue3中ref响应式仍然是通过 Object.defineProperty()的getter和settrer拦截来实现 .value
reactive:定义一个对象类型的响应式数据
语法:
import { reactive } from 'vue'
const proxyObj = reative(obj);
接收一个普通对象或数组,然后返回一个响应式代理对象
说明
- 定义的响应式数据是深层的:会影响对象内部所有嵌套的属性(递归深度响应式)
- reactive响应式底层原理是基于ES6的proxy实现
- reactive只能处理对象或数组,处理其他类型数据会报错
reactive与ref的区别
- ref:定义基本类型数据响应式,得到的是:RefImpl的实例,引用对象.value
- reactive:定义复杂类型响应式数据,得到的是Proxy的实例,代理对象:变量名.属性名(下标)即可
ref处理对象或数组的时候,内部借助了reactive函数来实现响应
reactive只能处理对象或数组,处理其他类型数据会报错
reactive核心是对ES6的proxy进行了封装
toRef()
toRef是函数:转换响应式对象中某个属性为单独响应式数据,并且跟原来的数据的值是关联的
语法:
import toRef from 'vue'
第一个值是响应式对象,第二个值是在obj下需要独立出来的数据的属性名
let name = toRef(obj,'name')
独立出来的数据跟原来的对象是保持联系的,name发生了改变,obj里的name也会发生改变
toRef独立出来的数据类型是refImpl。改变数据的方法:name.value = '老铁'
toRefs()
toRefs是函数,转换响应式对象中所有属性为单独响应式数据,对象转为普通对象,并且值是关联的
<script>
import { ref, reactive,toRef,toRefs} from 'vue'
export default {
setup() {
let sub = {
name: '张三',
age: 18,
english: 80,
number: 20,
a: {
b: {
c: 1
}
},
aobby: ['🦈', '🐋', '🐡']
}
let obj = reactive(sub)
let reactiveObj=toRefs(obj)
function sayHi() {
obj.name = '李四'
obj.age = 99
console.log(sub.name);
console.log(sub.name) //独立出来的属性改变时,对象内的属性也会发送改变
}
return { ...reactiveObj , sayHi }
}
}
</script>
<template>
<div>
<div>{{ name }}</div>
<p>age:{{ age }}</p>
<p> english :{{ english }} </p>
<p> number :{{ number }} </p>
<p> 测试 C :{{ a.b.c }} </p>
<p> 海洋世界 :{{ aobby }} </p>
<button @click="sayHi">hello</button>
</div>
</template>
计算属性
Vue3的计算属性完全可以提取到单独的文件里去处理,处理完再引入
原理
<template>
<div>
<h2>vue3的特殊计算属性</h2>
姓:<input type="text" v-model="user.firestName"><br>
名:<input type="text" v-model="user.lastNamt"><br>
计算属性:<input type="text" v-model="user.computed"><br>
</div>
</template>
<script setup>
import { reactive,computed } from 'vue';
let user = reactive({
firestName:"易烊",
lastNamt:"千玺"
})
user.computed = computed({
//获取值时返回get里的方法,改变值时调用set的方法
get(){
return user.firestName+"——"+user.lastNamt
},
set(val){
const arr = val.split('——')
console.log(arr)
user.firestName=arr[0]
user.lastNamt=arr[1]
}
})
</script>
使用与封装
在utils文件夹下
import { computed } from 'vue'
export function TextComputed(val){
const str = computed(()=>{
return val.split('').reverse().join('')
})
return str
}
<template>
<div>
<h2>Vue3</h2>
<p>msg:{{ str }}</p>
<p>翻转:{{ reverse }}</p>
<p>翻转:{{ conment }}</p>
</div>
</template>
<script setup>
import { ref,computed ,reactive } from 'vue'
let str = ref('hello i am vue3')
// const reverse = computed(()=>{
// return str.value.split('').reverse().join('')
// })
import { TextComputed } from '../utils/computed'
const reverse = TextComputed(str.value)
let cmt = ref('hellotodo')
// const conment = computed(()=>{
// return cmt.split('').reverse().join('')
// })
const conment = TextComputed(cmt.value)
</script>
watchEffect函数
watch的作用:即要指明监听的属性,也要指明监视的回调
watchEffect的作用:
- 不用指明监视哪个属性,监视的回调中用到哪个属性,就监视哪个属性
- 默认初始时就会执行第一次,从而可以收集需要监视的数据
类似计算属性
- 计算属性需要返回值
- 而watchEffect,没有返回值,是监视到变化之后,做说明逻辑处理
计算属性是注重计算结果,监听器是注重计算过程
watch函数:
<template>
<div>
<h2>Vue3监听器(ref定义的数据)</h2>
<p>msg:{{ str }}</p>
<p>num:{{ num }}</p>
<p><input type="text" v-model="str"/></p>
<p><input type="text" v-model="num"/></p>
</div>
</template>
<script setup>
import { ref,computed ,reactive,watch } from 'vue'
let str = ref('hello')
let num = ref(0)
单变量的监听ref定义的数据,第一个值是监听的变量,第二个值是监听的回调函数,第三个值是监听器的配置
// watch(str,(newdata,olbdata)=>{
// console.log('vue3',newdata,olbdata)
// },{
// immediate:true,
// deep:true
// })
多变量的监听ref定义的数据,将需要监听的对象放入数组中
watch(
[str,num],
(n,l)=>{
console.log(n,l)
},
{
immediate:true,
deep:true
}
)
</script>
watchEffect函数:
import { ref , reactive ,watch,watchEffect } from 'vue'
watchEffect(()=>{
const h = counst.value
const n = number.value
console.log('watchEffect 被调用了')
})
watch监听reactive的一些坑(很重要)
<template>
<div>
<h2>vue3的特殊计算属性</h2>
名字:<input type="text" v-model="user.name"><br>
年龄:<input type="text" v-model="user.age"><br>
成绩:<input type="text" v-model="user.school.english.num"><br>
</div>
</template>
<script setup>
import { reactive, computed, watch } from 'vue';
let user = reactive({
name: '张三',
age: '18',
school: {
english: {
num: 85
}
}
})
1.监听reative数据时,无法获取正确的olbdata。因为reative数据是数组或对象,这些数据的键名都是指向某个内存地址。
2. deep深度监视默认开启,不是处理对象类型时无效
// watch(user, (newval, olbval) => {
// console.log(newval, olbval)
// }, {
// deep: false
// }
// )
4.监听器监听reactive的某个属性时报出错误:只能是ref类型,或响应式对象类型,或一个函数
// watch(()=>user.name, //所以我们需要通过一个函数的方式
// (newval,olbval)=>{
// console.log(newval,olbval)
// }
// )
5.监听器监听reactive对象中的某个对象属性
// watch(()=>user.school,(newval,olbval)=>{
// console.log(newval,olbval)
// },{
// deep:true //深层监听,false就无法监听
// })
6.监听器监听reative定义的多个响应式数据 利用数组包裹
watch([()=>user.name,()=>user.age],
(newval,olbval)=>{
console.log(newval,olbval)
})
</script>
生命周期
注意:
- 选项式API中出现两个同名钩子函数,后面的会覆盖前面的
- 组合式API中出现两个同名钩子不会覆盖会按顺序执行依次执行
Vue3获取Dom
<template>
<div>
<h2>Vue3的ref获取dom节点</h2>
<div ref="boxs">321</div>
</div>
</template>
<script setup>
import { onMounted , ref } from 'vue';
let boxs = ref(null) //vue3中获取dom元素的方法是调用ref
//使用dom的时候因为是refimpl的响应数据所以要加.value
onMounted(() => {
console.log(boxs.value)
boxs.value.style.fontSize="40px"
boxs.value.style.color="red"
})
</script>
Props
作用:子组件内使用defineProps来接收参数,不需要引入就可以使用
<template>
<div>
<h2>vue3的父组件</h2>
<child :msgstr="msg" ></child>
</div>
</template>
<script setup>
import child from './childer.vue'
import { ref } from 'vue'
let msg = ref('msg number')
</script>
上面是父组件
下面是子组件
<template>
<div>
<h5>vue3的子组件{{ msgstr }}</h5>
{{ mmg }}
</div>
</template>
<script setup>
// let { msgstr } = defineProps(['msgstr']) //传过来的数据是对象,传递数据的名称:数据
// console.log(msgstr)
defineProps({
msgstr:{
type:String, //定义数据类型
default:'默认值'
},
mmg:{
type:Number,
default:100 //默认值
}
})
</script>
Vue3 自定义事件
通过defineEmite宏来声明需要抛出的事件。和defineProps类似,defineEmits仅可用于<script setup>之中,并且不需要引入
<template>
<div>
<h2>vue3的父组件</h2>
<child :msgstr="msg" @changeMsg="changeMsg" ></child>
</div>
</template>
<script setup>
import child from './childer.vue'
import { ref } from 'vue'
let msg = ref(true)
const changeMsg=(data)=>{
msg.value=data
}
</script>
上面为父组件
下面为子组件
<template>
<div>
<h5>vue3的子组件{{ msgstr }}</h5>
{{ mmg }}
<button @click="changefather">自定义事件</button>
</div>
</template>
<script setup>
let hh = defineProps({ //设置返回值,方便函数内也可以调用传过来的参数
msgstr:{
type:Boolean, //定义数据类型
default:true
},
mmg:{
type:Number,
default:100 //默认值
}
})
const emit = defineEmits(['changeMsg']) //vue3组合式API的接收自定义事件的方式
const changefather = ()=>{
console.log(hh.msgstr);
emit('changeMsg',!hh.msgstr) //第一个值是事件名,第二个值是要传过去的参数
}
</script>