Vue3的组合式api
setup函数
composition api 的使用,需要配置一个setup函数
- 从生命周期角度来看,setup会在beforeCreate钩子函数之前执行
- setup中不能使用this,this指向 undefined
- 在模板中需要使用的数据和函数,需要在
setup返回 - setup函数是一个新的组件选项,作为组件中compositionAPI的起点
<script>
export default {
beforeCreate() {
console.log(4,'beforeCreate');
},
setup() {
console.log(4, this);
console.log(9,'setup');
const obj = {
name: "王五",
age: 18,
};
return {
obj: obj,
};
},
};
</script>
<template>
<h1>
{{ obj.age }}
</h1>
</template>
reactive函数
前置说明:
1.setup:需要有返回值,只有返回的值才能在模板中使用
2.默认普通的数据,不是响应式的
作用:传入一个复杂数据类型,将复杂类型数据,转换成响应式数据(返回改对象的响应式代理)
<script>
import { reactive } from "vue";
export default {
setup() {
// 1.setup 需要返回值,返回的值才能在模板中使用
// 2.默认的普通的值不是响应式的,需要用 reactive 函数
const obj = reactive({
name: "zs",
age: 18,
});
const addAge = () => {
obj.age++;
console.log(obj);
};
return { obj, addAge };
},
};
</script>
<template>
<h1>
{{ obj }}
</h1>
<button @click="addAge">年龄加一</button>
</template>
总结: 通常是用来定义响应式 对象数据
ref函数
reactive 处理的数据,必须是复杂类型,如果是简单类型无法处理成响应式,所以有ref函数!
作用:对传入的数据(一般简单数据类型),包裹一层对象,转换成响应式.
- ref函数接收一个的值,返回一个ref响应式对象,又唯一的属性value
- 在setup函数中,通过ref对象的value属性,可以访问到值
- 在模板中,ref属性会自动解套,不需要额外的 .value
- ref函数也支持传入复杂类型,传入复杂类型,也会做响应式处理
<script>
import { ref } from "vue";
export default {
setup() {
const age = ref(9);
console.log(age);
const addAge = () => {
age.value++;
};
return { age, addAge };
},
};
</script>
<template>
<h1>
{{ age }}
</h1>
<button @click="addAge">年龄加一</button>
</template>
ref和reactive的嘴贱使用方式:
- 明确的对象,明确的属性,用reactive,其他用ref
- 从vue3.2之后,更推荐使用ref
script setup 语法
script setup 是在单文件组件(SFC)中使用组合式API的编译时语法糖.相比与普通的script语法更加简洁
要使用这个语法,需要将setup attribute 添到
<script setup>
console.log("hello script setup");
</script>
顶层的绑定会自动暴露给模板,所以定义的变量,函数和import导入的内容都可以直接在模板中直接使用
<template>
<div>
{{ age }}
</div>
<button @click="addAge">+</button>
</template>
<script setup>
import { ref } from "vue";
const age = ref(0);
const addAge = () => {
age.value++;
};
</script>
练习
<template>
<div>
<h3>鼠标x坐标:{{ mouse.x }}</h3>
<h3>鼠标y坐标:{{ mouse.y }}</h3>
<h3>{{ testNum }}</h3>
<button @click="addbtn">+</button>
</div>
</template>
<script setup>
import { onMounted, onUnmounted, reactive, ref } from "vue";
const mouse = reactive({
x: 0,
y: 0,
});
const handleMouse = (e) => {
mouse.x = e.clientX;
mouse.y = e.clientY;
};
onMounted(() => {
document.addEventListener("mousemove", handleMouse);
});
onUnmounted(() => {
document.addEventListener("mousemove", handleMouse);
});
const testNum = ref(0);
const addbtn = () => {
testNum.value++;
};
</script>
计算属性computed函数
computed函数调用时,要接受一个处理函数,处理函数中,需要返回计算属性的值
<template>
<div>今年年龄: <input type="text" v-model.number="age" /></div>
<div>明年年龄: <input type="text" v-model.number="newAge" /></div>
<div>后年年龄: <input type="text" v-model.number="lastAge" /></div>
</template>
<script setup>
import { computed, ref } from "vue";
// 今年年龄
const age = ref(18);
// 不带set的计算属性
const newAge = computed(() => {
return age.value + 1;
});
// 带set的计算属性
const lastAge = computed({
get() {
// console.log(age.value);
return age.value + 2;
},
set(val) {
// console.log(val);
age.value = val - 2;
},
});
</script>
侦听器watch函数
watch监视,接收三个参数
1. 参数1:监视的数据源
2. 参数2:回调函数
3. 参数3:额外的配置
<template>
<div>
<h1>{{ money }}</h1>
<h1>{{ count }}</h1>
<button @click="money -= 250">花掉520</button>
<h3>{{ user }}</h3>
<button @click="user.age += 1">年龄加一</button>
<button @click="user.name += '芜湖'">变名字</button>
</div>
</template>
<script setup>
import { reactive, ref, watch } from "vue";
// 监听单个ref
const money = ref(5200);
watch(money, (val, oldVal) => {
console.log(val, oldVal);
});
// 监听多个ref
// 补充: 参数一可以监听多个参数
const count = ref(0);
watch([money, count], (val, oldVal) => {
console.log(val, oldVal);
});
// 监听ref复杂数据
const user = reactive({
name: "zs",
age: 18,
});
watch(user, () => {
console.log("user对象发生了变化"),
{
deep: true,
immediate: true,
};
});
// 监听对象的某一个属性
watch(
() => {
return user.name.value;
},
() => {
console.log("名字变了");
}
);
</script>
组件通讯-父传子
目标:能够实现组件通讯中得父传子组件通讯
步骤:
- 父组件提供数据
- 父组件将数据传递给子组件
- 子组件通过defineProps进行接收
- 子组件渲染父组件传递得数据
核心代码
父组件
<template>
<div>父组件</div>
<son :money="money" />
</template>
<script setup>
import son from "./components/son.vue";
import { ref } from "vue";
const money = ref(9999);
</script>
子组件
<template>
<div>子组件</div>
<div>{{ money }}</div>
</template>
<script setup>
// 简单写法(较少用,类型不明确)
// defineProps(['money']);
// 指定类型普通写法
defineProps({
// money: {
// typeof: String, //指定类型
// default: 888, //默认值
// },
});
console.log(money);
</script>
注意:如果使用defineProps接收数据,这个数据只能在模板中渲染,如果想要在script中也操作props属性,应该接收返回值
const res = defineProps({
money: String,
});
组件通讯-子传父
目标: 能够实现组件通讯中得子传父
步骤:
- 子组件通过defineEmits获取emit对象(setup中没有this)
- 子组件通过emit触发事件,并且传递数据
- 父组件提供方法
- 父组件通过自定义事件得方式给子组件注册事件
核心代码
子组件
<template>
<div>子组件</div>
<div>{{ money }}</div>
<button @click="btn">花钱</button>
</template>
<script setup>
defineProps({
money: String,
});
// 返回emit对象,用于触发父组件得自定义事件
const emit = defineEmits("changeMoney");
const btn = () => {
emit("changeMoney", 500);
};
</script>
父组件
<template>
<div>父组件</div>
<son :money="money" @changeMoney="changeMoneyFn" />
</template>
<script setup>
import son from "./components/son.vue";
import { ref } from "vue";
const money = ref(9999);
const changeMoneyFn = (val) => {
money.value -= val;
};
</script>
依赖注入-provied和inject
依赖注入,可以非常方便的实现 跨层级的 组件通信
父组件利用provide提供数据
<template>
<div>父组件</div>
<son />
</template>
<script setup>
import { provide, ref } from "vue";
import son from "./components/son.vue";
const money = ref(520);
provide("money", money);
</script>
子组件(子孙后代,都可以拿到这个数据)
<template>
<div>
{{ money }}
</div>
</template>
<script setup>
import { inject } from "vue";
const money = inject("money");
console.log(money);
</script>
模板中ref的使用(通过ref获取dom实例)
联想之前的ref和$refs,获取模板的元素(dom元素,组件)
- 创建ref=>const divRef = ref (null)
- 模板中建立关联 => 钩子函数
- 使用=>divRef.value
<template>
<!-- 2.在模板中建立关联,模板挂在完毕后,自动把DOM节点的内存地址给 ref -->
<div ref="divRef">父组件</div>
<button @click="btn">操作dom</button>
<son />
</template>
<script setup>
import { onMounted, ref } from "vue";
// 1.创建空ref
const divRef = ref(null);
// 3.组件挂载完毕后,就能获取DOM节点了
onMounted(() => {
console.log(13, divRef.value);
});
const btn = () => {
divRef.value.innerText = "芜湖";
};
</script>
ref获取组件实例
父组件
<template>
<son ref="sonCom" />
</template>
<script setup>
import { onMounted, ref } from "vue";
import son from "./components/son.vue";
const sonCom = ref(null);
onMounted(() => {
console.log(12, sonCom.value.money);
console.log(13, sonCom.value.sayHi);
});
</script>
子组件
<template>
<div></div>
</template>
<script setup>
import { ref } from "vue";
const money = ref(88);
const sayHi = () => {
console.log("hello");
};
// 所有的变量或函数都是局部的
// defineExpose 暴露组件内部的数据或方法
defineExpose({
money,
sayHi
});
</script>
vue3中废弃了过滤器
vue3.0中不能使用过滤器,直接使用函数进行代替
<template>
<div>{{ fornmatTime(now) }}</div>
<div>{{ fornmatTime(other) }}</div>
</template>
<script setup>
import dayjs from "dayjs";
const now = new Date();
const other = new Date("2020-11-12 12:00:00");
console.log(now);
const fornmatTime = (val) => {
return dayjs(val).format("hh:mm:ss");
};
</script>
补充-toRefs函数
使用场景:如果对各响应式数据,进行解构 或者 展开,会丢失他的响应式特性!
原因:vue3底层是对 对象 进行监听劫持
作用:对一个响应式对象的所有内部属性,都做响应式处理
- reactive/ref 的响应式功能是赋值给对象的,如果给对象解构或者展开,会让数据丢失响应式的能力
- 使用 torefs 可以保证该对象展开的每个属性都是响应式的
<template>
<div>{{ user }}</div>
<div>name:{{ name }}</div>
<div>age:{{ age }}</div>
<button @click="user.age++">年龄++</button>
</template>
<script setup>
import { reactive, toRefs } from "vue";
const user = reactive({
name: "zs",
age: 18,
});
// console.log(user.name);
const { name, age } = toRefs(user);
</script>