动态加载时出现 TypeScript 问题

问题描述 投票:0回答:1

我有一个包含从

images.ts
导出的图标的文件:

export { default as SvgArrow } from './arrow.svg';
export { default as SvgCopy } from './copy.svg';
...
export { default as SvgWater } from './water.svg';

这是设计系统库的一部分。

然后我的图标组件导入图标:

import * as Icons from '../images';

并在渲染中设置当前图标:

const CurrentIcon = Icons[iconName];

通过这样的实现,使用

<Icon />
的库中的所有组件都拥有构建包中的所有 svgs。因此,捆绑包非常大 - 大约 6mb。

我实现了当前图标的动态加载。

const [IconComponent, setIconComponent] = useState<React.ElementType | null>(
  null,
);

useEffect(() => {
  const loadIcon = async () => {
    const icons = await import('../icons');

    setIconComponent(() => icons[iconName]);
  };
  loadIcon();
}, [iconName]);

问题是,虽然它的工作方式是捆绑包不是 700kB 而不是 6mb,但 TypeScript 失败了 - 这意味着在我的消费应用程序中我遇到了大量错误,大多是这样的:

您可能需要合适的加载器来处理此文件类型,目前 没有配置加载程序来处理此文件。看 https://webpack.js.org/concepts#loaders

动态加载是唯一的变化。当我恢复它时,它在我的应用程序中一切正常,但同时构建再次达到 6mb。

我尝试了很多解决方案 - webpack 魔法注释、JS eval(有效,但这不是正确的方法)、更好的 useEffect 类型。它们都不起作用,仍然是相同的 TS 错误。

我的 tsconfig 是:

{
  "compilerOptions": {
    "baseUrl": ".",
    "target": "es2020",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "declaration": true,
    "outDir": "./dist",
    "rootDir": ".",
    "declarationDir": "./dist",
    "forceConsistentCasingInFileNames": true,
    "esModuleInterop": true,
    "module": "es2020",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "sourceMap": true,
    "declarationMap": true,
    "jsx": "react-jsx",
    "incremental": false,
    "paths": {
      "components/*": ["components/*"],
      "utils/*": ["utils/*"],
    },
    "plugins": []
  },
  "include": ["**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules", "dist"]
}

我的 webpack 配置是:

const path = require('path');
const TerserPlugin = require('terser-webpack-plugin');
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const { getEntries } = require('./getEntries');

module.exports = {
  entry: getEntries(),
  mode: 'production',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: (pathData) => {
      const name = pathData.chunk.name;

      return `${name}.js`;
    },
    libraryTarget: 'module',
    globalObject: 'this',
  },
  module: {
    rules: [
      {
        test: /\.(ts|tsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.svg$/,
        use: [
          {
            loader: '@svgr/webpack',
            options: {
              typescript: true,
              ref: true,
            },
          },
        ],
      },
    ],
  },
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
    alias: {
      components: path.resolve(__dirname, 'components'),
      theme: path.resolve(__dirname, 'theme'),
      images: path.resolve(__dirname, 'images'),
      types: path.resolve(__dirname, 'types'),
    },
  },
  externals: {
    '@mui/material': '@mui/material',
    next: 'next',
    react: 'react',
    'react-dom': 'react-dom',
  },
  optimization: {
    minimizer: [new TerserPlugin()],
  },
  plugins: [new CleanWebpackPlugin()],
  experiments: {
    outputModule: true,
  },
};

我尝试了这个问题的所有解决方案,但没有一个有效 - 除了 JS eval 之外,出于安全和性能原因,这通常被认为是不好的做法。

我也在 webpack 中使用

@svgr/webpack
,无需动态加载我的包,设计系统就可以正常工作,图标也是如此。

即使是现在,尽管存在所有 TS 错误,如果我这样做,消费应用程序仍然可以工作:

{
  test: /\.d\.ts$/,
  loader: 'ignore-loader',
},

但这不是我想要的。我希望我的设计系统包开箱即用,没有任何 TS 问题,并且可以动态导入图标。

javascript reactjs typescript webpack
1个回答
0
投票
1. Export SVG Icons as React Components
You can use the following pattern to export SVG icons:

export { default as SvgArrow } from "./arrow.svg";
2. Import Icons in Your Components
Now, you can import the icons directly into your components from the centralized file:

import { SvgArrow } from "@/assets";

function Example() {
  return (
    <div>
      <SvgArrow />
    </div>
  );
}
3. Update next.config.ts
Add the following configuration to next.config.ts for performance optimization, custom console handling, and handling remote images:

/** @type {import('next').NextConfig} */
const nextConfig = {
  reactStrictMode: false, // Enable React strict mode for improved error handling
  swcMinify: true, // Enable SWC minification for improved performance
  productionBrowserSourceMaps: false, // Disable source maps in development
  optimizeFonts: false, // Disable font optimization
  compiler: {
    // Remove console logs in production builds
    removeConsole: process.env.NODE_ENV !== "development",
  },
  images: {
    remotePatterns: [
      {
        protocol: "https",
        hostname: "storage.googleapis.com",
      },
      {
        protocol: "https",
        hostname: "placehold.co",
      },
      {
        protocol: "http",
        hostname: "localhost",
        port: "3000", // Specify the port for localhost
      },
    ],
  },
};

export default nextConfig;
最新问题
© www.soinside.com 2019 - 2025. All rights reserved.