【前端】【微项目】【代码编辑器库CodeMirror】支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等
✨打造属于你的在线代码编辑器:CodeMirror 使用指南
一款轻量、高效、可定制的代码编辑器组件,前端项目提升体验的秘密武器!
📌 什么是 CodeMirror?
CodeMirror 是一个专为网页打造的多语言代码编辑器,支持语法高亮、自动缩进、主题切换、行号显示、括号匹配等功能。它广泛应用于各种在线 IDE、Markdown 编辑器、教学平台和数据可视化系统中。
🧱 实战案例:构建一个 Python 编辑器
让我们一步步拆解这份完整的 HTML 页面,看看 CodeMirror 如何轻松变身为一个功能丰富的 Python 编辑器界面。
效果
🎯 功能亮点
🔧 核心配置解析
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);
通过监听 change
和 cursorActivity
,我们可以动态更新:
- 当前总行数
- 光标所在的行与列
document.getElementById(\'cursor-position\').textContent = `第 ${cursor.line + 1} 行, 第 ${cursor.ch + 1} 列`;
📱 页面美化:TailwindCSS + 自定义样式
为了增强界面美感与用户体验,页面采用了:
- 玻璃磨砂导航栏(
glass-effect
) - 分隔按钮 UI
- 高对比度编辑器背景(#1E1E1E)
- 代码字体使用 Consolas/Monaco 系列
此外,状态栏展示了 git 分支
、语法状态
、编码格式
、缩进设置
等仿 VSCode 的元素。
🚀 如何拓展?
如果你想让这个编辑器功能更丰富,可以考虑:
.py
文件mode
,支持 JS、HTML、Markdown 等✅ 总结
通过 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>