> 技术文档 > 【前端】【微项目】【代码编辑器库CodeMirror】支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等

【前端】【微项目】【代码编辑器库CodeMirror】支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等



✨打造属于你的在线代码编辑器:CodeMirror 使用指南

一款轻量、高效、可定制的代码编辑器组件,前端项目提升体验的秘密武器!


📌 什么是 CodeMirror?

CodeMirror 是一个专为网页打造的多语言代码编辑器,支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等功能。它广泛应用于各种在线 IDE、Markdown 编辑器、教学平台和数据可视化系统中。


🧱 实战案例:构建一个 Python 编辑器

让我们一步步拆解这份完整的 HTML 页面,看看 CodeMirror 如何轻松变身为一个功能丰富的 Python 编辑器界面


效果

【前端】【微项目】【代码编辑器库CodeMirror】支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等

🎯 功能亮点

功能点 说明 🎨 语法高亮 自动识别 Python 代码结构 🔢 行号显示 默认开启,每一行都清晰可见 🧩 括号匹配 提升编码准确性,自动高亮配对括号 🔍 活跃行高亮 当前编辑行背景加亮,聚焦更清晰 🔁 禁止自动换行 横向滚动,长行代码更易读 📏 光标追踪 实时显示当前行列位置 📈 动态统计 显示总行数,便于代码管理 📱 响应式设计 使用 TailwindCSS 构建现代 UI 💡 支持运行 虽然示例中未真正执行,但界面已预留执行按钮布局

🔧 核心配置解析

const editor = CodeMirror(container, { value: \"...\",  // 编辑器初始内容 mode: \"python\", // 设置语言模式(python) theme: \"monokai\", // 使用 monokai 主题 lineNumbers: true, // 显示行号 lineWrapping: false, // 禁用自动换行 styleActiveLine: true, // 高亮当前行 matchBrackets: true, // 匹配括号 indentUnit: 4, // 缩进单位为 4 个空格 smartIndent: true, // 智能缩进 viewportMargin: Infinity // 无限高度视口,防止滚动截断});

这些配置项就是让 CodeMirror 变得“像 IDE 一样”的秘密。


💡 交互增强:监听变化和状态更新

editor.on(\'change\', updateStatus);editor.on(\'cursorActivity\', updateStatus);

通过监听 changecursorActivity,我们可以动态更新:

  • 当前总行数
  • 光标所在的行与列
document.getElementById(\'cursor-position\').textContent = `${cursor.line + 1} 行, 第 ${cursor.ch + 1}`;

📱 页面美化:TailwindCSS + 自定义样式

为了增强界面美感与用户体验,页面采用了:

  • 玻璃磨砂导航栏glass-effect
  • 分隔按钮 UI
  • 高对比度编辑器背景(#1E1E1E)
  • 代码字体使用 Consolas/Monaco 系列

此外,状态栏展示了 git 分支语法状态编码格式缩进设置等仿 VSCode 的元素。


🚀 如何拓展?

如果你想让这个编辑器功能更丰富,可以考虑:

拓展方向 描述 💾 文件上传/下载 将编辑内容导出为 .py 文件 ☁️ 云端保存 利用后端 API 实现云同步 🎯 真正运行代码 通过 Web Worker 或 Python Web 环境(如 Pyodide)执行代码 🧱 多语言支持 动态切换 mode,支持 JS、HTML、Markdown 等 🧩 插件扩展 引入 lint、自动补全、代码折叠等 CodeMirror 插件

✅ 总结

通过 CodeMirror,我们能在几分钟内打造出一款:

  • 颜值在线
  • 功能实用
  • 易于拓展
  • 体验丝滑

源码

<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"UTF-8\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"> <title>CodeMirror Editor</title> <script src=\"https://cdn.tailwindcss.com\"></script> <link href=\"https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css\" rel=\"stylesheet\">  <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.min.css\"> <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/theme/monokai.min.css\"> <script src=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/lib/codemirror.min.js\"></script> <script src=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/mode/python/python.min.js\"></script> <script src=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/addon/selection/active-line.min.js\"></script> <script src=\"https://cdn.jsdelivr.net/npm/codemirror@5.65.2/addon/edit/matchbrackets.min.js\"></script> <script> tailwind.config = { theme: { extend: {  colors: { primary: \'#165DFF\', secondary: \'#00B42A\', danger: \'#F53F3F\', warning: \'#FF7D00\', dark: \'#1D2129\', \'dark-light\': \'#4E5969\', \'light-bg\': \'#F2F3F5\', \'editor-bg\': \'#1E1E1E\',  },  fontFamily: { code: [\'Consolas\', \'Monaco\', \'Courier New\', \'monospace\'],  }, } } } </script> <style type=\"text/tailwindcss\"> @layer utilities { .content-auto { content-visibility: auto; } .scrollbar-thin { scrollbar-width: thin; } .scrollbar-thin::-webkit-scrollbar { width: 6px; height: 6px; } .scrollbar-thin::-webkit-scrollbar-thumb { background-color: rgba(156, 163, 175, 0.5); border-radius: 3px; } .editor-shadow { box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1); } .glass-effect { backdrop-filter: blur(8px); background-color: rgba(255, 255, 255, 0.7); } } </style> <style> .CodeMirror { height: 100%; font-family: \'Consolas\', \'Monaco\', \'Courier New\', monospace; font-size: 14px; line-height: 1.5; } .CodeMirror-linenumber { padding: 0 8px 0 4px; } .CodeMirror-gutters { background-color: #252526; border-right: 1px solid #333; } </style></head><body class=\"bg-gray-50 font-sans text-dark\">  <header class=\"glass-effect border-b border-gray-200 fixed top-0 left-0 right-0 z-50\"> <div class=\"container mx-auto px-4 py-3 flex items-center justify-between\"> <div class=\"flex items-center space-x-2\"> <i class=\"fa fa-code text-primary text-xl\"></i> <h1 class=\"text-xl font-bold text-dark\">CodeWriter</h1> </div> <div class=\"hidden md:flex items-center space-x-4\"> <button class=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 transition-colors\">  <i class=\"fa fa-file-o mr-1\"></i> New </button> <button class=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 transition-colors\">  <i class=\"fa fa-folder-open-o mr-1\"></i> Open </button> <button class=\"px-3 py-1.5 text-sm rounded-md hover:bg-gray-100 transition-colors\">  <i class=\"fa fa-save mr-1\"></i> Save </button> <div class=\"h-6 w-px bg-gray-300\"></div> <button class=\"px-3 py-1.5 text-sm rounded-md bg-primary text-white hover:bg-primary/90 transition-colors\">  <i class=\"fa fa-play mr-1\"></i> Run </button> </div> <div class=\"flex items-center space-x-3\"> <button class=\"p-1.5 rounded-full hover:bg-gray-100 transition-colors md:hidden\">  <i class=\"fa fa-bars\"></i> </button> <div class=\"relative\">  <img src=\"https://picsum.photos/id/64/200/200\" alt=\"User avatar\" class=\"w-8 h-8 rounded-full object-cover border-2 border-primary\"> </div> </div> </div> </header>  <main class=\"container mx-auto pt-20 pb-10 px-4\">  <div class=\"bg-white rounded-t-lg border border-gray-200 border-b-0 flex items-center px-4 py-2\"> <div class=\"flex items-center space-x-2\"> <div class=\"w-3 h-3 rounded-full bg-danger\"></div> <div class=\"w-3 h-3 rounded-full bg-warning\"></div> <div class=\"w-3 h-3 rounded-full bg-secondary\"></div> </div> <div class=\"ml-4 flex items-center text-sm text-dark-light\"> <div class=\"px-3 py-1 rounded-t-md bg-editor-bg text-white font-medium\">main.py</div> <div class=\"ml-2 flex items-center\">  <i class=\"fa fa-globe mr-1\"></i> Python </div> </div> </div>  <div class=\"bg-editor-bg rounded-b-lg editor-shadow overflow-hidden h-[70vh]\"> <div id=\"editor-container\" class=\"w-full h-full\"></div> </div>  <div class=\"mt-3 flex flex-wrap items-center justify-between bg-white rounded-lg border border-gray-200 p-3 text-sm text-dark-light\"> <div class=\"flex items-center space-x-4 mb-2 sm:mb-0\"> <div class=\"flex items-center\">  <i class=\"fa fa-git mr-1\"></i> master </div> <div class=\"flex items-center\">  <i class=\"fa fa-check-circle mr-1 text-secondary\"></i> 无错误 </div> </div> <div class=\"flex items-center space-x-4\"> <div class=\"flex items-center\">  <i class=\"fa fa-file-text-o mr-1\"></i> <span id=\"line-count\">128</span></div> <div class=\"flex items-center\">  <i class=\"fa fa-tachometer mr-1\"></i> UTF-8 </div> <div class=\"flex items-center\">  <i class=\"fa fa-indent mr-1\"></i> 空格: 4 </div> <div class=\"flex items-center\">  <i class=\"fa fa-cursor mr-1\"></i> <span id=\"cursor-position\">第 1 行, 第 1 列</span> </div> </div> </div> </main> <script> document.addEventListener(\'DOMContentLoaded\', function() { // 初始化CodeMirror编辑器 const editor = CodeMirror(document.getElementById(\'editor-container\'), { value: `def fibonacci(n): \"\"\"生成斐波那契数列的前n项\"\"\" sequence = [] a, b = 0, 1 for _ in range(n): sequence.append(a) a, b = b, a + b return sequence# 打印前10个斐波那契数print(\"斐波那契数列前10项:\")for i, num in enumerate(fibonacci(10)): print(f\"第{i+1}项: {num}\")def factorial(n): \"\"\"计算n的阶乘\"\"\" if n == 0: return 1 else: return n * factorial(n-1)# 计算5的阶乘print(\"\\n5的阶乘是:\", factorial(5))class Calculator: \"\"\"简单的计算器类\"\"\" def __init__(self): self.memory = 0 def add(self, a, b=None): \"\"\"加法运算\"\"\" if b is None: self.memory += a return self.memory else: return a + b def subtract(self, a, b=None): \"\"\"减法运算\"\"\" if b is None: self.memory -= a return self.memory else: return a - b`, mode: \"python\", theme: \"monokai\", lineNumbers: true, lineWrapping: false, // 禁用自动换行,使内容可以横向滚动 styleActiveLine: true, matchBrackets: true, indentUnit: 4, tabSize: 4, smartIndent: true, autofocus: true, viewportMargin: Infinity // 允许无限滚动 }); // 更新行号和光标位置显示 function updateStatus() { const cursor = editor.getCursor(); const lineCount = editor.lineCount(); document.getElementById(\'line-count\').textContent = lineCount; document.getElementById(\'cursor-position\').textContent =  `${cursor.line + 1} 行, 第 ${cursor.ch + 1}`; } // 监听编辑器变化事件 editor.on(\'change\', updateStatus); editor.on(\'cursorActivity\', updateStatus); // 初始化状态 updateStatus(); // 监听窗口大小变化,调整编辑器高度 window.addEventListener(\'resize\', function() { editor.refresh(); }); // 解决初始加载时可能不显示滚动条的问题 setTimeout(() => { editor.refresh(); }, 100); }); </script></body></html>