关于vue3中Scope slot实战中使用

今天学了啥-23/03/28
 
vue提供slot机制让组件可以接收模板片段,来渲染模板片段。比如最常见的button组件,基本是如下这样使用。
<FancyButton>
  Click me! <!-- 插槽内容 -->
</FancyButton>
FancyButton 组件则是这样:
<button class="fancy-btn">
  <slot></slot> <!-- 插槽出口 -->
</button>
不过我的重点不是这个基本的slot的使用,我的重点是关于scope slot功能,首先按照vue中对slot的定义,slot是无法访问到子组件的状态。
因为插槽内容是在父组件渲染的,插槽内容的数据也是由父组件定义的。
比如FancyButton 组件只会被父组件引用渲染。如下:
<!--Parent-->
<FancyButton>{{ message }}</FancyButton>
插槽内容无法访问子组件的数据,Vue 模板中的表达式只能访问其定义时所处的作用域,这和 JavaScript 的词法作用域规则是一致的。
父组件模板中的表达式只能访问父组件的作用域;子组件模板中的表达式只能访问子组件的作用域。
比如上面代码中message它是在父组件定义,message的数据也是由父组件提供。它是在父组件渲染,虽然它插入了fancyButton组件中但是无法访问fancybutton组件定义的数据。
 
但是有时候在项目中会遇到这种问题,插槽内容需要访问子组件的数据。
比如已经写好的组件ComponentA和componentB组件,现在我们要扩展componentA组件功能,要在ComponentA中使用ComponentB功能,但是B组件只是在某个功能才需要渲染,如果把componentB直接引入ComponentA,必须时刻使用v-if来控制渲染,有点麻烦。
所以我想到使用scope slot功能,scope slot机制可以让插槽组件访问子组件数据。
用vue3的scope slots文档 的例子,
<!--MyComponent-->
<script>
export default {
  data() {
    return {
      greetingMessage: 'hello'
    }
  }
}
</script>

<template>
  <div>
          <slot :text="greetingMessage" :count="1"></slot>
        </div>
</template>
MyComponent组件内部定义了数据和插槽。
<script>
import MyComponent from './MyComponent.vue'
  
export default {
  components: {
    MyComponent
  }
}
</script>

<template>
        <MyComponent v-slot="slotProps">
          {{ slotProps.text }} {{ slotProps.count }}
  </MyComponent>
</template>
像对组件传递 props 那样,向插槽内容传递 attributes,定义slotProps变量。
此时MyComponent v-slot的slotProps 可以接受到MyComponent组件定义的text和count数据,实际上此时slotProps={text:'hello',count:1} 。那么MyComponent 插槽模板就能接收到数据从而渲染。
你可以把MyComponent例子类别为如下函数:

MyComponent({
  // 类比默认插槽,将其想成一个函数
  default: (slotProps) => {
    return `${slotProps.text} ${slotProps.count}`
  }
})

function MyComponent(slots) {
  const greetingMessage = 'hello'
  return `<div>${
    // 在插槽函数调用时传入 props
    slots.default({ text: greetingMessage, count: 1 })
  }</div>`
}
MyComponent 它的参数slots只接受函数(模板片段),函数内部有定义好的变量,一旦传入参数就会生成新的模板字符串。
slotProps 数据是MyComponent 组件内部提供,实际上这个例子基本不可能,最常见的是数据通过props传递过来的。所以我们要改造下MyComponent 让它更接近实际碰到的样子。
 
<!--MyComponent-->
<script  lang="ts" setup>
import {ref} from 'vue'

const info = ref<any>()
const showInfo =(data:any)=>{
  info.value = data
}

defineExpose({

showInfo
})
</script>

<template>
  <div>
          <slot v-bind="info"></slot>
        </div>
</template>
Parent
<script setup>
import MyComponent from './MyComponent.vue'
import {ref} from 'vue'  
const componentRef =ref()
componentRef.value.showInfo({text:'hello',count:1})

</script>

<template>
        <MyComponent v-slot="slotProps" ref="componentRef">
          {{ slotProps.text }} {{ slotProps.count }}
  </MyComponent>
</template>
在parent组件中使用showInfo将数据传入,Component就能绑定传入的数据。从而进行渲染。

posted on 2023-04-20 21:40  Alice_Fincher  阅读(403)  评论(0)    收藏  举报

导航