一,setup
setup在beforeCreate,created
前,现在用它来取代这两个生命周期函数:
<template>
<div class="home-box">
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue';
//这里取代了beforeCreate,created生命周期函数,在Mounted之前执行
onMounted(() => {
//这里是页面渲染完毕之后执行的代码
});
</script>
<style lang="scss" scoped>
</style>
二,ref
在setup中,我们新建一个变量:let val=4,将它属性绑定到html中。这只是普通的字符串或者数字。并不是响应式数据(修改val的值后html会自动更新这个值)。
<template>
<div class="home-box">
<div class="title">{{ test }}</div>
</div>
</template>
<script setup>
let test = 'test';
setTimeout(() => {
test = '8888';
console.log(test);
}, 1500);
//虽然1.5s之后test变成了8888,但是它不是响应式数据,并不会通知html中的test再更新一遍。
</script>
使用ref就可以把基本数据类型转化为响应式数据。
<template>
<div class="home-box">
<div class="title">{{ test }}</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
let test = ref('test');
setTimeout(() => {
test.value = '8888';
console.log(test);//打印出来会是RefImpl的实例对象
}, 1500);
</script>
你会发现ref把它们变成了RefImpl的实例对象,使用的时候需要name.value(但是因为vue3检测到你是ref对象,它就自动给你.value了,于是读取值时可以省略这个.value),但是修改值时,必须加这个.value。
这就是ref创建的RefImpl的实例对象,它的底层原理是Object.defineProperty
里面的get
和set
进行数据劫持然后进行响应式。
三,ref定义对象
let test = ref({ aaa: 'test' });
setTimeout(() => {
test.value.aaa = '8888';
console.log(test.value); //变成了procy对象
}, 1500);
ref定义对象时,是用到的Proxy
,vue3把它封装在新函数reactive里,就相当于,ref中是对象,自动会调用reactive。
也就是说,test是ref响应式数据,但是他的值变成了procy对象,在修改值的时候,也是需要test.value的,内部的则已经是procy对象,不需要.value了。
但是建议对象的响应式创建,直接使用reactive。
四,reactive
reactive只能定义对象类型的响应式数据,使用它,可以像vue2中那样定义所有响应式数据。
let data=reactive({
name:'燕儿',
age:18,
job:{
occupation:'程序员',
salary:'10k'
},
hobby:['刷剧','吃鸡','睡觉']
})
//方法
function say(){
data.job.salary='12k'
data.hobby[0]='学习'
}
data中的所有数据和vue2中的data()中的数据一样,全是响应式的。而且,reactive创建的数据不需要.value来修改值。
五,computed
在vue3中,把computed
变成为组合式API,那么就意味着你要去引入它
import {reactive,computed} from 'vue'
let names=reactive({
a:'阿',
b:'斌'
})
fullName=computed(()=>{
return names.a+'.'+names.b
})
和vue2中一样,计算属性根据你依赖的数据变更,如果依赖的数据没有变更,则不会重新计算。通常,我们并不建议去修改计算属性计算出来的值(fullName)。
六,watch
监听单个响应式数据时,当num发生变化的时候,会执行watch中的函数:
let num=ref('0')
watch(num,(newValue,oldValue)=>{
console.log(`当前数字增加了,${newValue},${oldValue}`)
})
监听reactive:
let names=reactive({
familyName: '鳌',
age:23,
job:{
salary:10
}
})
watch(()=>names.age,(newValue,oldValue)=>{
console.log('names改变了',newValue,oldValue)
})//监听names.age
监听多个属性:
watch([()=>names.age,()=>names.familyName],(newValue,oldValue)=>{
console.log('names改变了',newValue,oldValue)
})
深度监听,有两种写法:
//第一种
watch(()=> names.job.salary,(newValue,oldValue)=>{
console.log('names改变了',newValue,oldValue)
})
//第二种
watch(()=> names.job,(newValue,oldValue)=>{
console.log('names改变了',newValue,oldValue)
},{deep:true})
watch监听computed
const iconMenus = computed(() => store.state.iconMenus.iconList);
const menu_item = ref(null);
watch(
() => iconMenus.value,
(newVal, oldVal) => {
//需要在新的dom渲染完毕后触发。所以需要宏任务包裹下
setTimeout(() => {
iconContentHandle(newVal);
}, 0);
}
);
function iconContentHandle(list) {
if (list.length > 0) {
menu_item.value.children.forEach(element => {
let parentWith = element.offsetWidth;
let childrenWith = element.children[1].offsetWidth;
if (childrenWith >= parentWith - 10) {
element.children[1].classList.add('item-content-flag');
} else {
element.children[1].classList.remove('item-content-flag');
}
});
}
}
七,watchEffect
相比于watch,他有两个特点:
自动默认开启了immediate:true
用到了谁就监视谁, watchEffect 不需要指定监听的属性,他会自动的收集依赖, 只要我们回调中引用到了响应式的属性, 那么当这些属性变更的时候,这个回调都会执行,而 watch 只能监听指定的属性而做出变更(v3开始可以同时指定多个)。
第二点就是 watch 可以获取到新值与旧值(更新前的值),而 watchEffect 是拿不到的。
第三点是 watchEffect 如果存在的话,在组件初始化的时候就会执行一次用以收集依赖(与computed同理),而后收集到的依赖发生变化,这个回调才会再次执行,而 watch 不需要,因为他一开始就指定了依赖。
watchEffect(()=>{
console.log('watchEffect执行了')
})
八,通过ref获取并操作dom元素
<input ref="uploadFile" />
然后在setup中通过ref获取dom元素,又因为是响应式的,所以需要使用.value来使用。
const uploadFile = ref(null);
而后在页面已经渲染之后:
console.log('---', uploadFile.value);
uploadFile.value.click();