ReferenceError:React 未定义 - 从 CRA 迁移到 Vite 和 NX

Jos*_*e A 12 javascript typescript reactjs nrwl-nx vite

我目前正在将 create-react-app (CRA - v4) monorepo Webpack 设置迁移到由 Vite 支持的 NX Monorepo。

我目前正试图弄清楚如何解决典型的问题

未捕获的引用错误:React 未定义

每当文件不直接导入 React,而是从中进行命名导入时,就会发生这种情况,例如:

import { memo } from 'react';
Run Code Online (Sandbox Code Playgroud)

我已经运行了删除所有语句的 linter import React,并且通过数百个文件再次添加它会令人畏惧。

以下是更多信息:

  • 我相信我正在使用最新的 JSX 转换和 React 17。
  • nx-插件-vite:^1.1.0
  • 投票:^2.7.1
  • vite-tsconfig-paths: ^3.3.17
  • vite-plugin-eslint: ^1.3.0
  • @vitejs/plugin-react:^1.1.3
  • @nrwl/react": 13.2.4,(更多内容在 package.json 中)

我还反复阅读了 GitHub、SO 和网络上的多个来源,但没有找到任何内容

这是我的vite.config.ts

import path from 'path';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import eslintPlugin from 'vite-plugin-eslint';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [tsconfigPaths(), eslintPlugin(), react()],
  resolve: {
    alias: {
      stream: 'stream-browserify',
      '~': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    open: true,
  },
});
Run Code Online (Sandbox Code Playgroud)

这是我的 tsconfig.json:

import path from 'path';
import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import eslintPlugin from 'vite-plugin-eslint';
import react from '@vitejs/plugin-react';

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [tsconfigPaths(), eslintPlugin(), react()],
  resolve: {
    alias: {
      stream: 'stream-browserify',
      '~': path.resolve(__dirname, 'src'),
    },
  },
  server: {
    open: true,
  },
});
Run Code Online (Sandbox Code Playgroud)

以及基本的 tsconfig.json:

{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "target": "ESNext",
    "useDefineForClassFields": true,
    "lib": ["DOM", "DOM.Iterable", "ESNext"],
    "allowJs": true,
    "skipLibCheck": false,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "ESNext",
    "moduleResolution": "Node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "noFallthroughCasesInSwitch": true
  },
  "include": ["./src"]
}
Run Code Online (Sandbox Code Playgroud)

项目.json:

{
  "compileOnSave": false,
  "compilerOptions": {
    "rootDir": ".",
    "sourceMap": true,
    "isolatedModules": true,
    "declaration": false,
    "moduleResolution": "node",
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "importHelpers": true,
    "target": "es2015",
    "module": "esnext",
    "lib": ["dom", "dom.iterable", "esnext"],
    "skipLibCheck": true,
    "skipDefaultLibCheck": true,
    "baseUrl": ".",
    "paths": {
      "@schon/legacy-components/*": ["apps/app/src/components/*"],
      "@schon/graphql/*": ["apps/app/src/graphql/*"],
      "@schon/hooks/*": ["libs/components/src/hooks/*"],
      "@schon/components/*": ["libs/components/src/ad/*"],
      "@schon/legacy-components2/*": ["libs/components/src/stories/*"],
      "@schon/theme": ["libs/components/src/styles/index.ts"],
      "@schon/typings": ["libs/components/src/typings/index.ts"],
      "@schon/utils": ["libs/components/src/utils/index.ts"],
      "~/*": ["apps/app/src/*"]
    }
  },
  "exclude": ["node_modules", "tmp"]
}

Run Code Online (Sandbox Code Playgroud)

这是给我带来问题的文件之一(让我发现 React 未定义):(这个文件来自故事书正在处理的组件存储库)

import { memo } from 'react';
import { Container as MaterialContainer } from '@material-ui/core';
import { ThemeSpecs } from '../../../styles/theme';

type ContainerProps = {
  children?:
    | JSX.Element
    | JSX.Element[]
    | React.ReactNode
    | React.ReactChildren;
  className?: string;
};

/**
 * Jose decided to wrap this up, in case we needed to apply a general styling to the container
 * itself, and avoid repeating it in every other component.
 */
const Component: React.FC<ContainerProps> = (props) => (
  <MaterialContainer
    className={props.className}
    fixed
    style={{ paddingTop: ThemeSpecs.container.paddingTop }}
  >
    {props.children!}
  </MaterialContainer>
);

type withContainerProps = {};

/**
 * This is a HOC so we can use this to Containerize the imports back
 * at root. This way we can choose which routes use Containers
 * and which don't.
 */

export const withContainer = <P extends object>(
  ComponentToContainer: React.ComponentType<P>
) =>
  class WithContainer extends React.PureComponent<P & withContainerProps> {
    render() {
      return (
        <Container>
          <ComponentToContainer {...this.props} />
        </Container>
      );
    }
  };

export const Container = memo(Component) as typeof Component;
Run Code Online (Sandbox Code Playgroud)

包.json

{
  "root": "apps/app",
  "projectType": "application",
  "sourceRoot": "apps/app/src",
  "tags": [],
  "targets": {
    "serve": {
      "executor": "nx-plugin-vite:serve",
      "options": {
        "configFile": "apps/app/vite.config.ts",
        "port": 3001,
        "host": false,
        "https": false
      }
    },
    "preview": {
      "executor": "nx-plugin-vite:preview",
      "options": {
        "configFile": "apps/app/vite.config.ts"
      }
    },
    "build": {
      "executor": "nx-plugin-vite:build",
      "options": {
        "outDir": "dist",
        "configFile": "apps/app/vite.config.ts",
        "watch": false,
        "write": true,
        "emitAtRootLevel": false,
        "manifest": true
      }
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

nx.json:

import { memo } from 'react';
import { Container as MaterialContainer } from '@material-ui/core';
import { ThemeSpecs } from '../../../styles/theme';

type ContainerProps = {
  children?:
    | JSX.Element
    | JSX.Element[]
    | React.ReactNode
    | React.ReactChildren;
  className?: string;
};

/**
 * Jose decided to wrap this up, in case we needed to apply a general styling to the container
 * itself, and avoid repeating it in every other component.
 */
const Component: React.FC<ContainerProps> = (props) => (
  <MaterialContainer
    className={props.className}
    fixed
    style={{ paddingTop: ThemeSpecs.container.paddingTop }}
  >
    {props.children!}
  </MaterialContainer>
);

type withContainerProps = {};

/**
 * This is a HOC so we can use this to Containerize the imports back
 * at root. This way we can choose which routes use Containers
 * and which don't.
 */

export const withContainer = <P extends object>(
  ComponentToContainer: React.ComponentType<P>
) =>
  class WithContainer extends React.PureComponent<P & withContainerProps> {
    render() {
      return (
        <Container>
          <ComponentToContainer {...this.props} />
        </Container>
      );
    }
  };

export const Container = memo(Component) as typeof Component;
Run Code Online (Sandbox Code Playgroud)

abo*_*ron 19

ReferenceError: React is not defined当我将我的应用程序从 create-react-app 移植到 vite 时,我也收到了。该问题源于 CRA 使用新的 jsx 运行时将 React 自动导入 jsx 文件。

默认情况下,Vite 不使用新的 jsx 运行时,但您可以通过安装以下命令轻松添加它@vitejs/plugin-react

# install the plugin
yarn add @vitejs/plugin-react
Run Code Online (Sandbox Code Playgroud)

...然后更新vite.config.js

// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()]
})
Run Code Online (Sandbox Code Playgroud)

注意:@vitejs/plugin-react还包括,因此如果您正在使用它,react-refresh您应该能够将其删除。@vitejs/plugin-react-refresh


Jos*_*e A 2

我发现了问题。显然有那么一刻我删除了所有的import React陈述。但要注意,如果你不破坏 Children、Fragment 或其他东西,你就不能盲目地删除它。

例如:

如果你有:

import React, {memo} from 'react';

const MyElem = () => {
  const myMemo = useMemo(() => {}, []);
  return (
       <React.Fragment>
       </React.Fragment>
  );
}
Run Code Online (Sandbox Code Playgroud)

当您删除 时React,您的依赖关系将会Fragment挥之不去。

您必须导入它,或者销毁它Fragment

这意味着你必须这样做:

import {Fragment, memo} from 'react';

const MyElem = () => {
  const myMemo = useMemo(() => {}, []);
  return (
       <Fragment>
       </Fragment>
  );
}
Run Code Online (Sandbox Code Playgroud)