无渲染Vue图标组件的class属性被覆盖无法合并如何解决
实现方案
Vue 3 环境实现
方案1:模板写法(Icon内置svg标签,插槽放图标路径)
如果你的Icon.vue是固定内置<svg>标签,插槽仅用来承载内部路径等内容,可以直接合并class后绑定:
<!-- Icon.vue --> <template> <svg xmlns="http://www.w3.org/2000/svg" :class="['icon', $attrs.class]" aria-hidden="true" v-bind="$attrs" > <slot /> </svg> </template>
方案2:无渲染组件写法(动态处理插槽内的svg元素)
如果你的Icon.vue是纯无渲染逻辑,需要动态给插槽内任意svg元素添加属性,用渲染函数处理VNode即可:
<!-- Icon.vue --> <script setup> import { useSlots, useAttrs } from 'vue' const slots = useSlots() const attrs = useAttrs() const mergeClass = (...classes) => classes.filter(Boolean).join(' ') const renderSlotSvg = () => { const slotContent = slots.default?.() if (!slotContent) return null return slotContent.map(vnode => { if (vnode.type !== 'svg') return vnode vnode.props = vnode.props || {} // 合并内置icon类、svg原有class、外部传入的自定义class vnode.props.class = mergeClass('icon', vnode.props.class, attrs.class) // 注入其他通用属性 vnode.props.xmlns = 'http://www.w3.org/2000/svg' vnode.props['aria-hidden'] = true return vnode }) } </script> <template> <renderSlotSvg /> </template>
Vue 2 环境实现
Vue 2 推荐用函数式组件实现无渲染逻辑,注意VNode的属性存储在data字段下:
<!-- Icon.vue --> <script> export default { functional: true, render(h, context) { const slotContent = context.slots().default if (!slotContent) return null const mergeClass = (...classes) => classes.filter(Boolean).join(' ') return slotContent.map(vnode => { if (vnode.tag !== 'svg') return vnode vnode.data = vnode.data || {} // 合并三类class vnode.data.class = mergeClass('icon', vnode.data.class, context.data.class) // 注入通用属性 vnode.data.attrs = vnode.data.attrs || {} vnode.data.attrs.xmlns = 'http://www.w3.org/2000/svg' vnode.data.attrs['aria-hidden'] = true return vnode }) } } </script>
核心逻辑
所有方案的核心都是避免直接给svg写死class="icon"覆盖外部传入值,改为将内置的icon类、svg原有class、上层组件透传的自定义class做合并后再绑定,全程不需要修改任何已封装的业务图标组件。
内容的提问来源于stack exchange,提问作者Muhammad Rehan Saeed
相关产品推荐
相关产品推荐

