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

Monorepo跨项目导入Nuxt3组件到Storybook无法渲染问题

问题场景

Monorepo架构下,将Nuxt3项目内的组件导入到独立部署的Storybook项目中使用,组件渲染失败。

相关代码

路径:/projects/marketplace/components/Button.vue

<template>
  <a-button type="button" :class="classes">{{ label }}</a-button>
</template>
<!-- a-button为Ant Design Vue组件库提供的组件 -->
<script>
// 其他导入逻辑
export default {
  name: 'my-button',
  props: { /* 相关props定义 */ },
  setup() { /* 组合式逻辑 */ },
};
</script>

路径:/projects/shared_components/src/stories/Button.stories.js

import MyButton from '../../../marketplace/components/Button.vue';

export default {
  title: 'Example/Button',
  component: MyButton,
  argTypes: {
    backgroundColor: { control: 'color' },
    onClick: {},
    size: {
      control: { type: 'select' },
      options: ['small', 'medium', 'large'],
    },
  },
};

const Template = (args) => ({
  components: { MyButton },
  setup() {
    return { args };
  },
  template: '<my-button v-bind="args" />',
});

export const Primary = Template.bind({});
Primary.args = {
  primary: true,
  label: 'Button',
};

报错表现

启动Storybook后组件无法正常渲染,控制台输出警告:

resolveComponent can only be used in render() or setup().

同时抛出Vue运行时错误:

runtime-core.esm-bundler.js:38 [Vue warn]: Invalid VNode type: Symbol(Text) (symbol) 
  at <MyButton primary=true label="Button" onClick=fn<mockConstructor> > 
  at <Anonymous> 
  at <Anonymous> 
  at <App>
根本原因
  • 跨项目导入组件时,Monorepo下两个子项目各自安装了独立的Vue运行时,出现多实例冲突,Vue渲染上下文错乱。
  • Nuxt3自带自动组件注册、自动导入的构建能力,业务目录下的.vue组件默认依赖Nuxt的构建转换逻辑解析依赖(比如示例中的a-button没有显式导入注册,靠Nuxt自动扫描注册),Storybook是普通Vue项目环境,没有Nuxt注入的构建插件和运行时上下文,组件解析失败,最终触发VNode类型错误。
修复步骤

按顺序操作即可解决:

  • 解决多Vue实例冲突
    以pnpm为例,在根目录.npmrc中添加shamefully-hoist=true,把vue、@vue/runtime-core、ant-design-vue这类公共依赖提升到Monorepo根目录,确保所有子项目引用同一个依赖实例。如果用yarn/npm workspaces,在根目录package.json的resolutions字段中锁定上述依赖的版本,强制所有子项目使用同一版本。
  • 配置Storybook构建规则覆盖跨项目导入的组件
    让Storybook的构建工具(Vite/Webpack)把Nuxt项目下的组件目录纳入编译范围,和Storybook本地组件走同一套Vue SFC编译逻辑,不要直接复用Nuxt侧的编译产物。
    如果使用Vite作为Storybook构建器,在.storybook/main.js中添加如下配置:
    module.exports = {
      // 保留原有其他配置
      viteFinal: async (config) => {
        config.optimizeDeps.include.push('/projects/marketplace/components/**/*.vue');
        config.server.fs.allow.push('/projects/marketplace');
        return config;
      }
    }
    
  • 跨项目复用的组件移除Nuxt自动导入依赖
    所有需要跨项目使用的组件,不要依赖Nuxt的自动注册/自动导入能力,用到的第三方组件、工具函数全部显式导入注册,修改Button.vue:
    <template>
      <a-button type="button" :class="classes">{{ label }}</a-button>
    </template>
    <script>
    // 显式导入依赖的第三方组件
    import { Button as AButton } from 'ant-design-vue';
    export default {
      name: 'my-button',
      components: {
        'a-button': AButton
      },
      props: {
        label: {
          type: String,
          default: ''
        }
        // 其余原有props定义
      },
      setup() {
        // 原有组合式逻辑
        return {
          // 原有返回值
        }
      }
    };
    </script>
    
  • (可选)长期方案
    不要直接从Nuxt业务项目目录导入复用组件,把公共组件抽到Monorepo下独立的shared/components包中,包内组件不绑定任何框架专属运行时逻辑,Nuxt项目和Storybook项目都从这个独立共享包导入组件,从根源避免环境差异问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 03:09:37