如何使用 @codemirror/view 库创建一个简单的代码编辑器

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"}}/>
}
相关推荐
huali41 分钟前
草稿该存在哪:per-session 是怎么成为共识的
前端·产品经理·产品
IPIPD43 分钟前
Proxifier规则为什么没有生效?从连接日志定位规则命中与DNS路径
前端
baiyunfan44 分钟前
微信小程序做 PDA 扫码出入库:库存并发控制与 4 个坑
前端
ZoeLandia44 分钟前
React6:组件通信
前端·react.js·typescript
爱勇宝44 分钟前
AI 都这么强了,咱们程序员以后还能干点啥?
前端·后端·程序员
FPGA小徐1 小时前
【一生一芯 / PA】AM 核心文件逐行解析(上):从 start.S 到 gpu.c
前端·算法
三十而立洋1 小时前
知枝 · MindSprout:可以直接将 markdown 转换为思维导图的 vscode 插件
前端·visual studio code
兔子零10241 小时前
架构师最危险的能力:用正确的技术,做出错误的架构决策
前端·javascript·后端
Bug收容所1 小时前
Vue+js Day2
前端·javascript·vue.js