Next.js 13与Storyblok循环依赖问题求助
解决Next.js 13 + Storyblok循环依赖导致的预渲染错误
问题根源
你遇到的ReferenceError: Cannot access '__WEBPACK_DEFAULT_EXPORT__' before initialization确实是循环依赖导致的:
component-list.js导入所有业务组件并导出ComponentsMapStoryblokComponent.js依赖ComponentsMap匹配渲染组件- 业务组件(如Grid)又依赖
StoryblokComponent渲染嵌套内容
三者形成引用闭环,导致Webpack在预渲染阶段无法正确初始化模块。
解决方案1:动态导入组件(推荐)
通过动态导入打破循环依赖,不在component-list中直接导入所有组件,而是在需要时按需加载:
1. 重构component-list.js
将静态导入改为动态导入的函数:
// components-list.js export const getComponent = async (componentKey) => { switch(componentKey) { case 'page': return (await import('@components/contentTypes/Page')).default; case 'news': return (await import('@components/contentTypes/News')).default; case 'newsArticle': return (await import('@components/contentTypes/NewsArticle')).default; case 'grid': return (await import('./grid/Grid')).default; case 'feature': return (await import('./feature/Feature')).default; case 'teaser': return (await import('./teaser/Teaser')).default; case 'config': return (await import('./config/Config')).default; case 'menu_header': return (await import('./config/MenuHeader')).default; case 'menu_link': return (await import('./config/MenuLink')).default; case 'menu_group': return (await import('./config/MenuGroup')).default; default: console.error(`Component ${componentKey} doesn't exist.`); return null; } }
2. 修改StoryblokComponent.js使用React.lazy和Suspense
利用React的懒加载能力处理动态导入的组件:
// StoryblokComponent.js import React, { lazy, Suspense } from 'react'; import { getComponent } from './components-list'; const StoryblokComponent = ({ blok, ...restProps }) => { if (!blok) { console.error("Please provide a 'blok' property to the StoryblokComponent"); return <div>Please provide a blok property to the StoryblokComponent</div>; } const LazyComponent = lazy(() => getComponent(blok.component).then(comp => ({ default: comp })) ); return ( <Suspense fallback={<div>Loading...</div>}> <LazyComponent blok={blok} {...restProps} /> </Suspense> ); }; export default StoryblokComponent;
3. 业务组件无需修改
原来的Grid等组件保持原样即可,动态导入会自动打破循环依赖。
解决方案2:通过props传递渲染函数规避直接导入
如果不想用动态导入,可以让业务组件不直接导入StoryblokComponent,而是通过props接收渲染函数:
1. 修改业务组件(以Grid为例)
移除StoryblokComponent的导入,改用props传入的renderBlok函数:
// Grid.js import { storyblokEditable } from '@storyblok/js'; const Grid = ({ blok, renderBlok }) => { return ( <div {...storyblokEditable(blok)}> {blok.columns.map((nestedBlok) => ( <div key={nestedBlok._uid}> {renderBlok(nestedBlok)} </div> ))} </div> ); }; export default Grid;
2. 修改StoryblokComponent.js传递渲染函数
在渲染组件时,将自身作为renderBlok传递给子组件:
// StoryblokComponent.js import React from 'react'; import { ComponentsMap } from './components-list'; export const StoryblokComponent = ({ blok, ...restProps }) => { if (!blok) { console.error("Please provide a 'blok' property to the StoryblokComponent"); return <div>Please provide a blok property to the StoryblokComponent</div>; } if (blok.component) { const Component = ComponentsMap[blok.component]; if (Component) { return ( <Component blok={blok} renderBlok={(childBlok) => <StoryblokComponent blok={childBlok} {...restProps} />} {...restProps} /> ); } } return <> </>; };
注意:此方法需确保所有嵌套组件都接受
renderBlokprops,且Next.js预渲染时可能需要额外处理序列化问题,不如动态导入方案通用。
内容的提问来源于stack exchange,提问作者mattisvensson
相关产品推荐
相关产品推荐

