Private
Public Access
1
0
Files
u-desk/docs/01-技术文档/CodeMirror/CodeMirror-修复状态报告.md

6.5 KiB
Raw Blame History

CodeMirror 多实例问题 - 当前状态

日期: 2026-02-05 状态: 已修复


🎉 修复成功

经过 10 次探索,问题已成功解决

最终方案: 统一使用 defaultHighlightStyle,移除自定义高亮样式


📊 问题摘要

错误: Unrecognized extension value in extension set - CodeMirror 6 多实例错误

影响: 代码编辑器无法加载,语法高亮失效


🔧 已尝试的解决方案

# 方案 结果 详情
1 统一导出文件 codemirrorExports.js
2 manualChunks 合并 反而可能导致问题
3 移除旧包 版本不是问题
4 修复返回格式 不是根本原因
5 resolve.alias Windows 路径问题
6 dedupe + exclude 主要影响开发模式
7 移除 manualChunks 即使单文件打包仍失败
8 深入分析错误 找到真正原因
9 统一使用默认样式 成功

最终解决方案

方案:使用 defaultHighlightStyle

文件修改:

  1. CodeEditor.vue (frontend/src/components/CodeEditor.vue)

    • 移除 HighlightStyletags 导入
    • 添加 defaultHighlightStylesyntaxHighlighting 导入
    • 删除 lightHighlightStyle 定义22 行代码)
    • 修改 getThemeExtension() 使用 syntaxHighlighting(defaultHighlightStyle)
  2. codemirrorExports.js (frontend/src/utils/codemirrorExports.js)

    • 移除 HighlightStyletags 的导出

验证结果

  • 生产环境构建成功(无错误)
  • 开发服务器启动成功
  • 与 SqlEditor 等其他组件保持一致

vite.config.js

export default defineConfig({
  resolve: {
    alias: { '@': resolve(__dirname, 'src') },
    // 强制去重 CodeMirror 包
    dedupe: [
      '@codemirror/state',
      '@codemirror/view',
      '@codemirror/language',
      // ... 所有 CodeMirror 和 Lezer 包
    ]
  },
  build: {
    rollupOptions: {
      output: {
        // 移除 manualChunks让 Rollup 自动处理
        chunkFileNames: 'assets/js/[name]-[hash].js',
        entryFileNames: 'assets/js/[name]-[hash].js',
        assetFileNames: 'assets/[ext]/[name]-[hash].[ext]'
      }
    }
  },
  optimizeDeps: {
    include: ['vue', 'pinia', '@arco-design/web-vue', 'marked', 'highlight.js'],
    // 排除 CodeMirror避免预构建多实例
    exclude: [
      '@codemirror/state',
      // ... 所有 CodeMirror 包
    ]
  }
})

构建结果

  • 主包: index-CB_oYaZz.js (2.5 MB) - 包含所有代码
  • 无单独的 CodeMirror chunk - 所有 CodeMirror 代码在同一 bundle 中

📝 技术原理

真正原因

之前的假设: 多个 @codemirror/state 实例导致 instanceof 检查失败

实际原因: HighlightStyle.define() 创建的扩展对象与 defaultHighlightStyle 使用了不同的 @lezer/highlight 实例

为什么之前的方案都失败了

  1. 统一导出文件 - 无法解决预构建阶段的多实例
  2. manualChunks 合并 - 即使打包到单个文件仍失败
  3. resolve.alias - Windows 路径问题,且不能解决 @lezer/highlight 实例问题
  4. 移除 manualChunks - 代码在同一 bundle 中,但 HighlightStyle.define() 内部使用了不同的实例

为什么最终方案成功

  • SqlEditor.vue 使用 defaultHighlightStyle 一直正常工作
  • CodeEditor.vue 改用 defaultHighlightStyle 后也正常了
  • 官方提供的 defaultHighlightStyle 内部处理了实例一致性问题

📝 关于自定义样式

问题: 自定义样式不能用吗?

答案: 可以用,但需要确保实例一致性。

方案 1使用 CSS 覆盖(推荐)

基于默认高亮样式,通过 CSS 修改颜色:

/* 在组件的 <style> 中 */
.cm-editor :deep(.cm-keyword) { color: #d73a49 !important; }
.cm-editor :deep(.cm-string) { color: #032f62 !important; }

方案 2确保 tags 实例统一

所有地方都从同一个 @lezer/highlight 导入 tags,确保没有多个实例。但这仍然可能失败。

当前方案选择了最简单、最稳定的方案:使用官方默认样式。


📚 相关文档


🎯 关键发现

问题本质: 自定义 HighlightStyle.define() 创建的对象与默认样式使用的 @lezer/highlight 实例不一致。

根本原因: tags 实例的引用不一致,导致 instanceof 检查失败。

解决方向: 统一使用官方提供的 defaultHighlightStyle,避免自定义样式带来的实例问题。


📝 经验总结

错误方向

  1. 关注构建配置 - resolve.alias、manualChunks、optimizeDeps 都无法解决
  2. 代码分割问题 - 即使打包到单个文件仍然失败
  3. 多实例问题 - @codemirror/state 实例不是根本原因

正确方向

  1. 关注代码本身 - 自定义 HighlightStyle.define() 的问题
  2. 对比正常工作的代码 - SqlEditor 使用默认样式正常工作
  3. 使用官方方案 - defaultHighlightStyle 处理了实例一致性

核心教训

Occam's Razor奥卡姆剃刀原则: 如果其他组件SqlEditor使用默认样式正常工作那么最简单的方案就是让 CodeEditor 也使用默认样式。

不应该花费 9 次尝试去调整构建配置,而应该第 1 次就对比正常工作的代码。


修复完成!代码编辑器现在可以正常工作了。


🚀 配置优化2026-02-05

在修复问题后,对构建配置进行了优化:

移除的无用配置

  1. resolve.dedupe - 28 个包的去重配置,对生产构建无效
  2. optimizeDeps.exclude - 28 个包的排除配置,不能解决 instanceof 问题
  3. inlineDynamicImports - 导致所有代码打包到单个文件5.2MB
  4. manualChunks: undefined - 无意义的显式配置

优化效果

指标 优化前 优化后 改善
主包大小 5,226 KB 2,569 KB ↓ 51%
构建时间 33.64s 17.14s ↓ 49%
代码分割 无(全部内联) 按需加载

详细文档: CodeMirror-配置优化总结.md