Class 属性在无渲染 Vue 组件中被覆盖

Class Attribute Overwritten in Renderless Vue Component

我构建了一个无渲染的基本图标 Vue 组件,它只输出其插槽中的任何内容,但向插槽组件添加了一些附加属性。我设置的属性之一是 class="icon",它允许我将所有图标的样式集中在一个地方。

Icon.vue

<script>
import Vue from "vue";

export default {
  name: "u-icon",
  render(createElement) {
    const svg = this.$slots.default[0];
    const attrs = svg.data.attrs;

    attrs.xmlns = "http://www.w3.org/2000/svg";
    attrs.class = "icon";
    attrs["aria-hidden"] = "true";
    attrs.focusable = "false";
    attrs.role = "img";

    return this.$slots.default;
  },
};
</script>

<style>
.icon {
  // ...
}
</style>

关闭-Icon.vue

然后我可以更轻松地创建许多不同的图标组件,例如这个关闭图标组件。

<template>
  <u-icon>
    <svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2">
      <line x1="4" y1="4" x2="20" y2="20"></line>
      <line x1="4" y1="20" x2="20" y2="4"></line>
    </svg>
  </u-icon>
</template>

<script>
import icon from "~/icon.vue";
export default {
  name: "close-icon",
  components: {
    "u-icon": icon,
  },
};
</script>

实际结果

问题是当我使用 close-icon 组件并像这样设置 class 时:

<close-icon class="foo"/>

呈现的输出未合并两个 classes "icon foo"

<svg class="foo" ...>
  ...
</svg>

预期结果

如何确保 class 不被覆盖而是附加到输出,以便输出为:

<svg class="icon foo" ...>
  ...
</svg>

如果可能,我所做的任何更改都应该对 Icon.vue 进行,并且我想避免对 Close-Icon.vue 进行更改,因为还有大量其他图标。

classstyle 绑定不是 $attrs - source

的一部分

我不确定为什么你的代码即使没有 close-icon class 绑定也能工作 但是 如果你想 add/modify classes 现有 VNode(来自默认插槽),您应该使用 svg.data.staticClass 而不是 svg.data.attrs.class

下面的示例甚至处理了静态 class 也直接放置在 close-icon 内的 svg 元素上的情况 - 呈现 <svg class="foo bar icon" ...

Vue.component("u-icon", {  
  render(createElement) {
    const svg = this.$slots.default[0];
    const attrs = svg.data.attrs;
    
    attrs.xmlns = "http://www.w3.org/2000/svg";
    attrs["aria-hidden"] = "true";
    attrs.focusable = "false";
    attrs.role = "img";
    
    svg.data.staticClass = (svg.data.staticClass || "") + " icon";

    return this.$slots.default;
  },
});

Vue.component("close-icon", {
  template:`
  <u-icon>
    <svg viewBox="0 0 24 24" stroke="currentColor" stroke-width="2" class="bar">
      <line x1="4" y1="4" x2="20" y2="20"></line>
      <line x1="4" y1="20" x2="20" y2="4"></line>
    </svg>
  </u-icon>
  `
})

new Vue({
  el: "#app"
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.14/vue.js"></script>
<div id="app">
  <close-icon class="foo"></close-icon>
</div>