_
2024/4/3 00:14:00544

选项式API和组合式API2

前面我们大致了解了一下组合式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

第一个参数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>

参数context

参数context它是一个对象,里面包含如下属性。

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.$slotsslots是组件插槽集,是组件所有默认插槽、具名插槽的集合,可以用来获取当前组件的插槽集。

我们来打印出来看一下,我需要提前来准备两个插槽一个具名插槽,一个默认插槽。

<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 
      }
    }