背景
react 如果想在 ant-design 封装的组件上二次开发, 因为 jsx 的特性, 可以将 dom 作为 props, 所以很简单, 但 vue 就有点麻烦, 需要使用 slot 插槽的方式, 以下是业务开发中时间成功后的记录, 感觉还是学到了挺多东西.
$attrs 和$slots
在具体时间之前需要了解一些前置知识, $attrs
和slots
都是绑定在组件作用域上的私有属性, 不需要在setup
中 return, 同时, 也可以在setup(props, ctx)
的 ctx 上下文中看到 attrs 和 slots 两个属性, 表示的含义都是一样的.
$attrs
先看下官方的解释:
The $attrs object includes all attributes that are not declared by the component’s props or emits options (e.g., class, style, v-on listeners, etc.).
也就是说父组件传入子组件的属性中没被子组件 props 声明的都会包含在$attr
中, 包括 class, style, v-on 这些 dom 原生的属性或 vue 绑定的事件.
这就很适合我们需要二次开发组件的场景, 保证传入的原有属性不受影响的前提下, 传入新的属性进行改造
在子组件中开启$attrs
$attr
想在子组件中使用需要设置inheritAttrs: true
, vue3 默认该设置, 所以可以直接使用
$slots
顾名思义, $attrs
包含了所有传入的非 props 属性, 那$slots
就是包含了传入的所有插槽了
v-bind
$attrs
包含了父组件传入的所有非 props 属性, v-bind="$attrs"
则将所有属性绑定在对应组件标签上
具名插槽和作用域插槽
具名插槽
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <base-layout > <template v-slot:header > <h1 > Here might be a page title</h1 > </template > <template v-slot:default > <p > A paragraph for the main content.</p > <p > And another one.</p > </template > <template v-slot:footer > <p > Here's some contact info</p > </template > </base-layout > <div class ="container" > <header > <h1 > Here might be a page title</h1 > </header > <main > <p > A paragraph for the main content.</p > <p > And another one.</p > </main > <footer > <p > Here's some contact info</p > </footer > </div >
作用域插槽
一般情况下父组件和子组件属于各自的作用域, 作用域插槽则可以让父组件使用插槽时访问子组件的变量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <ul > <li v-for ="( item, index ) in items" > <slot :item ="item" :index ="index" :another-attribute ="anotherAttribute" > </slot > </li > </ul > <todo-list > <template v-slot:default ="slotProps" > <i class ="fas fa-check" > </i > <span class ="green" > {{ slotProps.item }}</span > </template > </todo-list >
其中, v-slot:default
可以简写为#default
, slotProps
为包含绑定在<slot>
上所有属性的对象, 变量名可以自定义, 也可以直接v-slot:default="{item, index, anotherAttribute}"
这样写.
下面这张图可以帮助理解:
二次开发 Table 组件
ant-design 原生的Table
组件不支持将整个表格 disabled, 接下来就要实现该功能,(这也是我业务中所需要的功能)
改造完组件使用方式和 ant-design 原生的Table
相同, 只是支持了表格全局 disabled, 这里使用官方的案例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 <template > <a-table :columns ="columns" :data-source ="data" :disabled > <template #name ="{ text }" > <a > {{ text }}</a > </template > <template #customTitle > <span > <smile-outlined /> Name </span > </template > <template #tags ="{ text: tags }" > <span > <a-tag v-for ="tag in tags" :key ="tag" :color ="tag === 'loser' ? 'volcano' : tag.length > 5 ? 'geekblue' : 'green'" > {{ tag.toUpperCase() }} </a-tag > </span > </template > <template #action ="{ record }" > <span > <a > Invite 一 {{ record.name }}</a > <a-divider type ="vertical" /> <a > Delete</a > <a-divider type ="vertical" /> <a class ="ant-dropdown-link" > More actions <down-outlined /> </a > </span > </template > </a-table ></template > <template > <div class ="table-wrap" > <div :class ="disabled ? 'mask': ''" > </div > <antd-table v-bind ="$attrs" > <template v-for ="(value, key) in $slots" #[key ]="slotProps" > <slot :name ="key" v-bind ="slotProps" /> </template > </antd-table > </div > </template > <script lang ="ts" > import { defineComponent } from "vue" ; import { Table } from "ant-design-vue" ; export default defineComponent ({ name : "ATable" , inheritAttrs : true , props : { disabled : Boolean , }, components : { "antd-table" : Table , }, setup (props, ctx ) { return {}; }, }); </script > <style lang ="scss" scoped > .table-wrap { position : relative; .mask { position : absolute; width : 100% ; height : 100% ; background-color : #f5f5f570 ; z-index : 1 ; } } </style >
reference
v-bind
Fallthrough Attributes (attrs 穿透属性)
具名插槽
slot