如何使用 create-react-app(无弹出)导入共享的typescript代码?

IT技术 javascript reactjs typescript webpack create-react-app
2021-04-30 12:02:01

我试图在非弹出模式下的 create-react-app 中实现 TypeScript 代码共享,但我遇到了臭名昭著的限制,即src不允许在外部导入

You attempted to import ../../common/src/lib.ts which falls outside of the project src/ directory. [...]

对于非 TypeScript 的情况,已在此处询问并回答,但我无法获得任何使用 TypeScript 的解决方案。具体而言,所提出的解决方案存在的问题是:

  • 设置baseDirts-config.json:在这里创建react的应用程序内抱怨:你的项目baseUrl只能被设置为srcnode_modulesCreate React App 目前不支持其他值。

  • 基于 react-app-rewired 的方法:更有前景。禁用ModuleScopePlugin让我过去了“尝试在 src 外导入”错误,但现在的问题是typescript文件的加载器无法正常运行:

    在此处输入图片说明

    我已经验证它.ts本身没问题:./src从那里复制和导入它工作正常。

    我还增加了../../common/src文件夹添加到includes列表中ts-config.json

    我的猜测是 webpack 加载器配置以某种方式有一个规则,可以防止 TypeScript 加载器转换与其预期路径模式不匹配的文件。如何使用 react-app-rewired 解决这个问题?

  • 符号链接源也不起作用——同样的问题。可能是因为 webpack 在内部解析符号链接并在正常加载器规则不适用的路径中看到文件。

  • 基于弹出的解决方案:我现在不想弹出,但我尝试过,基本上又遇到了同样的问题。

我还发现了其他听起来相关但没有回答问题的问题:


在单存储库中重新使用 TypeScript 类型似乎是一个相当合理的模式。我是否遗漏了什么,或者为什么 create-react-app 使这变得如此困难?


重现:我的代码基本上是你所得到的 100%

npx create-react-app my-app --template typescript

添加了一个对外部的导入。

4个回答

您可以使用craco(create-react-app config override)来覆盖 webpack 配置(抽象为 CRA 的一部分)而不弹出。
此外,您可以使用ts-loader直接在外部项目中引用非转译的 ts 代码(例如,如果您想引用/使用共享代码/lib 作为单一存储库的一部分)。

假设您的 CRA 应用程序位于client您的项目结构如下所示目录中:

client/
|--src/
|--package.json
shared/
|--package.json
|--(ts files)
package.json

cd client
yarn add -D @craco/craco ts-loader
  1. craco.config.jsclient/(CRA) 目录中创建一个文件
  2. 确保craco.config.js有以下内容
const path = require("path");

module.exports = {
  webpack: {
    configure: webpackConfig => {

      // ts-loader is required to reference external typescript projects/files (non-transpiled)
      webpackConfig.module.rules.push({
        test: /\.tsx?$/,
        loader: 'ts-loader',
        exclude: /node_modules/,
        options: {
          transpileOnly: true,
          configFile: 'tsconfig.json',
        },
      })
      
      return webpackConfig;
    }
  }
};
  1. react-scripts命令替换client/package.jsoncraco
/* client/package.json */

"scripts": {
-   "start": "react-scripts start",
+   "start": "craco start",
-   "build": "react-scripts build",
+   "build": "craco build"
-   "test": "react-scripts test",
+   "test": "craco test"
}

更新:由于 react-app-rewired 只是被动维护并且不支持 CRA 版本 2+(在撰写本文时我们是三个主要版本),我不再推荐这种方法。


经过更多小时的试验和阅读 GitHub 问题,我终于有了一个可行的解决方案。非常感谢 BirukDmitry,他在 GitHub 上发布这篇非常有用的帖子分步指南:

  1. 安装 react-app-rewired 和 custom-cra

     npm i react-app-rewired customize-cra --save-dev
    
  2. 用这样的最小值配置 react-app-rewird config-overrides.js

    const { removeModuleScopePlugin, override, babelInclude } = require("customize-cra");
    const path = require("path");
    
    module.exports = override(
      removeModuleScopePlugin(),        // (1)
      babelInclude([
        path.resolve("src"),
        path.resolve("../common/src"),  // (2)
      ])
    );
    

    设置 (1) 类似于一般绕过 import-outside-src 限制所需的设置(请参阅链接问题)。

    设置(2)对于其他路径的启用 babel-transpilation(因此,包括我假设的 TS 类型剥离)至关重要。这是您必须添加要从中导入的路径的地方。

  3. 不需要调整tsconfig.json

  4. 使用相对路径导入,例如import * as mymodule from '../../common/src/mymodule'.

该解决方案是react-APP-再布线别名(对于react-APP-重新布线定制-CRAcraco):

根据所提到的文档替换react-scriptspackage.json和配置下:

  • 重新接线的 cra 版本:
// config-overrides.js
const {alias, configPaths} = require('react-app-rewire-alias')
const aliasMap = configPaths('./tsconfig.paths.json') // or jsconfig.paths.json

module.exports = alias(aliasMap)
module.exports.jest = aliasJest(aliasMap)
  • 克拉科版本:
// craco.config.js
const {CracoAliasPlugin, configPaths} = require('react-app-rewire-alias')

module.exports = {plugins: [{
  plugin: CracoAliasPlugin,
  options: {alias: configPaths('./tsconfig.paths.json')}
}]}

像这样在 json 中配置别名:

// tsconfig.paths.json
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "common/*": ["../../common/src/*"],
      "@library/*": ["../../library/src/*"]
    }
  }
}

并在extends主typescript配置文件的部分添加此文件:

// tsconfig.json
{
  "extends": "./tsconfig.paths.json",
  // ...
}

问题住在哪../../common/src/mymodule儿?这是另一个项目吗?如果这是另一个项目,为什么不链接它们

  • 在公共代码项目运行中 npm link

  • 在将使用公共代码的项目中: npm link common-project

如果没有两个不同的项目。为什么它必须在 src 之外?

您发布的链接src 目录之外的 create-react-app 导入限制 和您的案例/问题是两件完全不同的事情,而且它们非常困难。让我告诉你这个问题和你的想法。

正如我们已经知道的,CRA 创建了一个 SPA 单页应用程序。这意味着只有一个 html 文件。一切都发生在index.html位于 的<project>/public/index.html如果我们编译代码,我们会注意到最终包可能看起来像这样

build
--static
----js
----css
----media
--index.html
--...more hashed crap...
public
src

默认情况下process.env.PUBLIC_URL设置为“/”什么?🤨是的,让我给你看。有句老话说,一张图解释1000多字。 在此处输入图片说明

如果我们查看图像,根据其路径,它位于./src/styles/imageSlack.jpg或某个位置。那么如果我们 console.log 呢。

在此处输入图片说明

哇!他们在哪里这样做?你可以做的一件事来测试我的理论,如果你console.log(process.env.PUBLIC_URL)在代码中的任何地方。现在这是这个和那个之间的巨大差异。

浏览器本身不知道 Typescript。

所以要么你在里面设置 yow 代码src,我们就结束了,要么遵循关注点分离原则,我们用共享代码创建一个 npm 包,然后像任何其他module一样导入。