setup
vue2.x
了解vue3的setup之前,可以先回顾一下vue2.x中组件传值的写法。
// App.vue
<template>
<div class="app">
App
<Demo name="张三" age="18" />
</div>
</template>
<script>
import Demo from "./components/demo.vue";
export default {
name: "App",
components: {
Demo,
},
};
</script>
<style>
.app {
width: 100%;
height: 100px;
background-color: beige;
}
</style>
// demo
<template>
<div class="demo">demo组件</div>
</template>
<script>
export default {
name: "demo",
props: ["name", "age"],
mounted() {
console.log("this", this);
},
};
</script>
<style>
.demo {
width: 50%;
height: 50px;
background-color: pink;
}
</style>
通过打印this可以看到this上存在name和age
当组件中不写props时,虽然可以在
a
t
t
r
中
也
能
获
取
到
,
但
是
区
别
于
p
r
o
p
s
,
attr中也能获取到,但是区别于props,
attr中也能获取到,但是区别于props,attr中无法对传参进行类型约束,且无法在template中直接使用。
另外,当使用props接收时,$attr上是拿不到name和age。
再来看一下slot,即使没有在组件中使用slot时,会作为虚拟节点显示在 $slot中,使用后就会作为 $slot中的一个对象。
具名插槽,当组件中不接收时
以上分别演示了 $attr和 $slot写法,现在看vue3的setup
-setup的执行时机
- 在beforeCrate之前,并且this是undefined。
setup的参数 - props:值为对象,包含:组件外部传递过来,且组件内部声明接受了的属性。
- context:上下文对象
attrs:值为对象,包含:组件外部传递过来,但没有在props配置中声明的属性,相当于this.$attrs
slots:收到的插槽内容,相当于this. $slots
emit:分发自定义事件的函数,相当于this. $emit
先写一个简单的模板
// views/home.vue
<template>
<Demo></Demo>
</template>
<script>
import Demo from "@/components/demo.vue";
export default {
components: {
Demo,
},
};
</script>
// components/demo.vue
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
export default defineComponent({
name: "Home",
components: {},
setup() {
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
});
</script>
显示正常
首先代码验证setup执行时机,在beforeCrate之前,并且this是undefined。
// components/demo.vue
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
</template>
<script lang="ts">
import { defineComponent, ref, reactive } from "vue";
export default defineComponent({
name: "Home",
components: {},
beforeCreate() {
console.log("---beforeCreate---");
},
setup() {
console.log("---setup---");
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
});
</script>
打印后可以发现,setup执行时机是在beforeCrate之前,且this为undefined
再来看setup的参数
export default {
name: "Home",
setup(a, b, c) {
console.log("a", a);
console.log("b", b);
console.log("c", c);
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
};
先来第一个参数,官方定义props,为组件传值,来写一个例子并且打印
// views/home.vue
<template>
<Demo hobby="学习"></Demo>
</template>
<script>
import Demo from "@/components/demo.vue";
export default {
components: {
Demo,
},
};
</script>
// components/demo.vue
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
</template>
<script>
import { reactive } from "vue";
export default {
name: "Home",
setup(props) {
console.log("props", props);
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
};
</script>
会发现打印的内容是空的,并且有一段警告,意思是外部传递了hobby,组件内部并没有调用。
正确写法与2.x一致
// components/demo.vue
<script>
import { reactive } from "vue";
export default {
name: "Home",
props: ["hobby"],
setup(props) {
console.log("props", props);
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
};
</script>
并且在多传参数的情况下,不会有警告
// components/demo.vue
<script>
import { reactive } from "vue";
export default {
name: "Home",
props: ["hobby", "interest"],
setup(props) {
console.log("props", props);
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
};
正确的打印(再添加一个传值interest),可以发现props将传值整理成一个带有响应式的对象。
第二个参数叫context,可以理解成一个上下文对象
<script>
import { reactive } from "vue";
export default {
name: "Home",
props: ["hobby", "interest"],
setup(props, context) {
console.log("props", props);
console.log("context", context);
const person = reactive({
name: "张三",
age: 18,
});
return {
person,
};
},
};
先打印一下attrs,发现并没有需要的属性
当注释掉props时,再打印attrs
与vue2.x相似,会有个警告和props的数据被attrs接收了,接着试一下事件emit和打印结果
// view/home.vue 父组件
<template>
<Demo @hello="showHobby" hobby="学习" interest="还是学习"></Demo>
</template>
<script>
import Demo from "@/components/demo.vue";
export default {
components: {
Demo,
},
setup() {
function showHobby(val) {
alert(`触发hello事件,收到参数是:${val}`);
}
return {
showHobby,
};
},
};
</script>
// components/demo.vue 子组件
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
<button @click="test">测试组件的hello事件</button>
</template>
<script>
import { reactive } from "vue";
export default {
name: "Home",
props: ["hobby", "interest"],
emits: ["hello"],
setup(props, context) {
console.log("props", props);
// console.log("context.attrs", context.attrs);
console.log("context.attrs", context.emit);
const person = reactive({
name: "张三",
age: 18,
});
function test() {
context.emit("hello", 666);
}
return {
person,
test,
};
},
};
</script>
最后看一下slots
<template>
<Demo @hello="showHobby" hobby="学习" interest="还是学习">
<template v-slot:qwerb>
<span>在家</span>
</template>
</Demo>
</template>
控制台看一下打印结果
<template>
<p>个人信息</p>
<p>姓名:{{ person.name }}</p>
<p>年龄:{{ person.age }}</p>
<p>
<slot name="qwerb"></slot>
</p>
<button @click="test">测试组件的hello事件</button>
</template>