解决 Next.js 中 “react/no-unescaped-entities” ESLint 错误的指南

LightNode
作者 LightNode ·

引言

在 Next.js 中构建复杂界面时,经常会在 JSX 文本里包含特殊字符——例如引号(")、撇号(')以及尖括号(</>)。ESLint 规则 react/no-unescaped-entities 会禁止这些字符以“未转义”的形式出现在 JSX 中,否则可能导致解析或渲染异常。一旦触发,你会看到类似下面的错误:

no unescaped entities

./src/components/home/testimonials-section.tsx
74:17  Error: `"` can be escaped with `&quot;`, `&ldquo;`, `&#34;`, `&rdquo;`.  react/no-unescaped-entities
74:53  Error: `"` can be escaped with `&quot;`, `&ldquo;`, `&#34;`, `&rdquo;`.  react/no-unescaped-entities
info  - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/app/api-reference/config/eslint#disabling-rules
// Next.js 组件中的示例错误:
export default function Welcome() {
  return <h1>Welcome to John's Blog</h1>;
  //              ^ 这里有未转义的撇号(apostrophe)
}

如果不处理,CI/CD 检查可能会失败,团队成员也会被相关的 Lint 警告干扰。本文将解释错误出现的原因,并给出多种可靠的修复方式。

根因解析

JSX 中的特殊字符规则

JSX 与 HTML 类似,但在文本解析上更严格。<> 会被解析为元素分隔符,引号与撇号常用于界定属性值边界。如果在文本里直接使用这些未转义字符,可能会破坏抽象语法树(AST):

<p>Use <strong>bold</strong> for emphasis.</p>
// 这里的 "<strong>" 会被解析为嵌套元素,而不是字面文本。

Next.js 的默认 ESLint 配置

Next.js 内置了 ESLint 设置(@next/eslint-plugin-next),并扩展了 reactjsx-a11y 相关预设。默认情况下,React 预设包含:

"rules": {
  "react/no-unescaped-entities": "error"
}

因此,只要 JSX 中出现未转义的实体,在开发或构建阶段都会报错。

为何富文本项目更易受影响

涉及多语言、Markdown 渲染或大量用户生成内容(UGC)的项目常常包含很多特殊字符,更容易触发未转义实体的告警,造成持续的 Lint 噪音,从而影响开发效率。

推荐方案:在 Next.js 中配置 ESLint

全局关闭该规则

若希望在整个项目范围内关闭 no-unescaped-entities,修改 .eslintrc.js

module.exports = {
  extends: ["next", "next/core-web-vitals"],
  rules: {
    "react/no-unescaped-entities": "off",
  },
};

通过 next.config.js 调整构建期行为

如果你只想在构建阶段跳过 ESLint 校验,可修改 next.config.js

module.exports = {
  eslint: {
    ignoreDuringBuilds: true, // skips all lint checks in production builds
  },
};

⚠️ 该设置会在构建时静默所有 ESLint 错误,请谨慎使用。

在特定文件或组件中禁用

若只想在某个文件或组件内临时禁用,可添加内联 ESLint 指令:

/* eslint-disable react/no-unescaped-entities */
export default function Caution() {
  return <p>Warning: Don't click that button!</p>;
}
/* eslint-enable react/no-unescaped-entities */

TypeScript 项目的注意事项

如果你在 tsconfig.json 中配置了路径别名,并配合 ESLint overrides 使用,请确认引用的 ESLint 配置无误:

// .eslintrc.js
module.exports = {
  parser: "@typescript-eslint/parser",
  extends: [
    "next",
    "next/core-web-vitals",
    "plugin:@typescript-eslint/recommended",
  ],
  rules: {
    "react/no-unescaped-entities": "off",
  },
};

替代方案:使用 HTML 实体

如果你希望遵循该规则,同时又正确显示特殊字符,可以改用 HTML 实体。

常用 HTML 实体

Character Entity
Apostrophe (') &apos; or &#39;
Double quote (") &quot;
Less-than (<) &lt;
Greater-than (>) &gt;
Ampersand (&) &amp;

在 JSX 中的正确用法

export default function Quote() {
  return (
    <p>
      She said, &quot;Next.js is awesome!&quot; &amp; I couldn&apos;t agree more.
    </p>
  );
}

适用场景

  • 静态文案:页头、页脚或营销页中的硬编码文本。
  • 本地化文件:在 JSON/YAML 中保存实体,便于跨语言传递。
  • 对 SEO 关键的内容:确保源码中始终对特殊符号进行编码。

其他实用做法

使用花括号的 JavaScript 表达式

将文本包裹在花括号内,作为字符串字面量传入,避免 JSX 解析歧义:

export default function Ellipsis() {
  return <p>{'Wait for it...'}</p>;
}

用单引号包裹含双引号的字符串(或反之)

如果字符串内部含有双引号,可用单引号包裹(或反过来):

// 避免字符串内未转义的双引号
<p title='She said "Hello" to me'>Greeting</p>

利用 React Fragment

当一段内容包含较复杂的标签与文本时,可用 Fragment 将文本与标签隔离:

export default function Mixed() {
  return (
    <>
      <p>Use <strong>React</strong> &amp; Next.js together!</p>
      <p>{'Enjoy your coding journey.'}</p>
    </>
  );
}

总结与参考

方案 优点 缺点
全局关闭 ESLint 规则 见效快、无 Lint 噪音 可能掩盖真实的未转义问题
使用 HTML 实体 符合规范、明确编码 冗长、可读性下降
内联禁用 ESLint 注释 颗粒度细、可定点控制 指令分散在代码中,重复度高
使用花括号/替换引号的写法 不关闭 Lint 也能灵活处理 与惯用 JSX 写法略有差异