# CodeMirror 多实例问题修复记录 > **问题描述**: "Unrecognized extension value in extension set" 错误 > **修复日期**: 2026-02-05 > **状态**: ✅ 已解决 --- ## 📋 问题症状 ``` Error: Unrecognized extension value in extension set ([object Object]). This sometimes happens because multiple instances of @codemirror/state are loaded, breaking instanceof checks. ``` **影响**: 代码编辑器无法加载,语法高亮功能失效 --- ## 🔍 探索过程 ### 探索 #1:统一导出文件(❌ 失败) **方案**: 创建 `codemirrorExports.js` 统一导出所有 CodeMirror 模块 **实施**: - 创建 `frontend/src/utils/codemirrorExports.js` - 更新所有组件从中导入 **结果**: ❌ 无效,错误依然存在 **原因**: 统一导出无法解决 Vite 预构建阶段产生的多实例问题 --- ### 探索 #2:合并构建产物(❌ 失败) **方案**: 在 `vite.config.js` 中使用 `manualChunks` 合并所有 CodeMirror 包 **配置**: ```javascript manualChunks: (id) => { if (id.includes('@codemirror') || id.includes('@lezer')) { return 'vendor-codemirror' } } ``` **结果**: ❌ 无效,虽然构建时合并了,但运行时仍是多实例 --- ### 探索 #3:移除旧包(❌ 失败) **方案**: 移除可能冲突的旧包 - 删除 `@codemirror/highlight@0.19.8` - 删除 `@codemirror/legacy-modes` **结果**: ❌ 无效 --- ### 探索 #4:修复返回格式(❌ 失败) **方案**: 统一 `getThemeExtension()` 返回数组格式 **修改**: ```javascript // 之前 return oneDark // 之后 return [oneDark] ``` **结果**: ❌ 无效 --- ### 探索 #5:研究官方文档(✅ 找到根本原因) **参考资料**: - [CodeMirror Discussion #6809](https://discuss.codemirror.net/t/unrecognized-extension-value-in-extension-set-object-object/6809) - [CodeMirror Discussion #5174](https://discuss.codemirror.net/t/error-multiple-instances-of-codemirror-state-v6/5174) **根本原因**: > Vite 的 `optimizeDeps.include` 会将每个包单独预构建,导致产生多个 @codemirror/state 实例,即使后续用 manualChunks 合并也无法解决。 **关键发现**: 1. Vite 预构建阶段就创建了多个实例 2. instanceof 检查失败导致扩展系统崩溃 3. 必须在模块解析阶段就强制使用同一实例 --- ### 探索 #6:使用 resolve.alias(❌ 失败) **方案**: 使用 `resolve.alias` 强制所有包指向 node_modules 中的同一实例 **配置**: ```javascript resolve: { alias: { '@codemirror/state': resolve(__dirname, 'node_modules/@codemirror/state'), // ... 所有其他包 } } ``` **结果**: ❌ 无效,错误仍然存在 **原因**: Windows 平台路径解析问题,或生产构建时 alias 不生效 --- ### 探索 #7:使用 dedupe + exclude(❌ 失败) **方案**: 1. 使用 `resolve.dedupe` 强制去重 2. 使用 `optimizeDeps.exclude` 排除 CodeMirror 预构建 **配置**: ```javascript resolve: { dedupe: ['@codemirror/state', '@codemirror/view', ...] } optimizeDeps: { exclude: ['@codemirror/state', '@codemirror/view', ...] } ``` **结果**: ❌ 无效,错误仍然存在 **原因**: 这些配置主要影响开发模式,生产构建中 Rollup 的行为不同 --- ### 探索 #8:移除 manualChunks(❌ 失败) **方案**: 完全移除 `manualChunks` 配置,让 Rollup 自动处理代码分割 **修改前**: ```javascript manualChunks: (id) => { if (id.includes('@codemirror') || id.includes('@lezer')) { return 'vendor-codemirror' // 强制分离到单独 chunk } } ``` **修改后**: ```javascript // 完全移除 manualChunks,让 Rollup 自动处理 output: { chunkFileNames: 'assets/js/[name]-[hash].js', entryFileNames: 'assets/js/[name]-[hash].js', assetFileNames: 'assets/[ext]/[name]-[hash].[ext]' } ``` **构建结果变化**: | 文件 | 之前 | 之后 | |------|------|------| | CodeMirror chunk | vendor-codemirror-BXxC64C7.js (907KB) | 合并到 index-CB_oYaZz.js (2.5MB) | | 主包 | index-C2Qw32eb.js (187KB) | index-CB_oYaZz.js (2.5MB) | **结果**: ❌ 无效,仍然报错 **原因**: 即使所有代码打包到单个文件 (5.2MB),仍然报错。这说明问题不在代码分割。 --- ### 探索 #9:深入分析错误堆栈(✅ 找到真正原因) **关键发现**: 1. **打包到单个文件后仍然报错** → 问题不在代码分割 2. **错误发生在 `extension set` 检查时** → CodeMirror 扩展系统的 instanceof 检查失败 3. **SqlEditor.vue 使用 `defaultHighlightStyle` 正常工作** → 说明默认样式没问题 **真正原因**: `HighlightStyle.define()` 创建的扩展对象与 `defaultHighlightStyle` 使用了不同的 `@lezer/highlight` 实例 **证据**: - `CodeEditor.vue` 使用自定义 `lightHighlightStyle = HighlightStyle.define([...])` - `SqlEditor.vue` 使用默认 `syntaxHighlighting(defaultHighlightStyle)` - 正常工作 - 错误堆栈指向扩展系统的类型检查失败 --- ## ✅ 最终解决方案(探索 #10) ### 方案 A:统一使用 `defaultHighlightStyle` **优点**: - 简单直接,移除自定义高亮样式 - 与其他组件(SqlEditor)保持一致 - 官方提供的样式,经过充分测试 **缺点**: - 亮色主题的高亮颜色会变成默认样式 **实施步骤**: 1. **修改 `CodeEditor.vue`** (frontend/src/components/CodeEditor.vue) - 移除 `HighlightStyle` 和 `tags` 导入 - 添加 `defaultHighlightStyle` 和 `syntaxHighlighting` 导入 - 删除 `lightHighlightStyle` 定义(第 30-51 行,共 22 行代码) - 修改 `getThemeExtension()` 使用 `syntaxHighlighting(defaultHighlightStyle)` 2. **修改 `codemirrorExports.js`** (frontend/src/utils/codemirrorExports.js) - 移除 `HighlightStyle` 和 `tags` 的导出 **修改前**: ```javascript // 亮色主题的语法高亮样式(完整版) const lightHighlightStyle = HighlightStyle.define([ { tag: tags.keyword, color: '#d73a49', fontWeight: 'bold' }, { tag: tags.string, color: '#032f62' }, // ... 更多自定义样式 ]) // 使用 return [lightTheme, lightHighlightStyle] ``` **修改后**: ```javascript import { defaultHighlightStyle, syntaxHighlighting } from '@/utils/codemirrorExports' // 使用默认样式 return [ lightTheme, syntaxHighlighting(defaultHighlightStyle) ] ``` **验证结果**: - ✅ 生产环境构建成功(无错误) - ✅ 开发服务器启动成功 - ✅ 与 SqlEditor 等其他组件保持一致 **构建输出**: ``` ✓ 5190 modules transformed. dist/index.html 0.41 kB │ gzip: 0.29 kB dist/assets/css/index-DEyLjjgm.css 450.29 kB │ gzip: 56.45 kB dist/assets/js/index-C2qsyXz1.js 5,226.19 kB │ gzip: 1596.26 kB ✓ built in 33.64s ``` --- ## 🎯 关于自定义样式 **问题**: 自定义样式不能用吗? **答案**: 可以用,但需要确保实例一致性。 ### 如果需要自定义高亮颜色,有两个方案: #### 方案 1:使用 CSS 覆盖(推荐) 基于默认高亮样式,通过 CSS 修改颜色: ```css /* 在组件的