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

在Next.js项目中引入含Loadable-Components的自定义组件库报错求助

问题:组件库使用Loadable-Components做代码分割后,在Next.js项目中无法导入组件

我开发了一个可复用组件库(项目A),使用Loadable-Components结合Webpack实现了代码分割,但将其引入Next.js项目(项目B)时,出现了无法导入组件的错误,报错信息如下:

(node:20271) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'SomeComponent' of undefined

项目A配置详情

Webpack构建配置(webpack.build.config.js)

module.exports = { 
 ...
 entry: { [packageName]: './src/index.js' },
 target: 'web',
 output: {
  chunkFilename: '[chunkhash].chunk.js',
  library: packageName,
  libraryTarget: 'commonjs2',
  globalObject: 'this'
 },
 externals: nodeExternals(),
 optimization: {
  minimize: true,
  usedExports: true,
  splitChunks: {
   chunks: 'all',
  },
 },
 plugins: [ new LoadablePlugin() ],
 ...
}

入口文件(src/index.js)

import styled, { ServerStyleSheet, createGlobalStyle } from 'styled-components';
export * from '@components';
export * from '@style';
export { default as Icons } from '@assets/icons';
export { styled, ServerStyleSheet, createGlobalStyle };

注:仅@components目录中使用了Loadable-Components

Babel配置(.babelrc)

{ 
 "plugins": [ 
  "styled-components", 
  [ 
   "@babel/plugin-proposal-pipeline-operator", 
   { "proposal": "minimal" } 
  ], 
  [ 
   "@babel/plugin-proposal-optional-chaining", 
   { "loose": false } 
  ], 
  [ 
   "@babel/plugin-proposal-class-properties", 
   { "loose": true } 
  ], 
  "@loadable/babel-plugin" 
 ] 
}

项目B仅引入了该组件库及生成的代码分割块,但无论是否按需加载组件,都会出现上述错误。请问我哪里操作有误?能否给出相关建议?


问题分析与解决方案

这个问题核心是组件库的代码分割策略和Next.js的SSR模块解析逻辑不兼容,具体原因和修复步骤如下:

1. 核心问题拆解

  • 你用libraryTarget: 'commonjs2'输出CommonJS模块,但Loadable-Components生成的异步chunk是浏览器端的异步加载逻辑,Next.js在SSR阶段会同步解析CommonJS模块,此时异步chunk还未加载,导致导出的组件为undefined。
  • splitChunks: { chunks: 'all' }会把组件库内的组件拆分为独立chunk,主入口文件里并没有实际导出这些组件,而是通过异步引用的方式引入,Next.js无法识别这种异步导出结构。

2. 具体修复步骤

(1)调整Webpack代码分割策略,把控制权交给Next.js

组件库作为依赖包,内部尽量不要做代码分割,而是让引用它的项目(比如Next.js)来处理按需加载。修改webpack.build.config.js的optimization配置:

optimization: {
  minimize: true,
  usedExports: true,
  // 禁用组件库内部的代码分割,让Next.js通过Tree Shaking实现按需加载
  splitChunks: false,
},

如果确实需要保留组件库内部的代码分割,那必须确保导出的组件是SSR兼容的同步包装器。

(2)为Loadable-Components配置SSR支持

在组件库中使用Loadable时,显式开启SSR模式,确保服务器端能同步加载组件:

// @components/SomeComponent/index.js
import loadable from '@loadable/component'

// 开启ssr选项,让服务器端同步加载组件代码
const LoadableSomeComponent = loadable(() => import('./SomeComponent'), {
  ssr: true,
})

export default LoadableSomeComponent

同时可以把Webpack的target改为'node',或者为SSR单独构建一个版本,避免环境不兼容。

(3)修改组件导出方式,避免批量导出

export * from '@components'这种批量导出在代码分割后,导出的可能是Loadable包装器而非实际组件,Next.js SSR无法正确解析。改为显式导出每个组件:

// src/index.js
import styled, { ServerStyleSheet, createGlobalStyle } from 'styled-components';
import SomeComponent from '@components/SomeComponent';
import AnotherComponent from '@components/AnotherComponent';
export * from '@style';
export { default as Icons } from '@assets/icons';
export { styled, ServerStyleSheet, createGlobalStyle, SomeComponent, AnotherComponent };

(4)Next.js项目中配置Loadable-Components的SSR支持

在项目B中安装@loadable/server,并自定义_document.js来预加载组件库的chunk:

  • 安装依赖:npm install @loadable/server
  • 创建pages/_document.js:
import Document, { Html, Head, Main, NextScript } from 'next/document'
import { ChunkExtractor } from '@loadable/server'
import path from 'path'

class MyDocument extends Document {
  static async getInitialProps(ctx) {
    // 指向组件库构建时生成的.loadable-stats.json文件
    const extractor = new ChunkExtractor({
      statsFile: path.resolve('../your-component-library/.loadable-stats.json'),
    })

    const originalRenderPage = ctx.renderPage

    ctx.renderPage = () =>
      originalRenderPage({
        enhanceApp: (App) => (props) => extractor.collectChunks(<App {...props} />),
      })

    const initialProps = await Document.getInitialProps(ctx)

    return {
      ...initialProps,
      scripts: [...initialProps.scripts, ...extractor.getScriptElements()],
    }
  }

  render() {
    return (
      <Html>
        <Head />
        <body>
          <Main />
          <NextScript />
        </body>
      </Html>
    )
  }
}

export default MyDocument

3. 额外建议

  • 组件库优先提供ES模块版本(在package.json中用module字段指向ES模块入口),让Next.js通过Tree Shaking实现按需加载,这比组件库内部做代码分割更符合依赖包的设计逻辑。
  • 测试时先在纯React项目中验证组件库的代码分割是否正常,再引入到Next.js项目中,排除SSR环境的干扰。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 11:37:29