vue3中常用的Compositon API
setup
1》所有的组合API函数都在这里使用,只在初始化的时候执行一次;
2》函数如果返回对象,对象中的属性和方法,模板中可以直接使用
基本语法结构
setup(props,{attrs,slots,emit}){
return {}
}
setup的执行时机
在beforeCreate之前执行一次
由于在beforeCreate之前执行,所以this为undefined,也就不能通过this来访问data method computed prop
所有的composition API都不可以
setup的返回值
一般都返回一个对象
setup对象中的属性和data返回的对象中的属性都可以在html模板中使用,会合并为组件对象的属性
setup对象中的方法和method中的方法会合并为组件对象的方法
在Vue3中尽量不要混合使用data和setup,methods和setup;如果有重名,setup优先
setup不能是一个async函数,因为返回值不再是return的对象,二是promise对象了
在mounted等原来vue就有的钩子函数,方法等里面,是可以通过this来访问setup中的属性,调用里面的方法,而且不需要通过value来获取;但是在setup内部方法访问内部属性,需要通过value来获取
setup的参数
1>props:是一个对象,里面有父组件向子组件传递的数据,并且是在子组件中使用props接收到的所有属性
2>context:是一个对象,里面有attrs对象 ,emit方法,slots对象
2.1>attars:获取当前对象组件标签上的所有属性,但是这些属性是在props中没有声明接收的
2.2>emit方法:用来定义和分发事件,相当于this.$emit
2.3>slots对象:插槽
ref
作用1:定义一个简单数据(基本数据)类型的响应式;其实也可以定义复杂数据,只不过使用起来没那么方便
const name=ref(value)
返回一个ref对象,如果是在js中则需要通过操作ref对象的value值来操作这个变量,如果是html中则不需要
作用2:获取元素
<template>
<div ref="refBox">ref获取页面元素</div>
</template>
<script lang="ts">
import { defineComponent, ref, onMounted } from "vue";
export default defineComponent({
setup() {
const refBox = ref<HTMLElement | null>(null);
onMounted(() => {
refBox.value && (refBox.value.style.color = "red");
});
return {
refBox,
};
},
});
</script>
reactive
定义复杂数据类型(引用型)的响应式
const proxy=reactive(obj)
响应式是”深层的“,内部是通过ES6的Proxy实现的
ref和reactive简单使用实例:
<template>
<h4>setup以及ref,reactive的基本使用</h4>
<div>{{ count }}</div>
<button @click="updateCount">修改count</button>
<hr />
<div>性名:{{ proxyObj.name }}</div>
<div>女友:{{ proxyObj.girlfriend.name }}</div>
<button @click="modifyObj">修改对象信息</button>
</template>
<script lang="ts">
import { defineComponent, reactive, ref } from "vue";
export default defineComponent({
name: "App",
setup() {
// ------ref定义响应式数据(简单类型)--------
//let count = 0; // 此时变量count不是响应式的(响应式:数据改变,视图跟着改变)
// ref用来定义响应式数据的函数,返回一个Ref对象,对象中有value属性;如果需要对数据进行操作,需要使用Ref对象调用value属性的方式修改,但是html模板中是不需要调用value的方式进行数据的读写,渲染
let count = ref(0);
function updateCount() {
// count++;使用了ref后count就是一个响应式的数据了,但是它返回的是一个Ref对象,通过这个对象的value属性来读取和修改
count.value++;
}
// -----reactive定义响应式数据(复杂类型)---------
let obj = {
name: "郝建",
age: 24,
girlfriend: {
name: "小甜甜",
age: 23,
},
};
// proxyObj:代理对象,是一个Proxy对象;obj:目标对象
// reactive返回的是一个Proxy对象,被代理的是obj对象,也叫做目标对象
const proxyObj = reactive(obj);
function modifyObj() {
// 如果操作目标对象obj来修改值,那就不是响应式了
proxyObj.name = "luca";
proxyObj.girlfriend.name = "lucy";
}
return {
count,
updateCount,
proxyObj,
modifyObj,
};
},
});
</script>
点击后页面会同步变化
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bdeZeYtI-1646054589096)(C:\Users\lucas\Desktop\学习\图片\xiangyingshi.jpg)]
如果操作代理对象,目标对象中的数据也会随之变化
所以如果想要在 操作数据的时候,界面也要跟着重新变化,就使用代理对象
ref和reactive对比
ref用来处理基本数据类型,reactive用来处理复杂数据类型(递归深度响应式)
ref其实也可以传入对象或数组,不过内部会自动将其转换为reactive的代理对象,所以对象或数组直接使用reactive就行,ref在setup中获取还需要点value
ref内部通过给value属性添加getter和setter来实现数据劫持,reactive内部通过proxy来实现对对象内部所有数据的劫持,并通过reflect操作对象内部数据
vue3响应式原理
vue2:defineProperty
缺点:给对象或数组添加数据的时候,不是响应式的,需要使用$set来修改
vue3:Proxy和Reflect
let obj = {
name: "张三",
age: 25,
wife: {
name: "马冬梅",
age: 24,
creatrues: ["funny", "big eyes", "brave"],
},
};
const proxyObj = new Proxy(obj, {
get(targetObj, prop) {
return Reflect.get(targetObj, prop);
},
// 不仅可以修改属性,还能添加属性
set(targetObj, prop, val) {
return Reflect.set(targetObj, prop, val);
},
deleteProperty(targetObj, prop) {
return Reflect.deleteProperty(targetObj, prop);
},
});
计算属性computed和监听watch/watchEffect
<template>
<div>app组件</div>
<div>请输入姓:<input type="text" v-model="user.firstName" /></div>
<div>请输入名:<input type="text" v-model="user.lastName" /></div>
<hr />
computed计算属性姓名fullName:<input type="text" v-model="fullName" /><br />
computed计算属性姓名fullName2: <input type="text" v-model="fullName2" /><br />
watch监听姓名fullName3: <input type="text" v-model="fullName3" /><br />
watchEffect监听姓名fullName4: <input type="text" v-model="fullName3" />
</template>
<script>
import {
defineComponent,
reactive,
ref,
computed,
watch,
watchEffect,
} from "vue";
export default defineComponent({
setup() {
const user = reactive({
firstName: "东方",
lastName: "明月",
});
// 1>计算属性computed(如果只有一个回调,则是只读)
const fullName = computed(() => {
return user.firstName + user.lastName;
});
// 2>通过计算属性的大fullName2.修改fullName2,姓和名随之改变(可读可改)
const fullName2 = computed({
get() {
return user.firstName + user.lastName;
},
set(val) {
user.firstName = val.substr(0, 1);
user.lastName = val.substr(1);
},
});
const fullName3 = ref("");
// watch,监听姓和名的输入框,修改fullName3
watch(
user,
(newVal) => {
fullName3.value = newVal.firstName + newVal.lastName;
},
{ immediate: true, deep: true }
);
// watchEffect监听,自带immediate和deep
const fullName4 = ref("");
watchEffect(user, () => {
fullName4.value = user.firstName + user.lastName;
});
return {
user,
fullName,
fullName2,
fullName3,
fullName4,
};
},
});
</script>
computed:接收回调函数;如果只有一个回调,则只读,如果有两回调(get,set),则是可读可改的响应式
watchEffect会默认执行一次(immediate),而且自带deep深度监听
生命周期钩子函数
1》都写在setup里面,切且加了 on ;接收一个回调函数
2》没有了beforeCreated和created;这里的操作可以直接写在setup中
3》beforeDestroy/destroyed变为了beforeUnmounted/unmounted
4》setup中对应的钩子比vue2中写的对应钩子要先执行
自定义hook函数
使用vue3组合API封装的可复用的功能函数;类似于vue2中的mixin
vue组件
<template>
<h4>x:{{ x }}</h4>
<h4>y:{{ y }}</h4>
</template>
<script>
import { defineComponent } from "vue";
import clickPositionHook from "././components/hook";
export default defineComponent({
setup() {
const { x, y } = clickPositionHook();
return {
x,
y,
};
},
});
</script>
hook.js文件
import { ref, onMounted, onBeforeUnmount } from "vue";
export default function () {
const x = ref(0);
const y = ref(0);
function handleClick(e) {
x.value = e.clientX;
y.value = e.clientY
}
onMounted(() => {
window.addEventListener("click", handleClick);
});
onBeforeUnmount(() => {
window.removeEventListener("click", handleClick);
});
return {
x,
y,
};
}
toRefs
将响应式对象变为一个普通对象 ,但是里面每一个属性都是ref对象,仍然是响应式的
如果直接结构一个响应式对象,那么得到的属性就不具有响应式
使用场景:hook返回的是一个响应式对象,我们想直接结构使用时
<script lang="ts">
import { defineComponent, reactive, toRefs } from "vue";
// toRefs的使用场景,hook返回一个对象,想直接使用其属性名
function hook() {
const person = {
name2: "张三",
age2: 24,
};
setInterval(() => {
person.name2 += "疯";
}, 1000);
return {
...toRefs(person),
};
}
export default defineComponent({
setup() {
const user = reactive({
name: "卢卡",
age: 20,
});
const { name2, age2 } = hook();
// 方案2:直接结构user;这样也不是响应式
// let { name, age } = user;
// 方案3:使用 toRefs
let { name, age } = toRefs(user);
setInterval(() => {
// 方案1 return 的时候,对user结构赋值
// user.name += "=";
// console.log(user.name);
// 方案 2:不是响应式
// name += "=";
// console.log(name);
// 方案3:使用 toRefs
name.value += "=";
}, 2000);
return {
// ...user, 方案1,出来的时候结构赋值,但是这样结构出来的就不是响应式的数据了
name,
age,
name2,
age2,
};
},
});
</script>
() => {
// 方案1 return 的时候,对user结构赋值
// user.name += “=”;
// console.log(user.name);
// 方案 2:不是响应式
// name += “=”;
// console.log(name);
// 方案3:使用 toRefs
name.value += “=”;
}, 2000);
return {
// …user, 方案1,出来的时候结构赋值,但是这样结构出来的就不是响应式的数据了
name,
age,
name2,
age2,
};
},
});