首页 > 其他分享 >vue3 通过文件名称 插槽里只显示指定名称组件

vue3 通过文件名称 插槽里只显示指定名称组件

时间:2023-02-26 19:56:54浏览次数:60  
标签:vue const 插槽 组件 optionView vue3 文件名称 import type

vue3 过滤插槽里的组件

获取到插槽,也就是VNode,根据type的名称把不符合名称的组件过滤出去

想实现类似ElementUI的select效果,在el-sleect组件中放文本div或是别的组件都不显示只能显示el-option组件。ElementU源码里没找不到是怎么实现的。按道理应该是通过插槽的某些条件做的过滤。

用useSlots().default()打印出来的VNode里面有一个type 里面的__name是它的文件名称,注意:如果<script setup></style>标签里没有写js代码是不会有__name的。因为是直接判断文件名称的没办法区分两个同名组件
image

新建一个父组件 parent.vue引入use-filterSlots.ts

<slot />标签是不能用了,要做的是把处理好的节点用动态组件component循环出来

<template>
  <div class="parent">
    <template v-for="(item, index) in selectItem" :key="index">
      <component :is="item" />
    </template>
  </div>
</template>

<script setup lang="ts">
import filterSlots from './select/use-filterSlots'
import { toRef } from 'vue'
const props = defineProps<{ componentName: string }>()
const name = toRef(props, 'componentName') //引用props传进来的名称 用来动态控制过滤的组件名称
const { selectItem } = filterSlots(name)
</script>

<style>
.parent {
  cursor: pointer;
  min-width: 300px;
  padding: 5px;
  border: 3px solid #ababab;
  border-radius: 8px;
}
</style>

use-filterSlots.ts用来处理VNode数组

import type { ComputedRef, Ref, RendererElement, RendererNode, VNode } from 'vue';
import { computed, ref, unref, useSlots, watchEffect } from 'vue';

export type VNodeItem = VNode<RendererNode, RendererElement, { [key: string]: any; }>
export type Callback = (vnode: VNodeItem) => boolean
export type FilterComponents = (
  option: string | Callback | Ref<string | Callback> | ComputedRef<string | Callback>,
  ...componentName: string[]
) => { selectItem: Ref<VNodeItem[]>, slotsList: ComputedRef<VNodeItem[]> }

/**
 * 过滤插槽组件保留指定组件
 * @param { * } item 要保留组件名称或者传入一个回调函数
 * @param { * } componentName 多个组件名称
 * @returns { { selectItem, slotsList} } 
 */
const filterSlots: FilterComponents = (item, ...componentName) => {

  const slots = useSlots()
  const slotsList = computed(() => slots.default ? slots.default() : [])//默认插槽内容 过滤前

  const selectItem = ref<VNodeItem[]>([])//存放过滤后

  // 根据type.__name判断 把对应相同名称的组件添加到selectItem
  watchEffect(() => {

    const option = unref(item)//转换Ref
    const vnodeList = [option, ...componentName].filter(String)//要保留的组件名称

    let optionsItem: VNodeItem[] = []//临时存放过滤后的VNode

    // 判断名称
    const condition = (vNode: VNodeItem): boolean => {
      let type = vNode.type as string | { [key: string]: string }

      // 传入参数是function就直接使用function返回的判断结果
      if (typeof option === 'function') {
        return !!option(vNode)
      }

      // type是object为vue组件  判断type.__name是否存在组件名称列表中
      if (typeof type === 'object') {
        return vnodeList.includes(type.__name)
      }
      // 原生标签
      return vnodeList.includes(type)
    }

    slotsList.value.forEach((a) => {
      // 判断是否有template包裹 有就需要把template里面的拿出来
      if (Array.isArray(a.children) && typeof a.type === 'symbol') {
        let children = a.children as VNodeItem[]
        optionsItem.push(...children.filter(condition))
        return
      }
      // 判断名称对应就添加到optionsItem
      condition(a) && optionsItem.push(a)
    })
    // 把过滤后的Vnode更新到 selectItem
    selectItem.value = optionsItem
  })

  return {
    slotsList,//过滤前
    selectItem,//过滤后 
  }
}

export default filterSlots

parent.vue组件引入到App.vue中

<script setup lang="ts">
import { ref } from 'vue'
import optionView from './optionView.vue'
import Parentl from './parentl.vue'
import ikun from './ikun.vue'

const componentName = ref('ikun') //控制显示的组件
</script>

<template>
  <button @click="componentName = 'optionView'">optionView</button>
  <button @click="componentName = 'div'">div</button>
  <button @click="componentName = 'span'">span</button>
  <button @click="componentName = 'ikun'">ikun</button>

  <Parentl :componentName="componentName">
    3211312113
    <optionView>optionView</optionView>
    <optionView>optionView</optionView>
    <optionView>optionView</optionView>
    <ikun>ikun</ikun>
    <ikun>ikun</ikun>
    <ikun>ikun</ikun>
    <ikun>ikun</ikun>
    <optionView v-for="_ in 10">optionView{{ _ }}</optionView>
    <div v-for="_ in 10">div{{ _ }}</div>
    <span v-for="_ in 5">span{{ _ }}</span>
    <template v-for="_ in 10">
      <optionView>optionView</optionView>
      <div v-for="_ in 10">div</div>
      <span v-for="_ in 5">span</span>
    </template>
  </Parentl>
</template>

最终效果

image

标签:vue,const,插槽,组件,optionView,vue3,文件名称,import,type
From: https://www.cnblogs.com/zsyh/p/17157381.html

相关文章

  • 使用vue3重构项目的一点思考
    vue3如何做整页面复用?场景是,monorepo项目,子项目间总有可以复用的页面,比如说登录页、用户管理和权限管理等。这些页面大部分情况下都相同,但可能具有不同的用户权限分类,有......
  • 11_09_第六阶段:大前端进阶||07-Vue详解||P11:插槽slot【Vue的缩写】【观看狂神随笔】
    Vue:计算属性,【内容分发】,自定义事件1.内容分发[slot]2.代码演示第一步:定义一个待办事项的组件<divid="vue"><todo></todo></div><scripttype="text/javascript">......
  • 【vue3】vue3+ts+vite项目创建
    1、npminitvite@lastest 2、项目目录结构  3、npminstall(i) 安装依赖(是根据package.json中devDependencies的依赖安装的)启动命令 dev  打包命令build预......
  • Vue3 + Vite +TS 项目问题总结
    最近做的几个Vue项目基本都收尾了,总结一下在项目中遇到的问题,希望能帮助遇到同样问题的小伙伴项目情况:我做的项目都是Vue3.2(setup语法)+Vite+TS,一个H5项目,一个PC前......
  • 【vue3】实现全屏功能
    前言全屏效果:实现安装依赖包npmi@vueuse/core调用import{useFullscreen}from'@vueuse/core'useFullscreen的使用文档:https://vueuse.org/core/useFull......
  • Vue3学习笔记(1)
    安装//使用yarn构建//安装yarn需要管理员权限sudonpmiyarn-gyarncreatevitecd..yarnyarndev目录结构见名知义四种......
  • vue3兄弟组件传参
    兄弟组件传参数mitt使用方式和vue2的事件大巴类似。安装npmimitt-S新建plugin/Bus.jsimportmittfrom'mitt'cosntemitter=mitt()exportdefaultemitterHome.vue<t......
  • vue3插槽
    父级组件中使用 ​​<A></A>​​并且不提供任何插槽内容时,子组件中 ​​<slot></slot>​​标签中的内容当作默认内容展示。v-slot只能添加在​​<template​​(一种情......
  • Vue3中url传递参数通过全局前置守卫接收参数,利用store存储并控制DOM显示或隐藏(记录一
    业务场景:根据路由传递的标志eg:hidden来控制侧边栏和横条显示或隐藏核心代码:<a-layout-headerclass="header"v-if="!(parseInt(hidden)===1?true:false)">......
  • 一文搞懂Vue3中如何使用ref获取元素节点?
    一文搞懂Vue3中如何使用ref获取元素节点?会飞的猪开源网站:91huajian.cn 29人赞同了该文章​展开目录 前言虽然在Vue中......