CodeMirror6 是状态与视图分离架构:
@codemirror/state:纯数据层,EditorState(文档、选区、不可变),无 DOM@codemirror/view:视图渲染层,把EditorState渲染成 DOM、处理输入、滚动、光标、装饰、交互

1. 安装依赖
perl
npm install @codemirror/view @codemirror/state
最小核心只需要这两个包,
@codemirror/view视图渲染,@codemirror/state状态管理。 如果想要行号、快捷键、选中样式,可以额外引入内置扩展。
2. 完整最小示例(原生HTML+TS/JS)
index.html
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>CM6 Demo</title>
<style>
#editor {
height: 400px;
border: 1px solid #ccc;
}
</style>
</head>
<body>
<div id="editor"></div>
<script type="module" src="./main.js"></script>
</body>
</html>
main.js
js
import { EditorView, lineNumbers, highlightActiveLine, drawSelection, EditorView } from "@codemirror/view"
import { EditorState } from "@codemirror/state"
// 1. 创建 state(纯数据层,无DOM)
const state = EditorState.create({
doc: `// 测试代码
function hello() {
return "Hello CodeMirror View"
}
`,
extensions: [
lineNumbers(), // 行号
highlightActiveLine(), // 高亮当前行
drawSelection(), // 绘制选区光标
// 监听内容变化
EditorView.updateListener.of((update) => {
if (update.docChanged) {
// 获取最新文本
const text = update.state.doc.toString()
console.log("编辑器内容:", text)
}
})
]
})
// 2. 创建 EditorView,挂载到DOM
const view = new EditorView({
state,
parent: document.querySelector("#editor")
})
// ========== 常用API ==========
// 获取全部文本
console.log(view.state.doc.toString())
// 编程方式修改内容(必须 dispatch,禁止直接改dom)
// view.dispatch({
// changes: { from: 0, to: view.state.doc.length, insert: "新内容" }
// })
// 销毁编辑器(组件卸载时调用,防止内存泄漏)
// view.destroy()
3. 核心流程
EditorState.create → 生成不可变状态(文档、选区、扩展) new EditorView({state, parent}) → 视图订阅state,渲染DOM、处理输入事件 ✅ 单向数据流:所有修改必须走 view.dispatch(transaction)
4. 可选:加上JS语法高亮
bash
npm install @codemirror/lang-javascript
css
import { javascript } from "@codemirror/lang-javascript"
const state = EditorState.create({
doc: "...",
extensions: [
lineNumbers(),
highlightActiveLine(),
drawSelection(),
javascript() // JS语法高亮
]
})
5. React简易封装版本
javascript
import { useEffect, useRef } from "react"
import { EditorView, lineNumbers, highlightActiveLine, drawSelection, EditorView } from "@codemirror/view"
import { EditorState } from "@codemirror/state"
export default function SimpleCodeEditor() {
const containerRef = useRef<HTMLDivElement>(null)
const viewRef = useRef<EditorView | null>(null)
useEffect(() => {
if (!containerRef.current) return
const state = EditorState.create({
doc: "const a = 1",
extensions: [
lineNumbers(),
highlightActiveLine(),
drawSelection(),
EditorView.updateListener.of(u => {
if(u.docChanged) console.log(u.state.doc.toString())
})
]
})
const view = new EditorView({ state, parent: containerRef.current })
viewRef.current = view
return () => {
view.destroy() // 卸载销毁!
}
}, [])
return <div ref={containerRef} style={{height:400, border:"1px solid #ddd"}}/>
}