如何使用 TypeScript 正确导出 React 组件库中的 SVG

The*_*ist 5 typescript reactjs webpack babel-loader

我正在将 React 应用程序的核心组件提取到一个单独的组件库中,以供其他客户端应用程序使用。这些组件使用 SVG 图标,该图标已经通过 babel-loader 在原始应用程序中运行。

\n\n

然而,由于组件是用 Typescript 编写的,据我所知,我需要ts-loader该库正常工作,即使我无法测试它,因为我什至无法编译该库。为了能够导出 SVG,我另外babel-loaderbabel-plugin-named-asset-import.

\n\n

这是我的项目结构:

\n\n
.\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 config\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 webpack.config.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 global.d.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 package-lock.json\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 assets\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 icons\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.d.ts\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 upload.svg\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 bar\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 foo\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 index.ts\n\xe2\x94\x82\xc2\xa0\xc2\xa0     \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 Stuff.ts\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 tsconfig.json\n
Run Code Online (Sandbox Code Playgroud)\n\n

webpack.config.json:

\n\n
const path = require("path");\n\nmodule.exports = {\n  entry: "./index.ts",\n  module: {\n    rules: [\n      {\n        test: /\\.tsx?$/,\n        use: {\n          loader: "ts-loader",\n        },\n        exclude: /node_modules/,\n      },\n      {\n        test: /\\.(js|mjs|jsx|ts|tsx)$/,\n        loader: require.resolve("babel-loader"),\n        options: {\n          customize: require.resolve(\n            "babel-preset-react-app/webpack-overrides"\n          ),\n\n          plugins: [\n            [\n              require.resolve("babel-plugin-named-asset-import"),\n              {\n                loaderMap: {\n                  svg: {\n                    ReactComponent: "@svgr/webpack?-svgo,+ref![path]",\n                  },\n                },\n              },\n            ],\n          ],\n        },\n      },\n    ],\n  },\n  resolve: {\n    extensions: [".tsx", ".ts", ".js"],\n  },\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

tsconfig.json:

\n\n
{\n  "include": ["./global.d.ts", "./index.ts", "src/**/*"],\n\n  "compilerOptions": {\n    "allowJs": false,\n    "jsx": "react",\n    "esModuleInterop": true,\n    "downlevelIteration": true,\n    "declaration": true,\n    "noEmit": false,\n    "outDir": "dist",\n    "moduleResolution": "node",\n    "module": "commonjs",\n    "target": "es5",\n    "lib": ["es5", "es6", "es7", "es2017", "dom"],\n    "sourceMap": true,\n    "typeRoots": ["./node_modules/@types", "./@types"],\n    "resolveJsonModule": true\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

global.d.ts - 我发现declare module这里的部分完全没有效果,无论有没有它我都会得到相同的结果。

\n\n
import \'jest-dom/extend-expect\';\n\ndeclare module \'*.svg\' {\n  const content: string;\n  export default content;\n}\n\n
Run Code Online (Sandbox Code Playgroud)\n\n

./src/assets/icons/index.ts 应该负责导出所有可用的图标,以便其他文件导入它们。

\n\n
export { ReactComponent as Add } from "./add.svg";\n
Run Code Online (Sandbox Code Playgroud)\n\n

包含./src/demo/index.ts“使用”此 SVG 的代码:

\n\n
import { Add } from "../assets/icons";\n\nexport default {\n  Add,\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

根目录index.ts中包含相同的内容来证明我的问题的不一致:

\n\n
import { Add } from "../assets/icons";\n\nexport default {\n  Add,\n};\n
Run Code Online (Sandbox Code Playgroud)\n\n

到目前为止一切顺利,我想,所以我用 运行了 webpack npx webpack --config ./config/webpack.config.js,结果整个事情失败并出现以下错误消息:

\n\n
ERROR in C:\\work\\tstest\\src\\demo\\index.ts\n[tsl] ERROR in C:\\work\\tstest\\src\\demo\\index.ts(1,21)\n      TS2306: File \'C:/work/tstest/src/assets/icons/index.ts\' is not a module.\n
Run Code Online (Sandbox Code Playgroud)\n\n

这让我完全困惑于几个问题:

\n\n
    \n
  1. 为什么有问题的文件不是模块?很高兴知道是什么阻止 ts-loader 正确识别它
  2. \n
  3. ./src/demo/index.ts并且./index.ts内容100%相同。尽管如此,编译适用于一个文件,但不适用于另一个文件。这是为什么?这种行为记录在哪里?
  4. \n
  5. 我究竟做错了什么?
  6. \n
  7. 实现这一目标的正确方法什么?
  8. \n
\n\n

我认为发生这种情况是因为 babel-loader 完成其工作后,./src/assets/icons/index.ts看起来并不像 ts-loader 想要的那样,但这并不能完全解释具有相同内容的两个文件如何以不同的方式处理。

\n

The*_*ist 1

这是我不了解所使用工具的典型案例。问题出在 tsconfig.json 中:

"include": ["./global.d.ts", "./index.ts", "src/**/*"]
Run Code Online (Sandbox Code Playgroud)

其中包括tsc启动时项目的所有源文件。这导致每当 webpack 调用 ts-loader 时 tsc 都会尝试立即编译所有源文件,包括那些尚未被 处理的文件@svgr/webpack,从而导致错误。

解决办法是只

"include": ["./global.d.ts", "src/**/*.d.ts"]
Run Code Online (Sandbox Code Playgroud)

这样,webpack 负责根据其配置的逻辑编译入口点包含的各个源文件,而 tsc 只能看到 webpack 处理过的那些定义文件。