ProseMirror初探

引言

ProseMirror是代码编辑器[CodeMirror](codemirror.net/) "https://codemirror.net/)")的作者Marijn Haverbeke开发的一款开源的富文本编辑器。 提供了一组工具和设计概念用来构建富文本编辑器,像乐高积木一样,需要开发者自己组装,并不是开箱即用。

ProseMirror基础知识

ProseMirror提供了四个核心模块,编辑器任何操作都需要他们。

四个核心模块如下:

  • prosemirror-model定义编辑器文档模型,用来描述编辑器内容

  • prosemirror-state描述整个编辑器状态的数据结构

  • prosemirror-view将编辑器状态state 展示成对应视图

  • prosemirror-transform提供了可以撤销和恢复的修改文档的能力

第一个简单的编辑器

1. 创建一个react项目

js 复制代码
// 使用react脚手架创建一个react项目
create-react-app + 项目名称

2. 安装prosemirror相关依赖包

js 复制代码
npm i prosemirror-view prosemirror-model prosemirror-state

3. 创建Editor编辑器对象

js 复制代码
// Editor.js
import React from 'react';
import { EditorView } from 'prosemirror-view';
import { EditorState } from 'prosemirror-state';
import { Schema } from 'prosemirror-model';
import './Editor.css';

class Editor extends React.PureComponent {
    constructor() {
        super();
        this.editorRef = React.createRef(null);
    }

    componentDidMount() {
        this.createEditor();
    }

    createSchema() {
        // 创建schema
        this.schema = new Schema({
            nodes: {
                doc: {
                content: 'block'
            },
            paragraph: {
                content: 'inline*',
                group: 'block',
                toDOM: () => ['p', 0]
            },
            text: {
                group: 'inline'
            }
           },
        })
    }

    createPlugins() {
        // 编辑器插件,暂时不使用任何插件
        return [];
    }

    createView() {
        const { current: place } = this.editorRef;
        // 创建编辑器视图对象
        this.view = new EditorView(place, {
            state: EditorState.create({
                schema: this.schema
            })

        });

        // 创建编辑器数据对象
        const newState = this.view.state.reconfigure({
            plugins: this.createPlugins()
        });
        // 更新编辑器数据到视图
        this.view.updateState(newState);

    }

    createEditor() {
        this.createSchema();
        this.createView();
    }

    render() {
        return (
            <div className='editor-container' ref={this.editorRef}></div>
        )
    }

}

export default Editor;

// Editor.css
.ProseMirror {
    outline-width: 0px;
    margin: 100px 60px;
}

4. 入口文件中引入编辑器

js 复制代码
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import Editor from './Editor';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Editor />);

schema

描述编辑器中nodes类型和nodes之间嵌套关系,文档节点与dom节点之间的转化关系。例如定义一个最简单的文档schema结构,由文本节点构建的文档。

js 复制代码
new Schema({
    nodes: {
        // 文档顶层节点,必须有
        doc: {
            // 内容由0个或者多个text节点组成
            content: 'text*'
        },
        // text 节点
        text: {}
    }
})

上面schema结构应用到编辑器后对应dom结构如下:

给上面shema增加一个段落节点,即文档doc顶层节点由多个段落节点paragraph组成,段落节点由多个text节点组成。

css 复制代码
new Schema({
   nodes: {
    // 文档顶层节点,必须有
    doc: {
    // 内容由0个或者多个paragraph节点组成
       content: 'paragraph*'
    },
    paragraph: {
       // 内容由0个或者多个text节点组成    
        content: 'text*',
        // 定义当前节点的默认序列化成 DOM/HTML 的方式, 子节点内容在p标签中。下面详细解释下toDOM应该返回的结构
        toDOM: () => ['p', 0]
    },
    // text 节点
    text: {}
   }
})

schema中增加paragraph后,对应dom节点增加了一层p标签。

上面schema结构中有使用到toDOM,这里详细说明下。toDOM是一个函数,定义了当前节点渲染到dom中的序列化方式,需要返回DOMOutputSpec结构。

toDOM: (node) -> DOMOutputSpec

DOMOutputSpec 是DOM结构的描述方式,具体有三种形式:

  1. 字符串,表示文本节点
  2. dom节点,表示自身
  3. 一个数组,数组中第一个值表示DOM元素对应的标题名,第二个值是一个对象表示当前节点的attributes属性。数组第二个值之后的任何值(包括第二个值,如果它不是一个普通属性对象的话) 都被认为是该 DOM 元素的子元素,后面的值必须是有一个有效的 DOMOutputSpec 值,或者是数字 0。例如:
less 复制代码
['div', {style:'background:green'}, 0], 表示<div style="color:red">子元素<div>
['div', {style:'color:red'}, ['p', 0]],表示 <div style="color:red"><p>子元素</p><div>

schema还有很多属性,在后面的文档中一点点介绍具体使用方法。

plugin

ProseMirror中提供了插件系统,用来扩展编辑行为和编辑状态。ProseMirror官方提供了很多插件,例如快捷键 keymap 插件,undo/redo功能的 history 插件。下面我们实现一个统计编辑次数的插件,感受下插件是什么。

javascript 复制代码
const EditCountPlugin = new Plugin({
    state: {
        init() {
           reutrn 0;
        },
        apply(tr, value) {
            console.log('editCount:', vlaue + 1);
            return value + 1;
        }
    }
})

插件中可以定义自己的数据state,插件初始化时会调用init方式,文档数据发生变化时会执行插件中apply方案。类似react中组件渲染的生命周期,后面写一篇详细的文章介绍ProseMirror中插件系统的设计原理。

相关推荐
dy17172 小时前
element-plus表格默认展开有子的数据
前端·javascript·vue.js
2501_915918416 小时前
Web 前端可视化开发工具对比 低代码平台、可视化搭建工具、前端可视化编辑器与在线可视化开发环境的实战分析
前端·低代码·ios·小程序·uni-app·编辑器·iphone
程序员的世界你不懂7 小时前
【Flask】测试平台开发,新增说明书编写和展示功能 第二十三篇
java·前端·数据库
索迪迈科技7 小时前
网络请求库——Axios库深度解析
前端·网络·vue.js·北京百思可瑞教育·百思可瑞教育
gnip7 小时前
JavaScript二叉树相关概念
前端
attitude.x8 小时前
PyTorch 动态图的灵活性与实用技巧
前端·人工智能·深度学习
β添砖java8 小时前
CSS3核心技术
前端·css·css3
空山新雨(大队长)8 小时前
HTML第八课:HTML4和HTML5的区别
前端·html·html5
猫头虎-前端技术9 小时前
浏览器兼容性问题全解:CSS 前缀、Grid/Flex 布局兼容方案与跨浏览器调试技巧
前端·css·node.js·bootstrap·ecmascript·css3·媒体