Next.js 与 Vite 混合项目中正确配置 JSX 编译模式的实践指南

1次阅读

Next.js 与 Vite 混合项目中正确配置 JSX 编译模式的实践指南

在 Next.jsvite 共存的混合项目中,jsx: “react-jsx” 配置易被覆盖导致 React is not defined 错误;根本解法是显式导入 React,并配合合理的 typescript 和构建工具配置。

在 next.jsvite 共存的混合项目中,`jsx: “react-jsx”` 配置易被覆盖导致 `react is not defined` 错误;根本解法是显式导入 react,并配合合理的 typescript 和构建工具配置。

当在同一个项目中同时使用 Next.js(服务端渲染/SSR)和 Vite(客户端开发/测试)时,TypeScript 的 JSX 编译行为容易产生冲突。典型表现为:手动将 tsconfig.json 中的 “jsx” 设为 “react-jsx” 后,运行 npm run dev(触发 Next.js 的类型检查或配置生成)时,该值又被自动重写为 “preserve”——这会导致 Babel 或 Vite 在编译 .tsx 文件时跳过自动注入 React.createElement 调用,进而引发运行时错误:

ReferenceError: React is not defined  ❯ Module.Home [as default] src/app/page.tsx:2:3

✅ 正确解决方案:显式导入 + 配置隔离

核心原则:不要依赖自动 React 注入,而应显式声明依赖。
从 React 17+ 开始,”react-jsx” 模式虽支持无须手动 import React from ‘react’,但该特性高度依赖构建工具对 @jsxImportSource 的完整支持及一致配置。而在 Next.js + Vite 混合环境中,二者对 tsconfig.json 的读取优先级、插件链和 JSX 处理阶段并不统一,极易出现“一处配置、两处解释”的问题。

因此,最稳定、跨工具兼容的写法是:在每个使用 JSX 的文件顶部显式导入 React

// src/app/page.tsx import * as React from 'react'; // ✅ 关键:确保 React 命名空间可用  export default function Home() {   return <div>Home of Coach-Next</div>; }

? 注意:使用 import * as React 而非 import React from ‘react’,可避免默认导出与命名导出混淆(尤其在严格 ESM 环境下),也兼容 react-jsx 和 preserve 两种 JSX 模式。

? 补充配置建议(提升健壮性)

  1. 分离 tsconfig.json 配置作用域
    Next.js 推荐使用 tsconfig.json 作为主配置,而 Vite 测试环境(如 Vitest)应通过 vite.config.ts 显式指定 tsconfig 路径,避免被 Next.js CLI 覆盖:

    // vite.config.ts import { defineConfig } from 'vitest/config';  export default defineConfig({   test: {     include: ['**/*.test.{ts,tsx}'],     environment: 'jsdom',     globals: true,     // 显式指定测试专用 tsconfig,避免受 next dev 影响     alias: { react: 'react' },     setupFiles: ['./src/test/setup.ts'],   }, });
  2. 为 Vitest 单独维护 tsconfig.test.json(可选但推荐)
    创建独立配置,锁定测试环境的 JSX 行为:

    // tsconfig.test.json {   "extends": "./tsconfig.json",   "compilerOptions": {     "jsx": "react-jsx"   } }

    并在 vitest.config.ts 中引用:

    test: {   // ...   typecheck: {     enabled: true,     tsconfig: './tsconfig.test.json'   } }
  3. 禁用 Next.js 的自动 tsconfig 覆盖(Next.js ≥14.2)
    若使用较新版本 Next.js,可在 next.config.js 中添加:

    module.exports = {   typescript: {     ignoreBuildErrors: true, // 避免构建时强制修正 tsconfig   }, };

? 验证你的测试是否真正可靠

修改后的 home.test.ts 应保持简洁且无需 React 导入(因组件内部已处理):

/**  * @jest-environment jsdom  */  import { render, screen } from '@testing-library/react'; import { test, expect } from 'vitest'; import Home from '../app/page';  test('Home page', () => {   render(<Home />); // ✅ 推荐:JSX 元素调用,而非 Home()   expect(screen.getByText('Home of Coach-Next')).toBeInTheDocument(); });

⚠️ 注意:原测试中 render(Home()) 是函数调用,绕过了 React 渲染器生命周期,无法触发 hooks、上下文等,属于反模式。应始终使用 形式。

✅ 总结

场景 推荐做法
JSX 编译稳定性 所有 .tsx 文件顶部添加 import * as React from ‘react’
tsconfig 冲突 不强依赖 “jsx”: “react-jsx” 全局配置;改用显式导入 + 工具链隔离
Vitest 运行 使用独立 tsconfig.test.json,并通过 vitest.config.ts 显式指定
Next.js 开发 保持 tsconfig.json 中 “jsx”: “preserve”(Next.js 默认兼容),无需强行修改

这一方案不依赖构建工具“魔法”,符合 TypeScript 类型系统设计本意,也完全适配 ESM、SWC、Babel 及未来可能引入的新编译器,是混合技术下的长期可维护实践。

text=ZqhQzanResources