无法使用React.lazy与Material UI图标

4

由于我的应用程序中有很多地方需要使用很多图标,我想使用代码分割进行管理。

我创建了一个帮助组件来实现这个目的:

import React, { FunctionComponent, Suspense } from 'react';

interface LazyMuiIconProps {
  name: string;
}

export const LazyMuiIcon: FunctionComponent<LazyMuiIconProps> = ({ name }) => {
  console.log(name);
  const IconElement = React.lazy(() => import(`@material-ui/icons/${name}`));
  // const IconElement = React.lazy(() => import(`@material-ui/icons/Home`));
  return (
    <Suspense fallback={null}>
      <IconElement />
    </Suspense>
  );
};

当我使用固定名称的注释行到Home时,它可以正常工作并懒加载Home图标。然而,一旦我将其更改为上面的行,在编译过程中webpack会崩溃并显示低级错误:

69% building 15623/15657 modules 34 active ...terial-ui/icons/AccessibleOutlined.d.ts <--- Last few GCs --->

[30:0x33f4320] 77272 ms: Scavenge 1142.0 (1422.8) -> 1141.5 (1423.3) MB, 1.4 / 0.0 ms (average mu = 0.300, current mu = 0.342) allocation failure [30:0x33f4320] 77275 ms: Scavenge 1142.3 (1423.3) -> 1141.7 (1423.8) MB, 1.5 / 0.0 ms (average mu = 0.300, current mu = 0.342) allocation failure [30:0x33f4320] 77278 ms: Scavenge 1142.4 (1423.8) -> 1141.9 (1424.3) MB, 1.5 / 0.0 ms (average mu = 0.300, current mu = 0.342) allocation failure

<--- JS stacktrace --->

==== JS stack trace =========================================

0: ExitFrame [pc: 0x16ac4804fb5d] Security context: 0x02fea7a9d921 <JSObject>
1: add [0x2fea7a906c9](this=0x10fbf120c2e1 <Set map = 0x3f914303d81>,0x1a97709947e1 <DependenciesBlock map =

0x1c000787d3b1>) 2: new Set(aka Set) [0x2fea7a90391](this=0x056d59402691 ,0x33036eb7fbe1 ) 3: ConstructFrame [pc: 0x16ac48009e66] 4: StubFrame [pc: 0x16ac480f932c] 5: processDependenciesBlocksForC... 70% chunk graphFATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory 1: 0x948d20 node::Abort() [/usr/local/bin/node] 2: 0x9499bc node::OnFatalError(char const*, char const*) [/usr/local/bin/node] 3: 0xb1160e v8::Utils::ReportOOMFailure(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node] 4: 0xb11844 v8::internal::V8::FatalProcessOutOfMemory(v8::internal::Isolate*, char const*, bool) [/usr/local/bin/node] 5: 0xf0def2 [/usr/local/bin/node] 6: 0xf0dff8 v8::internal::Heap::CheckIneffectiveMarkCompact(unsigned long, double) [/usr/local/bin/node] 7: 0xf1a718 v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::GCCallbackFlags) [/usr/local/bin/node] 8: 0xf1b22b v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags) [/usr/local/bin/node] 9: 0xf1df61 v8::internal::Heap::AllocateRawWithRetryOrFail(int, v8::internal::AllocationSpace, v8::internal::AllocationAlignment) [/usr/local/bin/node] 10: 0xee7e96 v8::internal::Factory::AllocateRawArray(int, v8::internal::PretenureFlag) [/usr/local/bin/node] 11: 0xee885a v8::internal::Factory::NewFixedArrayWithFiller(v8::internal::Heap::RootListIndex, int, v8::internal::Object*, v8::internal::PretenureFlag) [/usr/local/bin/node] 12: 0xee8900 v8::internal::Handle v8::internal::Factory::NewFixedArrayWithMap(v8::internal::Heap::RootListIndex, int, v8::internal::PretenureFlag) [/usr/local/bin/node] 13: 0x108e547 v8::internal::OrderedHashTable::Allocate(v8::internal::Isolate*, int, v8::internal::PretenureFlag) [/usr/local/bin/node] 14: 0x1091ab1 v8::internal::OrderedHashTable::Rehash(v8::internal::Isolate*, v8::internal::Handle, int) [/usr/local/bin/node] 15: 0x109202b v8::internal::OrderedHashTable::EnsureGrowable(v8::internal::Isolate*, v8::internal::Handle) [/usr/local/bin/node] 16: 0x117ee2c v8::internal::Runtime_SetGrow(int, v8::internal::Object**, v8::internal::Isolate*) [/usr/local/bin/node] 17: 0x16ac4804fb5d Aborted (core dumped)

我的 Babel 插件:

plugins: [
                // plugin-proposal-decorators is only needed if you're using experimental decorators in TypeScript
                "@babel/plugin-syntax-dynamic-import",
                ['@babel/plugin-proposal-decorators', { legacy: true }],
                ['@babel/plugin-proposal-class-properties', { loose: true }],
                'react-hot-loader/babel',

据我所见,它正在尝试加载所有图标,而不仅仅是一个(我将使用实例减少到一个以便更容易找到解决方案),然后它会崩溃。

更新

如果我尝试使用react-loadable,情况完全相同...


return Loadable({
    loader: () => import(`@material-ui/icons/${props.name}`),
    loading: () => <span>icon</span>
  });
1个回答

4
如果你正在使用Webpack,那么要动态加载一个文件(例如图标)时,问题在于Webpack默认会为该模块生成一个代码块。如果你正在使用Webpack 4或更高版本,则可以使用webpackMode: eager来解决这个问题。因此,在这种情况下,导入的代码将类似于以下内容:
const IconElement = React.lazy(() => import(/* webpackMode: "eager" */`@material-ui/icons/${name}`));

这基本上会强制Webpack将模块包含在您的捆绑文件中(从而减少http请求的数量),这使得使用Lazy / Suspense的整个意义变得无用,但是这个Github问题与您的问题类似,并表明单独加载每个图标不值得,即您应该导入@material-ui/icons包并使用您需要的图标。

更新(因为引用了react-loader):

在使用react-loader时,您还需要指定文件的扩展名。我曾经遇到过这个问题,并通过导入指定文件的扩展名来解决它。在本例中:

return Loadable({
    loader: () => import(`@material-ui/icons/${props.name}.ts`),
    loading: () => <span>icon</span>
  });

但我不确定它是否仍然有效,因为我在有关Webpack和延迟加载的回答中已经提到过。


1
谢谢!我使用静态导入完成了它,因为这样做对于太多请求来说是有意义的,但我会记住它以备将来重用。 - Marek Urbanowicz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接