返回

搞定 react-codemirror JSON高亮:与官方示例同步

javascript

搞定 react-codemirror 样式:为什么你的 JSON 高亮和官方示例不一样?

react-codemirror 的时候,你可能希望代码编辑器的外观,尤其是语法高亮,能跟官方示例(比如 uiwjs.github.io/react-codemirror/)里展示的一模一样。但有时候,事情并不如预期,特别是在配置特定主题(比如 dark 模式)和语言(比如 JSON)时。

遇到的问题:JSON 颜色不对劲

咱们先看看具体情况。目标是实现官方 Demo 页面上那种 dark 主题下的 JSON 语法高亮效果。

理想效果 (官方 Demo):

官方 Demo 截图

注意看,像 "message" (键) 和 "Network Error" (值) 这两部分,它们的颜色明显不同,说明应用了不同的 CSS 样式。审查元素也能看到,它们各自拥有不同的 CSS 类名,比如键可能是 cm-property,字符串值可能是 cm-string

官方 Demo 键的 CSS 类
官方 Demo 值的 CSS 类

实际效果 (本地环境):

但是,在本地项目中,使用类似下面的代码配置后:

import React, { useCallback } from 'react';
import CodeMirror from '@uiw/react-codemirror';
import { javascript } from '@codemirror/lang-javascript';
import { basicDark } from '@uiw/codemirror-theme-basic'; // 假设从这里或react-codemirror导入

function MyEditor() {
  const [value, setValue] = React.useState('{\n  "message": "Network Error"\n}');
  const onChange = useCallback((val, viewUpdate) => {
    console.log('val:', val);
    setValue(val);
  }, []);

  // 注意:这里的配置是 *不完整* 的,导致了问题
  return (
    <CodeMirror
      value={value}
      minHeight={"120px"}
      // theme={basicDark} // Theme 应用方式可能有多种直接用对象或作为 extension
      extensions={[
          basicDark, // 将主题作为 extension 添加
          javascript({ jsx: true }), // 问题点用了 JS 语言包处理 JSON
          // EditorView.lineWrapping // 如果需要自动换行
      ]}
      onChange={onChange}
    />
  );
}

export default MyEditor;

得到的编辑器看起来却像是这样:

本地实际效果截图

最明显的问题是,JSON 的键(Key)和值(Value)颜色一样了,都是一种单调的颜色。审查元素看看本地生成的 HTML:

本地键的 CSS 类
本地值的 CSS 类

果然,键和值被赋予了相同的、或者说非常通用的 CSS 类名(比如截图里都主要是 cm-line 内部,缺乏更具体的类),没有像官方示例那样细致地区分开。感觉就像编辑器根本没“认出”这是一个 JSON 文件,所以没能应用上针对性的高亮规则。

这到底是怎么回事?为什么本地的效果和官方演示差这么多?

刨根问底:样式差异怎么来的?

CodeMirror 6 ( react-codemirror 底层使用的版本) 和之前的版本有个很大的不同:它极其模块化。几乎所有功能,包括语法高亮、主题、自动补全、行号等等,都是通过 Extensions 来添加的。

问题的根源通常就在于你给 CodeMirror 组件传入的 extensions 数组配置。

回头看上面那段有问题的代码:

extensions={[
    basicDark,
    javascript({ jsx: true }), // <-- 这里是关键!
]}

我们想要高亮的是 JSON,但 extensions 里却传入了 javascript({ jsx: true })。这是用于 JavaScript (甚至支持 JSX) 的语言包,它并不专门为解析和高亮 JSON 设计。

虽然 JSON 语法上是 JavaScript 的一个子集,但 javascript 语言包在解析时,可能不会像专门的 JSON 解析器那样,精确地区分出 JSON 特有的结构,比如严格的键值对(键必须是双引号字符串)。因此,它无法为这些不同的部分(键、字符串值、数字值、布尔值、null)打上精确的、供主题使用的 CSS 标记(类名)。

没有精确的 CSS 类名,即使你应用了 basicDark 主题,主题中的那些针对 JSON 特定元素(如 cm-property 代表键,cm-string 代表字符串值)的颜色规则也无法生效。结果就是,所有东西看起来都是一个颜色,或者只有非常基础的区别。

简单说:你想让编辑器按 JSON 规则高亮,就得给它装上 JSON 的“语言插件”,光用 JavaScript 的不行。

动手解决:让 JSON 高亮起来

知道了原因,解决起来就直接多了。核心是确保为 JSON 内容加载正确的语言支持扩展。

方案一:添加 JSON 语言支持扩展

这是最关键的一步。你需要安装并使用 CodeMirror 官方提供的 JSON 语言包。

  1. 安装依赖:
    打开你的项目终端,运行:

    npm install @codemirror/lang-json
    # 或者 yarn add @codemirror/lang-json
    
  2. 修改 CodeMirror 组件配置:
    在你的 React 组件里,导入 json 函数,并将其添加到 extensions 数组中,替换掉之前的 javascript()

    import React, { useCallback } from 'react';
    import CodeMirror from '@uiw/react-codemirror';
    // 导入正确的 JSON 语言包
    import { json } from '@codemirror/lang-json';
    // 导入你的主题,这里继续用 basicDark 作为例子
    import { basicDark } from '@uiw/codemirror-theme-basic';
    // 如果你希望从 @uiw/react-codemirror 直接导入主题对象也可以,确保其有效
    // import { basicDark } from '@uiw/react-codemirror';
    
    function MyJsonEditor() {
      const [value, setValue] = React.useState('{\n  "message": "Network Error",\n  "status": 404,\n  "success": false,\n  "data": null\n}');
      const onChange = useCallback((val, viewUpdate) => {
        console.log('val:', val);
        setValue(val);
      }, []);
    
      return (
        <CodeMirror
          value={value}
          minHeight={"120px"}
          // theme={basicDark} // 也可以这样应用主题但推荐放入 extensions
          extensions={[
              basicDark,      // 应用主题
              json(),         // 应用 JSON 语言支持!
              // EditorView.lineWrapping // 需要自动换行的话可以取消注释这个
          ]}
          onChange={onChange}
          // 可以考虑添加 basicSetup 来获取一些基础功能行号括号匹配等),但它可能包含默认主题要注意覆盖
          // import { basicSetup } from 'codemirror';
          // extensions={[basicSetup, basicDark, json()]} // 像这样组合但注意 basicSetup 包含的内容
        />
      );
    }
    
    export default MyJsonEditor;
    

    原理和作用:
    @codemirror/lang-json 包提供了专门用于解析 JSON 文本的解析器 (Parser)。当这个扩展被添加到 CodeMirror 实例时:

    • 编辑器会使用这个解析器来分析 JSON 代码结构,理解哪里是键 (property),哪里是字符串 (string),哪里是数字 (number),布尔值 (boolean),等等。
    • 解析器会根据分析结果,在生成的 HTML 结构中为这些不同的语法元素添加特定的 CSS 类名,例如 .cm-property, .cm-string, .cm-number, .cm-boolean, .cm-null
    • 此时,你应用的 basicDark 主题(或其他任何支持 CodeMirror 6 的主题)中定义的针对这些类名的样式规则就能准确匹配并生效了。比如,basicDark 主题里会有类似 .cm-property { color: #9CDCFE; }.cm-string { color: #CE9178; } 这样的规则,使得键和字符串值呈现出不同的颜色。

    结果: 完成这一步后,你的 JSON 代码应该就能获得和官方示例非常接近(甚至完全一样)的语法高亮效果了。键和值会根据 basicDark 主题的定义显示出不同的颜色。

方案二:确认并正确使用主题

虽然主要问题在于缺少 JSON 语言支持,但确保主题本身被正确加载和应用也很重要。

  1. 主题来源和导入:
    react-codemirror ( uiwjs ) 提供了好几种主题包。

    • @uiw/codemirror-theme-basic: 包含 basicLightbasicDark
    • @uiw/codemirror-themes: 一个集合包,包含更多流行主题(如 okaidia, githubLight/Dark, vscodeDark 等)。

    你需要先安装对应的主题包:

    npm install @uiw/codemirror-theme-basic
    # 或者包含更多主题的包
    # npm install @uiw/codemirror-themes
    

    然后像方案一的代码示例那样,从正确的包导入主题,并将其添加到 extensions 数组里。

    import { basicDark } from '@uiw/codemirror-theme-basic';
    // 或者 import { vscodeDark } from '@uiw/codemirror-themes';
    
    // ... 在 extensions 数组中使用 ...
    extensions={[ basicDark, json() ]}
    

    原理和作用:
    主题扩展本质上是提供了一系列 CSS 规则。这些规则定义了编辑器各个部分(背景、文本、选中区域、光标)以及各种语法元素(通过 lang- 包添加的 CSS 类名)的颜色、字体样式等。把主题添加到 extensions 数组,就是告诉 CodeMirror 应用这些 CSS 规则。

    检查点:

    • 确认你安装了包含所需主题的 npm 包。
    • 确认 import 语句指向了正确的包和主题对象/函数。
    • 确认主题实例被加到了 extensions 数组中。

    进阶使用技巧 - 自定义主题或微调:
    如果你觉得某个主题的特定颜色不满意,CodeMirror 6 允许你基于现有主题进行扩展或完全自定义。你可以创建一个新的 Extension 来覆盖特定 highlightStyle 的样式规则。
    例如,你想让 basicDark 主题下的 JSON 属性(键)颜色更绿一点:

    import { EditorView } from '@codemirror/view';
    import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
    import { tags as t } from '@lezer/highlight'; // 需要安装 @lezer/highlight
    
    // 定义自定义高亮规则
    const myCustomHighlight = HighlightStyle.define([
      { tag: t.propertyName, color: '#77CC77' } // 让属性名(JSON key)变绿
      // 你可以在这里添加更多自定义规则
    ]);
    
    // ... 在你的组件里 ...
    extensions={[
        basicDark,                // 基础主题
        json(),                   // JSON 语言支持
        syntaxHighlighting(myCustomHighlight) // 应用自定义高亮规则(会覆盖basicDark中对propertyName的定义)
    ]}
    

    这需要先安装 @lezer/highlight (npm install @lezer/highlight)。这种方式让你能精细控制每个语法元素的样式,而无需修改原始主题包。

方案三:检查 CodeMirror 相关库版本

极少数情况下,版本冲突也可能导致奇怪的问题。@uiw/react-codemirror 依赖于一系列 @codemirror/* 包。如果这些包的版本之间不兼容,可能会出现渲染或行为异常。

  1. 检查版本:
    运行 npm list 或查看 package-lock.json / yarn.lock 文件,确认 @uiw/react-codemirror, @codemirror/state, @codemirror/view, @codemirror/lang-json, @uiw/codemirror-theme-basic (或其他你用的主题/语言包) 等核心库的版本。

  2. 参考兼容性:
    通常 @uiw/react-codemirror 的文档或 README 会指明它所兼容的 @codemirror/* 版本范围。确保你的依赖都在推荐范围内。

  3. 清理和重装:
    如果怀疑是版本问题,可以尝试删除 node_modules 和锁文件 (package-lock.jsonyarn.lock),然后重新运行 npm installyarn install

    rm -rf node_modules package-lock.json # 或 yarn.lock
    npm install # 或 yarn install
    

安全建议:
更新或安装依赖时,留意版本号。主版本号(major version)的变更通常意味着可能有不兼容的 API 改动。小版本(minor)和补丁(patch)更新则相对安全。如果遇到问题,尝试锁定到一个已知可行的版本组合。

通过执行方案一(添加 json() 扩展),基本上就能解决最初的 JSON 高亮与官方示例不符的问题。方案二和三是辅助检查步骤,确保整个环境配置是健全的。现在,你的 react-codemirror 编辑器在处理 JSON 时应该能展现出预期的、色彩分明的高亮效果了。