前面我们大致了解了一下组合式API,在组合式API里面我们组件的所有逻辑代码都应该写到setup
方法里面。那么setup
方法它其实可以接受两个参数setup(props,context)
,这节课我们就来看一下这两个参数它的实际作用。我们来写一个HelloWorld
组件来给大家演示一下
<template>
<div>{{ message }}</div>
</template>
<script>
import {ref} from 'vue'
export default {
setup(props,context){
const message = ref("hello world!")
return{
message
}
}
}
</script>
然后在APP根组件里面来使用这个组件
<template>
<div>
<HelloWorld/>
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
export default {
components: { HelloWorld },
}
</script>
第一个参数props,它的主要作用其实就是用来接收,组件属性传值的,假设我们给helloWorld
组件传递一个msg
属性,值为hello vue
。
<HelloWorld msg="hello vue" />
在hello world
里面我们来接收一下这个msg
。我们需要先到props
里面来定义一下这个属性。
props:['msg'],
在之前选项式api中我们想要获取组件传值的话是通过this.msg
这样来获取的,那么组合式API我们在setup
方法里面就可以通过props
参数来获取到msg
属性的值。
<template>
<div>{{ message }}</div>
</template>
<script>
import { ref,onMounted} from 'vue'
export default {
props:['msg'],
setup(props,context){
const message = ref("hello world!")
onMounted(()=>{
console.log(props) //===> Proxy(Object) {msg: 'hello vue'}
message.value = props.msg
})
return{
message
}
}
}
</script>
所以参数props
主要就是用来在setup
方法里面获取组件传值的。如果我们不需要在setup里面获取这个值,单纯只是在template
里面来渲染的话我们可以不需要用props
来获取,可以直接到template
里面渲染。
<div>{{ msg }}</div>
参数contex
t它是一个对象,里面包含如下属性。
context: {
attrs: Data;
slots: Readonly<InternalSlots>;
emit: (event: string, ...args: any[]) => void;
expose: (exposed?: Record<string, any>) => void;
}
attrs
这是属性就是我们之前学过的Non-props,之前在选项式API里面我们可以使用this.$attrs
来访问这些属性。
Non-props
特性,当我们传递数据给子组件的时候,子组件没有设置相应的props属性来接受数据,那么它会默认把传递过来的数据和属性名渲染到子组件最外层的dom节点上。
我们可以打印出来看一下。
<template>
<div>{{ message }}</div>
</template>
<script>
import { ref,onMounted} from 'vue'
export default {
setup(props,context){
const message = ref("hello world!")
onMounted(()=>{
const {attrs,slots,emit,expose} = context
console.log(attrs)
//打印结果 Proxy(Object) {msg: 'hello vue', __vInternal: 1}
})
return{
message
}
}
}
</script>
slots
这个参数就跟插槽有关了,对标的选项式API中的this.$slots
,slots
是组件插槽集,是组件所有默认插槽、具名插槽的集合,可以用来获取当前组件的插槽集。
我们来打印出来看一下,我需要提前来准备两个插槽一个具名插槽,一个默认插槽。
<template>
<div>
{{ message }}
<slot />
<slot name="slot1" />
</div>
</template>
<script>
import { ref,onMounted} from 'vue'
export default {
// props:['msg'],
setup(props,context){
const message = ref("hello world!")
const slotObj = ref(null)
onMounted(()=>{
const {attrs,slots,emit,expose} = context
console.log(slots) // Proxy(Object) {_: 1, __vInternal: 1, slot1: ƒ, default: ƒ}
console.log(slots.default()) //打印出来一个object 其实就虚拟dom
})
return{
message
}
}
}
</script>
emit
参数emit
它时一个方法,对比选项式API中的this.$emit
,就是在组件里面向外抛出一个自定义事件。
helloWolrd
组件代码
<template>
<div>
{{ message }}
<button @click="handleClick">点击我</button>
</div>
</template>
<script>
import { ref,onMounted} from 'vue'
export default {
emits:["btnClick"],
setup(props,context){
const message = ref("hello world!")
const { attrs, slots, emit, expose } = context
function handleClick() {
emit("btnClick")
}
return{
message,
handleClick
}
}
}
</script>
之前在选项式API我们想要在模版中能够触发一个函数的话,我们只需要在methods
属性里面声明方法就行了。
methods:{
handleClick(){
....
}
}
那么在组合式API里面我们需要在setup
里面进行声明然后return
暴露出去。
app根组件代码
<template>
<div>
<HelloWorld msg="hello vue" @btn-click="handelBtnClick" />
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
export default {
components: { HelloWorld },
setup(){
const handelBtnClick = ()=>{
alert("按钮被点击了")
}
return {
handelBtnClick
}
}
}
</script>
expose
这个参数是个方法,主要是用来控制组件被引用时,向外暴露状态或者方法。比如如下代码:
<script>
import { ref,onMounted} from 'vue'
export default {
setup(props,context){
const message = ref("hello world!")
const { attrs, slots, emit, expose } = context
function handleClick() {
console.log("调用到我了")
}
expose({ handleClick })
//我们通过expose 暴露了一个handleClick方法
//那么我们在父组件里面通过ref组件实例只能访问到
//handleClick方法其它的状态和函数都访问不到
return{
message,
handleClick
}
}
}
</script>
然后我们在根组件里面通过ref来获取helloWorld组件的实例来访问它里面暴露的方法或者变量。这里ref
和选项式api
里面this.$refs
是一样的,这里我们先暂时忽略不讲,后期起在给大家讲组合api ref
的写法
<template>
<div>
<HelloWorld msg="hello vue" ref="child" />
</div>
</template>
<script>
import HelloWorld from '@/components/HelloWorld.vue'
import { onMounted,ref} from 'vue';
export default {
components: { HelloWorld },
setup(){
const child = ref(null)
onMounted(()=>{
child.value.handleClick()
//可以访问HelloWorld组件内部的 handleClick方法
console.log(child.value.message)
//message变量因为没有暴露所以是访问不到的,暴露之后就可以访问了
})
return{
child
}
}
}
</script>
如果expose()
什么都没有传递,就表示让组件实例处于 “关闭状态” , 不会向父组件暴露任何东西。打印出来的都会是undefine
。
setup(){
const child = ref(null)
onMounted(()=>{
console.log(child.value.handleClick) //==>undefine
console.log(child.value.message) //==>undefine
})
return{
child
}
}