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
相关产品推荐
相关产品推荐

