You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Storybook中渲染Vue 3动态插槽内容?

问题:Storybook中Vue组件插槽内容渲染异常,显示内部代码

我用以下Storybook代码测试Vue按钮组件,期望渲染出带有“Primary Button”文本的Primary按钮,但实际按钮内显示的是一段Vue内部渲染函数代码:

(...args) => {
        // If a user calls a compiled slot inside a template expression (#1745), it
        // can mess up block tracking, so by default we disable block tracking and
        // force bail out when invoking a compiled slot (indicated by the ._d flag).
        // This isn't necessary if rendering a compiled `<slot>`, so we flip the
        // ._d flag off when invoking the wrapped fn inside `renderSlot`.
        if (renderFnWithContext._d) {
            setBlockTracking(-1);
        }
        const prevInstance = setCurrentRenderingInstance(ctx);
        const res = fn(...args);
        setCurrentRenderingInstance(prevInstance);
        if (renderFnWithContext._d) {
            setBlockTracking(1);
        }
        if (true) {
            devtoolsComponentUpdated(ctx);
        }
        return res;
    }

我的Storybook代码如下:

// ./components/button/Button.stories.js
import AtxButton from './Button';
import ButtonTypes from './Button.types';


export default {
  /* 👇 The title prop is optional.
  * See https://storybook.js.org/docs/vue/configure/overview#configure-story-loading
  * to learn how to generate automatic titles
  */
  title: 'Button',
  component: AtxButton,
  argTypes: {
    default: {
      description: 'The default Vue slot',
      control: 'text'
    }
  }
};

//👇 We create a “template” of how args map to rendering
const Template = (args, { argTypes }) => ({
  props: Object.keys(argTypes),
  components: { AtxButton },
  setup() {
    //👇 The args will now be passed down to the template
    return { args };
  },
  template: `
    <AtxButton v-bind="args">{{ args.default }}
    </AtxButton>
    `
});

//👇 Each story then reuses that template
export const Primary = Template.bind({});
Primary.args = {
  intensity: ButtonTypes.Intensity.Primary,
  default: 'Primary Button'
};

export const Secondary = Template.bind({});
Secondary.args = {
  intensity: ButtonTypes.Intensity.Secondary,
  default: 'Secondary Button'
};

解决方法

问题出在你把Vue的默认插槽当成普通prop来处理了。在Vue中,default是默认插槽的标识,不是组件的prop,直接在模板里写{{ args.default }}会把插槽对应的渲染函数当成普通值输出,所以才会显示那段内部代码。

下面是几种正确的处理方式:

方式一:使用Vue原生插槽语法

修改Template模板,用<slot />来接收插槽内容,Storybook会自动把args里的default内容传入插槽:

const Template = (args, { argTypes }) => ({
  // 过滤掉default,因为它是插槽不是prop
  props: Object.keys(argTypes).filter(key => key !== 'default'),
  components: { AtxButton },
  setup() {
    return { args };
  },
  template: `
    <AtxButton v-bind="args">
      <slot />
    </AtxButton>
  `
});

方式二:自定义参数名替代default

如果想在Controls面板里动态修改插槽文本,可以把default换成自定义参数(比如slotText),避免和Vue插槽的默认标识冲突:

export default {
  title: 'Button',
  component: AtxButton,
  argTypes: {
    slotText: {
      description: '按钮文本(默认插槽内容)',
      control: 'text'
    }
  }
};

const Template = (args) => ({
  components: { AtxButton },
  setup() {
    return { args };
  },
  template: `
    <AtxButton v-bind="args">
      {{ args.slotText }}
    </AtxButton>
  `
});

export const Primary = Template.bind({});
Primary.args = {
  intensity: ButtonTypes.Intensity.Primary,
  slotText: 'Primary Button'
};

export const Secondary = Template.bind({});
Secondary.args = {
  intensity: ButtonTypes.Intensity.Secondary,
  slotText: 'Secondary Button'
};

方式三:直接在模板中指定插槽内容

如果不需要动态修改,也可以直接在Template的模板里写死插槽内容:

const Template = (args) => ({
  components: { AtxButton },
  setup() {
    return { args };
  },
  template: `
    <AtxButton v-bind="args">
      Primary Button
    </AtxButton>
  `
});

以上任意一种方式都能让按钮正确显示预期的文本内容,不会再输出内部渲染代码。


内容的提问来源于stack exchange,提问作者squirrelwhisperer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 07:48:27