# wangEditor-next 中文全量文档(Raw Markdown) > 本文件由脚本自动生成,汇总中文 Guide 的完整 Markdown,适合一次性投喂 AI。 > 这是原始 Markdown 文件,不经过页面渲染。 > 生成时间: 2026年6月13日 23:43:32 (Asia/Shanghai) > 语言切换: [English Raw Markdown](/ai/en-guide-full.txt) ## 包含文件 1. `docs/zh/guide/index.md` 2. `docs/zh/guide/installation.md` 3. `docs/zh/guide/getting-started.md` 4. `docs/zh/guide/for-frame.md` 5. `docs/zh/guide/content.md` 6. `docs/zh/guide/toolbar-config.md` 7. `docs/zh/guide/editor-config.md` 8. `docs/zh/guide/csp-class-mode.md` 9. `docs/zh/guide/menu-config.md` 10. `docs/zh/guide/API.md` 11. `docs/zh/guide/node-define.md` 12. `docs/zh/guide/development.md` 13. `docs/zh/guide/i18n.md` 14. `docs/zh/guide/theme.md` 15. `docs/zh/guide/for-ts.md` 16. `docs/zh/guide/plugins.md` 17. `docs/zh/guide/video-course.md` --- ## 1. index.md # 优势 ## 其他富文本编辑器的问题 网络搜索“Web 富文本编辑器”你会得到很多结果,例如国内的 UEditor kindEditor ,国外的 CKEditor TinyMCE Quill ProseMirror Draft Slate 等等。也有很多人使用这些编辑器,或者来做二次开发。 但他们都存在下面几个问题(敲黑板~),这可能会大大影响你的**开发效率、开发成本和产品稳定性**。 ### 技术老旧 如 UEditor KindEditor ,依然使用 `document.execCommand` API 。这将大大影响产品的稳定性、扩展性。 到时候啥啥都实现不了,哪儿哪儿都有问题,会被 PM 鄙视:“人家 xxx 咋能行?” ### 中文不友好 如 CKEditor TinyMCE Quill ProseMirror 等,没有官方的中文文档。这将大大影响你的开发效率(今晚加班~) PS:英语特别好的,请略过。 ### 需要大量二次开发 如 ProseMirror Draft Slate ,他们虽然也是富文本编辑器,但他们仅仅是一个 core 或者 controller ,并不是一个完整的功能。 大量的二次开发,不仅仅会导致研发成本大增(本月封闭~),还可能因为测试不完善而出现无尽的 bug ,陷入泥潭。 PS:除非你们有强烈的定制开发需要。 ### 有框架的约束 如 Slate 和 Draft ,是依赖于 React 框架的。如果你想用到 Vue 中,工作量和难度是非常大的。 ### 无官方 React Vue 等组件 一些无框架依赖的,如 ProseMirror ,如果你要用到 Vue React ,需要自己封装组件。 ### 新产品尚未稳定 上述列出来的编辑器,都是比较成熟的产品,用户量较大。你可能还会搜到其他产品,如新开发的、用户量不大的。
无论如何,请你慎重选择,因为富文本编辑器的坑真的太多了,需要经过大量的测试、使用才会慢慢稳定。 选择稳定的产品,可参考 - github stars - npm 下载量 - npm 发布时间和频率 - 搜索引擎的相关结果数量 - 是否有大厂背书 - 是否有单元测试 / e2e 测试 ## wangEditor 的优势 一个产品的价值,就在于解决用户的问题,提高效率、降低成本、增加稳定性和扩展性。 wangEditor 不是为了做而做,也不是单纯的模仿谁,而是经过上述问题分析之后,给出一个系统的解决方案。旨在真正去解决用户的问题,产出自己的价值。 ### 使用主流技术 wangEditor 从 V5 版本开始,有较大的技术更新。 #### 1. 升级为 L1 能力 弃用了 `document.execCommand` API ,使用 [slate.js](https://www.slatejs.org/)(但不依赖 React)为内核,升级为 L1 能力。 这也是目前主流富文本编辑器的技术方案,如知名的 Quill ProseMirror Draft 都在使用。 #### 2. 使用 vdom 使用 vdom 技术(基于 [snabbdom.js](https://github.com/snabbdom/snabbdom) )做视图更新,model 和 view 分离,增加稳定性。 #### 3. 扩展性 使用扩展插件和模块的机制,保证**扩展性**。未来还会继续扩展更多功能。 其实,现在 wangEditor 内置的各个功能,也都是通过扩展插件和模块的形式搭建起来的。 ### 中文文档 wangEditor 有详细的中文文档,以及中文交流环境。 ### 及时反馈和沟通 也可以去 github 提交 issue ,团队都会及时反馈或受理。 ### 集成所有功能,无需二次开发 wangEditor 内置了所有常见的富文本操作功能,能满足绝大部分使用需求。直接配置使用即可,无需再二次开发。 ```js // wangEditor 已内置 50+ 菜单 editor.getAllMenuKeys() [ "bold","underline","italic","through","code","clearStyle","headerSelect","header1","header2","header3", "color","bgColor","insertLink","editLink","unLink","viewLink","insertImage","deleteImage","editImage", "viewImageLink","imageWidth30","imageWidth50","imageWidth100","blockquote","emotion","fontSize","fontFamily", "indent","delIndent","justifyLeft","justifyRight","justifyCenter","lineHeight","redo","undo","divider","codeBlock", "bulletedList","numberedList","insertTable","deleteTable","insertTableRow","deleteTableRow","insertTableCol", "deleteTableCol","tableHeader","tableFullWidth","insertVideo","deleteVideo","uploadImage","codeSelectLang" ] ``` PS:同时,wangEditor 有丰富的 [API](./API.md) 和足够的扩展性,允许你[自定义开发](./development.md)菜单、模块、插件等。 ### 很方便地应用于 Vue React wangEditor 基于 slate 内核开发,但不依赖于 React ,所以它本身是无框架依赖的。 并且,我们官方封装了 Vue React 组件,可以很方便的[用于 Vue React 等框架](./for-frame.md)。
其他框架,我们会继续支持,大家也可以提交 issue 。 ### 踩过 5000 个坑 wangEditor 开源多年,大量用户使用和反馈,已经解决了[很多问题](https://github.com/wangeditor-next/wangEditor-next/issues)。在 V5 版本测试过程中,也这些问题进行了重复测试,最大程度保证稳定性。 ### 团队作业,持续迭代升级 wangEditor 由多人团队持续维护,一起修复 bug 、升级功能、跟踪问题、社区答疑。 --- ## 2. installation.md # 安装 包括 vue React 组件 ## npm 安装 editor ```shell yarn add @wangeditor-next/editor # 或者 npm install @wangeditor-next/editor --save ``` 安装 React 组件(可选) ```shell yarn add @wangeditor-next/editor-for-react # 或者 npm install @wangeditor-next/editor-for-react --save ``` 安装 Vue2 组件(可选) ```shell yarn add @wangeditor-next/editor-for-vue2 # 或者 npm install @wangeditor-next/editor-for-vue2 --save ``` 安装 Vue3 组件(可选) ```shell yarn add @wangeditor-next/editor-for-vue # 或者 npm install @wangeditor-next/editor-for-vue --save ``` ## CDN ```html ``` 如果上述 CDN 访问不成功,可以在 npm 安装完成之后,在安装包找到 JS CSS 文件,步骤如下: - 新建一个 `test1` 文件夹,打开控制台,目录定位到该文件夹,执行 `npm install @wangeditor-next/editor` 或 `yarn add @wangeditor-next/editor` - 安装完成,打开 `node_modules/@wangeditor-next/editor/dist` 文件夹,即可找到 JS CSS 文件: - `index.js` - `css/style.css` - 把这俩文件拷贝出来,然后删掉 `test1` 文件夹 --- ## 3. getting-started.md # 快速开始 快速了解可查看[视频教程](./video-course.md)。用于 Vue React 参考[这里](./for-frame.md)。 ## 创建空白编辑器 可直接参考 [demo 示例](https://wangeditor-next.github.io/demo/)的网页源码。 ### 引入 CSS 定义样式 可自定义编辑器、工具栏的尺寸、边框、`z-index` 等样式。 ```html ``` ### 自定义元素样式 ```html ``` ### 定义 HTML 结构 ```html
``` ::: tip - 如果想要“全屏”功能,则要求工具栏、编辑器 DOM 节点必须是同一层级 - 当然,工具栏、编辑器 DOM 节点也可自由组合,例如 [仿腾讯文档 demo](https://wangeditor-next.github.io/demo/like-qq-doc.html) ::: ### 引入 JS 创建编辑器 ```html ``` ::: tip 不同 `mode` 可参考 demo - [mode: 'default'](https://wangeditor-next.github.io/demo/index.html) 默认模式 - 集成了 wangEditor 所有功能 - [mode: 'simple'](https://wangeditor-next.github.io/demo/simple-mode.html) 简洁模式 - 仅有部分常见功能,但更加简洁易用 ::: 这样就创建出了一个最基本的编辑器。 ![](/image/editor.png) ## 接下来 要实现一个完整的富文本编辑器功能,你可能还需要以下功能: - [内容处理](./content.md) - 获取内容,**设置内容**,展示内容 - [工具栏配置](./toolbar-config.md) - 插入新菜单,屏蔽某个菜单等 - [编辑器配置](./editor-config.md) - 兼听各个**生命周期**,自定义**粘贴** - [菜单配置](./menu-config.md) - 配置颜色、字体、字号、链接校验、**上传图片、上传视频**等 - [编辑器 API](./API.md) - 控制编辑器内容和选区 - [扩展新功能](./development.md) - 扩展菜单、元素、插件等 --- ## 4. for-frame.md # 用于 Vue React 快速了解可查看[视频教程](./video-course.md)。 ## Vue2 ### Demo - [Demo 源码](https://github.com/wangeditor-next/vue2-wangeditor-demo) - [在线 demo](https://stackblitz.com/edit/vue2-vite-starter-hkmsif) ### 安装 ```sh yarn add @wangeditor-next/editor # 或者 npm install @wangeditor-next/editor --save yarn add @wangeditor-next/editor-for-vue2 # 或者 npm install @wangeditor-next/editor-for-vue2 --save ``` ### 使用 模板 ```xml ``` script ```html ``` :::tip - 赋值 `this.editor` 时要用 `Object.seal()` - 组件销毁时,要及时销毁编辑器 ::: 记得引入 style ```html ``` ### 配置 可通过 `toolbarConfig` 和 `editorConfig` 来修改菜单栏和编辑器的配置,详细文档参考 - [工具栏配置](./toolbar-config.md) - 插入新菜单,屏蔽某个菜单等 - [编辑器配置](./editor-config.md) - 兼听各个**生命周期**,自定义**粘贴** - [菜单配置](./menu-config.md) - 配置颜色、字体、字号、链接校验、**上传图片、视频**等 【注意】编辑器配置中 `onXxx` 格式的生命周期函数,**必须通过 Vue 事件来传递,不可以放在 `editorConfig` 中**,例如: ```xml ``` ```js methods: { onCreated(editor) { this.editor = Object.seal(editor) console.log('onCreated', editor) }, onChange(editor) { console.log('onChange', editor.children) }, onDestroyed(editor) { console.log('onDestroyed', editor) }, onMaxLength(editor) { console.log('onMaxLength', editor) }, onFocus(editor) { console.log('onFocus', editor) }, onBlur(editor) { console.log('onBlur', editor) }, customAlert(info: string, type: string) { window.alert(`customAlert in Vue demo\n${type}:\n${info}`) }, customPaste(editor, event, callback) { console.log('ClipboardEvent 粘贴事件对象', event) // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本 // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴) // 自定义插入内容 editor.insertText('xxx') // 返回 false ,阻止默认粘贴行为 event.preventDefault() callback(false) // 返回值(注意,vue 事件的返回值,不能用 return) // 返回 true ,继续默认的粘贴行为 // callback(true) }, } ``` ### 调用 API 当编辑器渲染完成之后,通过 `this.editor` 获取 editor 实例,即可调用它的 API 。参考 [编辑器 API](./API.md) 。 ```xml ``` ```js methods: { insertText() { const editor = this.editor // 获取 editor 实例 if (editor == null) return // 调用 editor 属性和 API editor.insertText('一段文字') console.log(editor.children) }, }, ``` ## Vue3 ### Demo - [Demo 源码](https://github.com/wangeditor-next/vue3-wangeditor-demo) - [在线 demo](https://stackblitz.com/edit/vue3-wangeditor-demo-8emmc7) ### 安装 ```sh yarn add @wangeditor-next/editor # 或者 npm install @wangeditor-next/editor --save yarn add @wangeditor-next/editor-for-vue # 或者 npm install @wangeditor-next/editor-for-vue --save ``` ### 使用 模板 ```xml ``` script ```html ``` :::tip - `editorRef` 必须用 `shallowRef` - 组件销毁时,要及时销毁编辑器 ::: ### 配置 可通过 `toolbarConfig` 和 `editorConfig` 来修改菜单栏和编辑器的配置,详细文档参考 - [工具栏配置](./toolbar-config.md) - 插入新菜单,屏蔽某个菜单等 - [编辑器配置](./editor-config.md) - 兼听各个**生命周期**,自定义**粘贴** - [菜单配置](./menu-config.md) - 配置颜色、字体、字号、链接校验、**上传图片、视频**等 【注意】编辑器配置中 `onXxx` 格式的生命周期函数,**必须通过 Vue 事件来传递,不可以放在 `editorConfig` 中**,例如: ```xml ``` ```js const handleCreated = (editor) => { editorRef.value = editor console.log('created', editor) } const handleChange = (editor) => { console.log('change:', editor.children) } const handleDestroyed = (editor) => { console.log('destroyed', editor) } const handleFocus = (editor) => { console.log('focus', editor) } const handleBlur = (editor) => { console.log('blur', editor) } const customAlert = (info, type) => { alert(`【自定义提示】${type} - ${info}`) } const customPaste = (editor, event, callback) => { console.log('ClipboardEvent 粘贴事件对象', event) // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本 // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴) // 自定义插入内容 editor.insertText('xxx') // 返回 false ,阻止默认粘贴行为 event.preventDefault() callback(false) // 返回值(注意,vue 事件的返回值,不能用 return) // 返回 true ,继续默认的粘贴行为 // callback(true) } return { // 省略其他 ... handleCreated, handleChange, handleDestroyed, handleFocus, handleBlur, customAlert, customPaste } ``` ### 调用 API 当编辑器渲染完成之后,通过 `editorRef.value` 获取 editor 实例,即可调用它的 API 。参考 [编辑器 API](./API.md) 。 ```xml ``` ```js const insertText = () => { const editor = editorRef.value // 获取 editor ,必须等待它渲染完之后 if (editor == null) return editor.insertText('hello world') // 执行 editor API } return { // 省略其他 ... insertText } ``` ## React ### Demo - [Demo 源码](https://github.com/wangeditor-next/react-wangeditor-demo) - [在线 demo](https://stackblitz.com/edit/react-4osjqn) ### 安装 ```sh yarn add @wangeditor-next/editor # 或者 npm install @wangeditor-next/editor --save yarn add @wangeditor-next/editor-for-react # 或者 npm install @wangeditor-next/editor-for-react --save ``` ### 使用 ```tsx import '@wangeditor-next/editor/dist/css/style.css' // 引入 css import React, { useState, useEffect } from 'react' import { Editor, Toolbar } from '@wangeditor-next/editor-for-react' import { IDomEditor, IEditorConfig, IToolbarConfig } from '@wangeditor-next/editor' function MyEditor() { // editor 实例 const [editor, setEditor] = useState(null) // TS 语法 // const [editor, setEditor] = useState(null) // JS 语法 // 编辑器内容 const [html, setHtml] = useState('

hello

') // 模拟 ajax 请求,异步设置 html useEffect(() => { setTimeout(() => { setHtml('

hello world

') }, 1500) }, []) // 工具栏配置 const toolbarConfig: Partial = { } // TS 语法 // const toolbarConfig = { } // JS 语法 // 编辑器配置 const editorConfig: Partial = { // TS 语法 // const editorConfig = { // JS 语法 placeholder: '请输入内容...', } // 及时销毁 editor ,重要! useEffect(() => { return () => { if (editor == null) return editor.destroy() setEditor(null) } }, [editor]) return ( <>
setHtml(editor.getHtml())} mode="default" style={{ height: '500px', overflowY: 'hidden' }} />
{html}
) } export default MyEditor ``` ### React 内置 loading(editor-for-react) `@wangeditor-next/editor-for-react` 支持 `loading` 和 `loadingText` 属性,可直接显示编辑器内部遮罩层。 ```tsx const [uploading, setUploading] = useState(false) setHtml(editor.getHtml())} loading={uploading} loadingText="Uploading..." /> ``` 建议优先使用该方式,而不是在外层再套 `Spin/Loader` 改变编辑器 DOM 层级。 ### 配置 可通过 `toolbarConfig` 和 `editorConfig` 来修改菜单栏和编辑器的配置,详细文档参考 - [工具栏配置](./toolbar-config.md) - 插入新菜单,屏蔽某个菜单等 - [编辑器配置](./editor-config.md) - 兼听各个**生命周期**,自定义**粘贴** - [菜单配置](./menu-config.md) - 配置颜色、字体、字号、链接校验、**上传图片、视频**等 ### 调用 API 当编辑器渲染完成之后,即可调用它的 API 。参考 [编辑器 API](./API.md) 。 ```jsx function insertText() { if (editor == null) return editor.insertText('hello') } return ( <>
) ``` --- ## 5. content.md # 内容处理 快速了解可查看[视频教程](./video-course.md)。 ## 获取内容 ### 获取 HTML 和 Text 使用 `editor.getHtml()` 获取 HTML 内容,可参考 [demo](https://wangeditor-next.github.io/demo/get-html.html)。使用 `editor.getText()` 获取纯文本内容。 如果你需要导出带唯一标识属性的 HTML(用于节点追踪/定位),可使用 `editor.getHtmlWithId(idKey?)` : ```js const html = editor.getHtmlWithId() // 默认 data-w-e-id const html2 = editor.getHtmlWithId('data-node-id') // 自定义属性名 ``` 推荐使用 HTML 格式存储数据。 ### 获取 JSON 使用 `editor.children` 获取 JSON 内容。 JSON 格式可以转换为 HTML 和 Text 格式,支持浏览器和 nodejs 。 如果是在 nodejs 中,需要安装 `yarn add jsdom global-jsdom` ,并且引入 `require('global-jsdom/register')`。 ```js const editor = createEditor({ content }) // `content` 即为 JSON 内容 const html = editor.getHtml() const text = editor.getText() ``` ### 自定义样式 编辑器输出或者生成的 HTML 都是**纯标签**,没有内联样式。所以,显示 HTML 时需要你自定义样式。可参考以下示例 - [显示 HTML](https://wangeditor-next.github.io/demo/get-html.html) - [自定义样式](https://wangeditor-next.github.io/demo/css/view.css) 另外,**代码高亮**也需要自行处理,推荐使用 [Prism.js](https://prismjs.com/) ,因为编辑器内容内部也是基于 Prism.js 来实现的。可参考 [demo](https://wangeditor-next.github.io/demo/code-highlight.html)。 ## 设置内容 创建编辑器时,传入的默认内容。即编辑器创建完成后,立马显示这些内容。 ### 设置 HTML 【注意】这里的 HTML 内容必须是 wangEditor 生成的(即 `editor.getHtml()` 返回的) HTML 格式,不可以自己随意写。HTML 格式非常灵活,wangEditor 无法兼容所有的 HTML 格式。 例如,wangEditor 可以识别 `hello` 为加粗,但无法识别 `hello` 等其他加粗方式。 #### 创建时设置 HTML ```js const editor = createEditor({ html: '

hello world

', // 从 editor.getHtml() 获取的 html 内容 // 其他属性... }) ``` #### 动态设置 HTML 参考 [demo](https://wangeditor-next.github.io/demo/set-html.html) ```js editor.setHtml('

hello world

') ``` :::tip 注意,`setHtml` 主要用于回显编辑器输出的 HTML ,即 `editor.getHtml()` 的内容。
如果想插入一段 HTML ,请使用 [dangerouslyInsertHtml](./API.md#dangerouslyinserthtml) ::: ### 设置 Text ```js // 1. 把 text 转换为 html const text = '...' // text 内容 const html = text.split(/\n/).map(line => `

${line}

`).join('\n') // 2. 设置 html const editor = createEditor({ html, // 其他属性... }) // 3. 或,在创建完 editor 之后执行 setHtml // editor.setHtml(html) ``` ### 设置 JSON ```js const editor = createEditor({ content: [...], // editor.children 获取的内容 // 其他属性 }) ``` ### Ajax 异步设置内容 可等待 Ajax 返回之后再创建编辑器。 ```ts // 伪代码 import { IDomEditor } from '@wangeditor-next/editor' let editor: IDomEditor | null = null // TS 语法 // let editor = null // JS 语法 ajax(url, res => { editor = createEditor({ // content 或 html // 其他属性 }) }) ``` ::: tip 其他的内容处理,可参考 [API](./API.md) ::: --- ## 6. toolbar-config.md # 工具栏配置 快速了解可查看[视频教程](./video-course.md)。 :::tip wangEditor 从 V5 版本开始,工具栏配置和[菜单配置](./menu-config.md)(如配置颜色、字体、链接校验、上传图片等)分离了。本文只讲工具栏配置。 ::: ```ts{5} import { IToolbarConfig } from '@wangeditor-next/editor' const toolbarConfig: Partial = { // TS 语法 // const toolbarConfig = { // JS 语法 /* 工具栏配置 */ } // 创建 toolbar ,或者传入 Vue React 组件中 ``` ## getConfig 可通过 `toolbar.getConfig()` 查看工具栏的默认配置。
如果你使用 Vue React ,可以通过如下代码获取 `toolbar` 实例 ```ts import { DomEditor } from '@wangeditor-next/editor' const toolbar = DomEditor.getToolbar(editor) const curToolbarConfig = toolbar.getConfig() console.log(curToolbarConfig.toolbarKeys) // 当前菜单排序和分组 ``` :::tip 在 editor.onCreated 生命周期中 toolbar 实例还没创建好,所以不能在 created 中获取 toolbar 实例。 ::: ## toolbarKeys **重新**配置工具栏,显示哪些菜单,以及菜单的排序、分组。 - `toolbar.getConfig().toolbarKeys` 查看当前的默认配置 - `editor.getAllMenuKeys()` 查询编辑器注册的所有菜单 key (可能有的不在工具栏上) ```ts toolbarConfig.toolbarKeys = [ // 菜单 key 'headerSelect', // 分割线 '|', // 菜单 key 'bold', 'italic', // 菜单组,包含多个菜单 { key: 'group-more-style', // 必填,要以 group 开头 title: '更多样式', // 必填 iconSvg: '....', // 可选 menuKeys: ['through', 'code', 'clearStyle'] // 下级菜单 key ,必填 }, // 继续配置其他菜单... ] ``` ## insertKeys 可以在当前 `toolbarKeys` 的基础上继续插入新菜单,如自定义扩展的菜单。 ```ts toolbarConfig.insertKeys = { index: 5, // 插入的位置,基于当前的 toolbarKeys keys: ['menu-key1', 'menu-key2'] } ``` ## excludeKeys 如果仅仅想排除掉某些菜单,其他都保留,可以使用 `excludeKeys` 来配置。
可通过 `toolbar.getConfig().toolbarKeys` 查看工具栏的默认配置 ```ts toolbarConfig.excludeKeys = [ 'headerSelect', 'italic', 'group-more-style' // 排除菜单组,写菜单组 key 的值即可 ] ``` 如果你想排除某个菜单组,可通过 `toolbar.getConfig().toolbarKeys` 找到这个菜单组的 key 。 ![](/image/exclude-group.png) ## modalAppendToBody 将菜单弹出的 modal 添加到 body 下,并自定义 modal 的定位和其他样式。 ![](/image/modal-appendTo-body.png) ```ts{1} toolbarConfig.modalAppendToBody = true // 创建 toolbar 和 editor // 可监听 `modalOrPanelShow` 和 `modalOrPanelHide` 自定义事件来设置样式、蒙层 editor.on('modalOrPanelShow', modalOrPanel => { if (modalOrPanel.type !== 'modal') return const { $elem } = modalOrPanel // modal element // 设置 modal 样式(定位、z-index) // 显示蒙层 }) editor.on('modalOrPanelHide', () => { // 隐藏蒙层 }) ``` 上述代码细节可以参考 [example 源码](https://github.com/wangeditor-next/wangEditor-next/blob/master/packages/editor/examples/modal-appendTo-body.html) --- ## 7. editor-config.md # 编辑器配置 快速了解可查看[视频教程](./video-course.md)。 ```ts{5} import { IEditorConfig } from '@wangeditor-next/editor' const editorConfig: Partial = { // TS 语法 // const editorConfig = { // JS 语法 /* 编辑器配置 */ } // 创建 editor 或传入 Vue React 组件 ``` :::tip 可通过 `editor.getConfig()` 查看编辑器默认配置 ::: ## placeholder 配置编辑器 placeholder ```ts editorConfig.placeholder = '请输入内容...' ``` ## readOnly 配置编辑器是否只读,默认为 `false` ```ts editorConfig.readOnly = true ``` 只读状态可通过 `editor.enable()` 和 `editor.disable()` 切换,详见 [API](./API.md) 。 ## autoFocus 配置编辑器默认是否 focus ,默认为 `true` ```ts editorConfig.autoFocus = false ``` ## scroll 配置编辑器是否支持滚动,默认为 `true` 。注意,此时**不要固定 `editor-container` 的高度**,设置一个 `min-height` 即可。 ```ts editorConfig.scroll = false ``` :::tip 可将 scroll 设置为 `false` 的情况: - 编辑器高度自增 - 在线文档,如腾讯文档、语雀那样的,参考 [demo](https://wangeditor-next.github.io/demo/like-qq-doc.html) 中的“仿腾讯文档” ::: ## textStyleMode 配置文本/段落样式导出模式。 ```ts editorConfig.textStyleMode = 'class' // 'inline' | 'class' ``` - `inline`(默认):输出内联 `style` - `class`:输出 `class + data-w-e-*`,适合严格 CSP 场景 详细使用与迁移建议见 [CSP class 样式模式](./csp-class-mode.md)。 ## classStylePolicy 当 `textStyleMode = 'class'` 时,配置未注册 token 的处理策略。 ```ts editorConfig.classStylePolicy = 'preserve-data' // 'preserve-data' | 'fallback-inline' | 'strict' ``` - `preserve-data`(默认):保留 `data-w-e-*` - `fallback-inline`:回退到 inline style - `strict`:直接抛错 ## styleClassTokens 扩展 class 模式允许的 token 集合。 ```ts editorConfig.styleClassTokens = { color: ['rgb(1, 2, 3)'], fontSize: ['20px'], } ``` :::tip `styleClassTokens` 只注册 token,不自动注入业务 CSS。 ::: ## onClassStyleUnsupported class 模式遇到未注册 token 时的通知回调。 ```ts editorConfig.onClassStyleUnsupported = payload => { // payload: { type, value, scene, fallback, message } console.warn(payload) } ``` ## maxLength onMaxLength 配置编辑器的 maxlength ,参考 [demo](https://wangeditor-next.github.io/demo/max-length.html)。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.maxLength = 1000 editorConfig.onMaxLength = function (editor: IDomEditor) { // TS 语法 // editorConfig.onMaxLength = function (editor) { // JS 语法 // 当达到 maxlength 限制时,触发该回调函数 } ``` :::tip 无特殊需求,请慎用 maxLength ,这可能会导致编辑器内容过多时,编辑卡顿。 ::: ## hoverbarKeys 配置编辑器的 hoverbar 菜单。通过 `editor.getConfig().hoverbarKeys` 可查看当前的 hoverbarKeys ![](/image/hoverbar.png) :::tip createEditor 时设置 `mode: 'simple'` 可隐藏选中文本时的 hoverbar 。 ::: ### 使用 element type 可以通过元素 `type` 配置某种元素的 hoverbar - 元素的 `type` 可通过 `editor.children` 查看,如下图 - 使用 `editor.getAllMenuKeys()` 可查看所有内置 menu key ![](/image/elem-type.png) ```ts editorConfig.hoverbarKeys = { link: { // 重写 link 元素的 hoverbar menuKeys: ['editLink', 'unLink', 'viewLink'], }, image: { // 清空 image 元素的 hoverbar menuKeys: [], } } ``` ### 自定义 match 函数 如果 element type 无法满足需求,可通过自定义 `match` 函数匹配元素。 ```ts import { SlateNode, IDomEditor } from '@wangeditor-next/editor' editorConfig.hoverbarKeys = { 'text': { // 如有 match 函数,则优先根据 match 判断,而忽略 element type match: (editor: IDomEditor, n: SlateNode) => { // TS 语法 // match: (editor, n) => { // JS 语法 // 可参考下文的源码 }, menuKeys: [ ... ], // 定义你想要的 menu keys } } ``` 可参考 hoverbar 配置的[源码](https://github.com/wangeditor-next/wangEditor-next/blob/master/packages/editor/src/init-default-config/config/hoverbar.ts)。 ## onCreated 编辑器创建完毕时的回调函数。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.onCreated = (editor: IDomEditor) => { // TS 语法 // editorConfig.onCreated = (editor) => { // JS 语法 // editor created } ``` ## onChange 编辑器内容、选区变化时的回调函数。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.onChange = (editor: IDomEditor) => { // TS 语法 // editorConfig.onChange = (editor) => { // JS 语法 // editor changed console.log('content', editor.children) } ``` ## onDestroyed 编辑器销毁时的回调函数。调用 `editor.destroy()` 即可销毁编辑器,详见 [API](./API.md) 。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.onDestroyed = (editor: IDomEditor) => { // TS 语法 // editorConfig.onDestroyed = (editor) => { // JS 语法 // editor destroyed } ``` ## onFocus 编辑器 focus 时的回调函数。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.onFocus = (editor: IDomEditor) => { // TS 语法 // editorConfig.onFocus = (editor) => { // JS 语法 // editor focused } ``` ## onBlur 编辑器 blur 时的回调函数。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.onBlur = (editor: IDomEditor) => { // TS 语法 // editorConfig.onBlur = (editor) => { // JS 语法 // editor blur } ``` ## customPaste 自定义粘贴。可阻止编辑器的默认粘贴,实现自己的粘贴逻辑。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.customPaste = (editor: IDomEditor, event: ClipboardEvent): boolean => { // TS 语法 // editorConfig.customPaste = (editor, event) => { // JS 语法 // event 是 ClipboardEvent 类型,可以拿到粘贴的数据 // 可参考 https://developer.mozilla.org/zh-CN/docs/Web/API/ClipboardEvent // const html = event.clipboardData.getData('text/html') // 获取粘贴的 html // const text = event.clipboardData.getData('text/plain') // 获取粘贴的纯文本 // const rtf = event.clipboardData.getData('text/rtf') // 获取 rtf 数据(如从 word wsp 复制粘贴) // 同步 editor.insertText('xxx') // 异步 setTimeout(() => { editor.insertText('yy') }, 1000) // 阻止默认的粘贴行为 event.preventDefault() return false // 继续执行默认的粘贴行为 // return true } ``` ## customCopy 自定义复制,可修改编辑器的复制结果。 ```ts import { IDomEditor } from '@wangeditor-next/editor' editorConfig.customCopy = (editor: IDomEditor, event: ClipboardEvent): void => { // TS 语法 // editorConfig.customCooy = (editor, event) => { // JS 语法 const originalText = event.clipboardData.getData('text/plain'); const originalHtml = event.clipboardData.getData('text/html'); // 修改或扩展内容 const modifiedText = `${originalText}\n---\n添加的文本`; const modifiedHtml = `${originalHtml}

添加的HTML内容

`; // 将修改后的内容写回剪贴板 event.clipboardData.setData('text/plain', modifiedText); event.clipboardData.setData('text/html', modifiedHtml); } ``` ## customAlert 自定义编辑器 alert 。如想用 antd 的 message 功能。 ```ts import { message } from 'antd' editorConfig.customAlert = (s: string, t: string) => { // TS 语法 // editorConfig.customAlert = (s, t) => { // JS 语法 switch (t) { case 'success': message.success(s) break case 'info': message.info(s) break case 'warning': message.warning(s) break case 'error': message.error(s) break default: message.info(s) break } } ``` ## EXTEND_CONF 用于第三方插件做扩展配置,如 [mention 插件](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-mention)。 --- ## 8. csp-class-mode.md # CSP class 样式模式 在严格 CSP(例如禁止内联 `style`)场景下,可使用 `textStyleMode: 'class'` 让样式以 `class + data-w-e-*` 输出。 :::tip 默认模式仍是 `inline`,老项目无需改动即可继续使用。 ::: ## 快速开始 ```ts import { createEditor, IEditorConfig } from '@wangeditor-next/editor' const editorConfig: Partial = { textStyleMode: 'class', classStylePolicy: 'preserve-data', styleClassTokens: { color: ['rgb(1, 2, 3)'], }, onClassStyleUnsupported(payload) { console.warn('[class-style-unsupported]', payload) }, } const editor = createEditor({ selector: '#editor-container', config: editorConfig, html: '


', }) ``` 同时请确保引入编辑器样式文件(内置默认 token 的 class 样式): ```html ``` ## 配置项说明 ### `textStyleMode` - `inline`(默认):样式输出到 `style`。 - `class`:样式输出到 `class + data-w-e-*`。 ### `classStylePolicy` 仅在 `textStyleMode: 'class'` 下生效。 - `preserve-data`(默认):仅保留 `data-w-e-*`,不输出 class/inline(可回读,可能不展示)。 - `fallback-inline`:保留 `data-w-e-*`,并回退到内联样式(优先展示)。 - `strict`:遇到未注册 token 直接抛错(避免静默降级)。 ### `styleClassTokens` - 用于注册额外可接受的 token。 - 仅注册 token,不会自动注入你的业务样式。 ### `onClassStyleUnsupported` 当遇到未注册 token 时回调,便于日志与监控。 回调 payload 包含: - `type` - `value` - `scene`(`render` 或 `toHtml`) - `fallback`(`preserve-data` / `inline` / `throw`) - `message` ## 支持的文本样式类型 - `color` - `bgColor` - `fontSize` - `fontFamily` - `textAlign` - `lineHeight` - `indent` ## 自定义 token 的 CSS 约定 推荐优先基于 `data-w-e-*` 写规则,不依赖 hash class 名: ```css [data-w-e-color="rgb(1, 2, 3)"] { color: rgb(1, 2, 3); } [data-w-e-font-size="20px"] { font-size: 20px; } [data-w-e-line-height="2"] { line-height: 2; } ``` ## 模块行为说明 - `basic-modules`:文本样式按策略输出 class/data/inline。 - `list-module`:列表颜色 class 使用 `w-e-list-color-*`,并保留 `data-w-e-color`。 - `table-module`:`border-style` 在 class 模式下按策略处理(支持单值 class,复杂值按策略降级)。 - `video-module`、`image`、`plugin-float-image`:对齐、尺寸等优先走 class/data 输出。 ## 迁移建议 1. 先在测试环境启用 `textStyleMode: 'class'` + `classStylePolicy: 'preserve-data'`。 2. 观察 `onClassStyleUnsupported` 日志,补齐 `styleClassTokens` 与 CSS。 3. 再按业务要求切到 `fallback-inline` 或 `strict`。 --- ## 9. menu-config.md # 菜单配置 快速了解可查看[视频教程](./video-course.md)。 本文是各个菜单项的详细配置。如想要自定义工具栏的菜单(隐藏某些菜单、排序、分组等),请参考[工具栏配置](./toolbar-config.md)。 ## 通用方法 ### 确定 menu key 要配置哪个菜单,首先要知道这个菜单的 key 。执行 `editor.getAllMenuKeys()` 可获取编辑器所有菜单,从中找到自己想要的菜单 key 即可。 ### 获取菜单的默认配置 找到菜单 key 之后,可以先看看菜单的当前配置,再自行修改。 ```ts editor.getMenuConfig('uploadImage') // 获取 uploadImage 的当前配置 ``` ### 修改配置 ```ts import { IEditorConfig } from '@wangeditor-next/editor' // 初始化 MENU_CONF 属性 const editorConfig: Partial = { // TS 语法 // const editorConfig = { // JS 语法 MENU_CONF: {} // 其他属性... } // 修改 uploadImage 菜单配置 editorConfig.MENU_CONF['uploadImage'] = { server: '/api/upload-image', fieldName: 'custom-field-name' // 继续写其他配置... //【注意】不需要修改的不用写,wangEditor 会去 merge 当前其他配置 } // 修改 otherMenuKey 菜单配置 editorConfig.MENU_CONF['otherMenuKey'] = { // 配置 } // 创建 editor 或传入 Vue React 组件 ``` ### 修改默认配置 #### 修改默认字体、字号、行高 ```ts const jsonContent = [ { type: 'paragraph', lineHeight: '1.5', children: [ { text: 'hello world', fontFamily: '黑体', fontSize: '32px' } ] }, ] ``` Vue React Editor组件有 defaultContent 属性,可传入上述 jsonContent HTML 格式 ```ts const htmlContent = '

hello world

' ``` Vue Editor件可以使用 v-model 属性传入 HTML 内容,React Editor组件可以使用 value 属性传入 HTML 内容使用 - [Vue修改默认字体字号行高](https://codesandbox.io/p/sandbox/vue2-wangeditor-demo-forked-67fh5s) - [React修改默认字体字号行高](https://codesandbox.io/p/sandbox/react-wangeditor-defaultfont-59c48n) #### 修改默认图标 在 menu conf 中对对应的 toolbar 和 hoverbar key 加上 iconSvg 属性,填入对应的 svg 字符串 ~~~JavaScript MEEN_CONF = { // toolbar 文字颜色 key color: { iconSvg: '' , }, // hoverbar 图片宽度 key imageWidth30: { iconSvg: '', }, } ~~~ ## 颜色 ```ts // 文字颜色 editorConfig.MENU_CONF['color'] = { colors: ['#000', '#333', '#666'] } // 背景色 editorConfig.MENU_CONF['bgColor'] = { colors: ['#000', '#333', '#666'] } ``` ## 字号 ```ts editorConfig.MENU_CONF['fontSize'] = { fontSizeList: [ // 元素支持两种形式 // 1. 字符串; // 2. { name: 'xxx', value: 'xxx' } '12px', '16px', { name: '24px', value: '24px' }, '40px', ] } ``` ## 字体 :::tip 请注意,某些字体不能商用。具体请自行查找。 ::: ```ts editorConfig.MENU_CONF['fontFamily'] = { fontFamilyList: [ // 元素支持两种形式 // 1. 字符串; // 2. { name: 'xxx', value: 'xxx' } '黑体', '楷体', { name: '仿宋', value: '仿宋' }, 'Arial', 'Tahoma', 'Verdana' ] } ``` ## 行高 ```ts editorConfig.MENU_CONF['lineHeight'] = { lineHeightList: ['1', '1.5', '2', '2.5'] } ``` ## 表情 ```ts editorConfig.MENU_CONF['emotion'] = { emotions: '😀 😃 😄 😁 😆 😅 😂 🤣 😊 😇 🙂 🙃 😉'.split(' ') // 数组 } ``` ## 链接 - `checkLink` 校验链接 - `parseLinkUrl` 转换链接 url ```ts // 自定义校验链接 function customCheckLinkFn(text: string, url: string): string | boolean | undefined { // TS 语法 // function customCheckLinkFn(text, url) { // JS 语法 if (!url) { return } if (url.indexOf('http') !== 0) { return '链接必须以 http/https 开头' } return true // 返回值有三种选择: // 1. 返回 true ,说明检查通过,编辑器将正常插入链接 // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息 } // 自定义转换链接 url function customParseLinkUrl(url: string): string { // TS 语法 // function customParseLinkUrl(url) { // JS 语法 if (url.indexOf('http') !== 0) { return `http://${url}` } return url } // 插入链接 editorConfig.MENU_CONF['insertLink'] = { checkLink: customCheckLinkFn, // 也支持 async 函数 parseLinkUrl: customParseLinkUrl, // 也支持 async 函数 } // 更新链接 editorConfig.MENU_CONF['editLink'] = { checkLink: customCheckLinkFn, // 也支持 async 函数 parseLinkUrl: customParseLinkUrl, // 也支持 async 函数 } ``` ## 图片 如果用于 Typescript ,需定义图片元素类型。可单独放在 `.d.ts` 中定义。 ```ts import { SlateElement } from '@wangeditor-next/editor' type ImageElement = SlateElement & { src: string alt: string url: string href: string } ``` 图片菜单的配置 - `onInsertedImage` 插入图片之后的回调 - `onUpdatedImage` 更新图片之后的回调 - `checkImage` 校验图片链接 - `parseImageSrc` 转换图片链接 ```ts // 自定义校验图片 function customCheckImageFn(src: string, alt: string, url: string): boolean | undefined | string { // TS 语法 // function customCheckImageFn(src, alt, url) { // JS 语法 if (!src) { return } if (src.indexOf('http') !== 0) { return '图片网址必须以 http/https 开头' } return true // 返回值有三种选择: // 1. 返回 true ,说明检查通过,编辑器将正常插入图片 // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息 } // 转换图片链接 function customParseImageSrc(src: string): string { // TS 语法 // function customParseImageSrc(src) { // JS 语法 if (src.indexOf('http') !== 0) { return `http://${src}` } return src } // 插入图片 editorConfig.MENU_CONF['insertImage'] = { onInsertedImage(imageNode: ImageElement | null) { // TS 语法 // onInsertedImage(imageNode) { // JS 语法 if (imageNode == null) return const { src, alt, url, href } = imageNode console.log('inserted image', src, alt, url, href) }, checkImage: customCheckImageFn, // 也支持 async 函数 parseImageSrc: customParseImageSrc, // 也支持 async 函数 } // 编辑图片 editorConfig.MENU_CONF['editImage'] = { onUpdatedImage(imageNode: ImageElement | null) { // TS 语法 // onUpdatedImage(imageNode) { // JS 语法 if (imageNode == null) return const { src, alt, url } = imageNode console.log('updated image', src, alt, url) }, checkImage: customCheckImageFn, // 也支持 async 函数 parseImageSrc: customParseImageSrc, // 也支持 async 函数 } ``` ## 上传图片 上传图片的配置比较复杂,拆分为几个部分来讲解。可参考这个 [demo](https://github.com/wangeditor-next/server)。 ```ts{2} editorConfig.MENU_CONF['uploadImage'] = { // 上传图片的配置 } ``` ### 服务端地址 使用内置上传器时 **必填**,否则上传图片会报错。 如果你使用 `customUpload` 或 `uploadAdapter` ,则不需要配置 `server` 。 ```ts editorConfig.MENU_CONF['uploadImage'] = { server: '/api/upload', } ``` **【特别注意】服务端 response body 格式要求如下:**
上传成功的返回格式: ```ts { "errno": 0, // 注意:值是数字,不能是字符串 "data": { "url": "xxx", // 图片 src ,必须 "alt": "yyy", // 图片描述文字,非必须 "href": "zzz" // 图片的链接,非必须 } } ``` 上传失败的返回格式: ```ts { "errno": 1, // 只要不等于 0 就行 "message": "失败信息" } ``` :::tip 如果你的服务端 response body 无法按照上述格式,可以使用下文的 `customInsert` ::: ### 基本配置 ```ts editorConfig.MENU_CONF['uploadImage'] = { // form-data fieldName ,默认值 'wangeditor-uploaded-image' fieldName: 'your-custom-name', // 单个文件的最大体积限制,默认为 2M maxFileSize: 1 * 1024 * 1024, // 1M // 最多可上传几个文件,默认为 100 maxNumberOfFiles: 10, // 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] allowedFileTypes: ['image/*'], // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。 meta: { token: 'xxx', otherKey: 'yyy' }, // 将 meta 拼接到 url 参数中,默认 false metaWithUrl: false, // 自定义增加 http header headers: { Accept: 'text/x-json', otherKey: 'xxx' }, // 跨域是否传递 cookie ,默认为 false withCredentials: true, // 超时时间,默认为 10 秒 timeout: 5 * 1000, // 5 秒 } ``` ### 回调函数 ```ts editorConfig.MENU_CONF['uploadImage'] = { // 上传之前触发 onBeforeUpload(file: File) { // TS 语法 // onBeforeUpload(file) { // JS 语法 // file 选中的文件,格式如 { key: file } return file // 可以 return // 1. return file 或者 new 一个 file ,接下来将上传 // 2. return false ,不上传这个 file }, // 上传进度的回调函数 onProgress(progress: number) { // TS 语法 // onProgress(progress) { // JS 语法 // progress 是 0-100 的数字 console.log('progress', progress) }, // 单个文件上传成功之后 onSuccess(file: File, res: any) { // TS 语法 // onSuccess(file, res) { // JS 语法 console.log(`${file.name} 上传成功`, res) }, // 单个文件上传失败 onFailed(file: File, res: any) { // TS 语法 // onFailed(file, res) { // JS 语法 console.log(`${file.name} 上传失败`, res) }, // 上传错误,或者触发 timeout 超时 onError(file: File, err: any, res: any) { // TS 语法 // onError(file, err, res) { // JS 语法 console.log(`${file.name} 上传出错`, err, res) }, } ``` ### 自定义功能 如果用于 Typescript ,则要定义插入函数的类型。 ```ts type InsertFnType = (url: string, alt: string, href: string) => void ``` #### 自定义插入 如果你的服务端 response body 无法按照上文规定的格式,则无法插入图片,提示失败。
但你可以使用 `customInsert` 来自定义插入图片。 ```ts editorConfig.MENU_CONF['uploadImage'] = { // 自定义插入图片 customInsert(res: any, insertFn: InsertFnType) { // TS 语法 // customInsert(res, insertFn) { // JS 语法 // res 即服务端的返回结果 // 从 res 中找到 url alt href ,然后插入图片 insertFn(url, alt, href) }, } ``` #### 自定义上传 如果你不想使用 wangEditor 自带的上传功能,例如你要上传到阿里云 OSS 。
可以通过 `customUpload` 来自定义上传。 ```ts editorConfig.MENU_CONF['uploadImage'] = { // 自定义上传 async customUpload(file: File, insertFn: InsertFnType) { // TS 语法 // async customUpload(file, insertFn) { // JS 语法 // file 即选中的文件 // 自己实现上传,并得到图片 url alt href // 最后插入图片 insertFn(url, alt, href) } } ``` #### 自定义上传适配器 如果你想替换底层上传实现,但仍然复用 wangEditor 内置的上传回调和插入链路,可以使用 `uploadAdapter` 。 - `customUpload` 表示你完全接管上传和插入 - `uploadAdapter` 表示你只替换上传器实现,编辑器仍然复用现有的进度、成功、失败、错误和插入流程 - 如果同时配置了 `customUpload` 和 `uploadAdapter` ,则优先使用 `customUpload` ```ts editorConfig.MENU_CONF['uploadImage'] = { uploadAdapter({ config, editor }) { const files: File[] = [] return { addFiles(fileList) { files.push(...fileList.map(item => item.data as File)) }, async upload() { for (const file of files) { const fileInfo = { name: file.name, type: file.type, size: file.size, } try { // 例如:上传到 OSS / S3 / 自定义服务 const res = await myUpload(file, editor) config.onProgress?.(100) config.onSuccess(fileInfo, { errno: 0, data: { url: res.url, alt: file.name, href: res.url, }, }) } catch (err) { config.onError(fileInfo, err, null) } } }, } }, } ``` :::tip `uploadAdapter` 需要在合适的时机主动调用 `config.onProgress`、`config.onSuccess`、`config.onError` 等回调。 如果你想复用编辑器默认插入逻辑,请给 `config.onSuccess` 传入和内置上传一致的 response body;如果你的返回格式不同,可以配合 `customInsert` 一起使用。 ::: #### 自定义选择图片 如果你不想使用 wangEditor 自带的选择文件功能,例如你有自己的图床,或者图片选择器。
可以通过 `customBrowseAndUpload` 来自己实现选择图片、上传图片,并插入图片。 ```ts editorConfig.MENU_CONF['uploadImage'] = { // 自定义选择图片 customBrowseAndUpload(insertFn: InsertFnType) { // TS 语法 // customBrowseAndUpload(insertFn) { // JS 语法 // 自己选择文件 // 自己上传文件,并得到图片 url alt href // 最后插入图片 insertFn(url, alt, href) } } ``` ### base64 插入图片 ```ts editorConfig.MENU_CONF['uploadImage'] = { // 其他配置... // 小于该值就插入 base64 格式(而不上传),默认为 0 base64LimitSize: 5 * 1024 // 5kb } ``` ### 获取已删除的图片 这是一个常见的需求。
上传图片到编辑器,然后又把图片删除了。此时你可能想要拿到这张删除的图片,在服务器也把图片文件删了。 - 使用 [onInsertedImage](./menu-config.md#图片) 来收集所有上传或者插入的图片,记录为 `imageList1` - 最后保存编辑器内容之前,使用 `editor.getElemsByType('image')` 获取当前编辑器的所有图片,记录为 `imageList2` - 对比 `imageList1` 和 `imageList2` ,两者的差异,就是删除过的图片 可能会有疑问:为何要在最后去对比?我想要在图片删除时就及时得到反馈。
但,这样是不行的,因为图片删除了,还可能会被**撤销**回来。所以,一定要在最后去操作。 ## 视频 如果用于 Typescript ,需定义视频元素类型。可单独放在 `.d.ts` 中定义。 ```ts import { SlateElement } from '@wangeditor-next/editor' type VideoElement = SlateElement & { src: string poster?: string } ``` 菜单配置 - `onInsertedVideo` 插入视频之后的回调 - `checkVideo` 校验视频链接 - `parseVideoSrc` 转换视频链接 ```ts // 自定义校验视频 function customCheckVideoFn(src: string, poster: string): boolean | string | undefined { // TS 语法 // function customCheckVideoFn(src, poster) { // JS 语法 if (!src) { return } if (src.indexOf('http') !== 0) { return '视频地址必须以 http/https 开头' } return true // 返回值有三种选择: // 1. 返回 true ,说明检查通过,编辑器将正常插入视频 // 2. 返回一个字符串,说明检查未通过,编辑器会阻止插入。会 alert 出错误信息(即返回的字符串) // 3. 返回 undefined(即没有任何返回),说明检查未通过,编辑器会阻止插入。但不会提示任何信息 } // 自定义转换视频 function customParseVideoSrc(src: string): string { // TS 语法 // function customParseVideoSrc(src) { // JS 语法 if (src.includes('.bilibili.com')) { // 转换 bilibili url 为 iframe (仅作为示例,不保证代码正确和完整) const arr = location.pathname.split('/') const vid = arr[arr.length - 1] return `` } return src } editorConfig.MENU_CONF['insertVideo'] = { onInsertedVideo(videoNode: VideoElement | null) { // TS 语法 // onInsertedVideo(videoNode) { // JS 语法 if (videoNode == null) return const { src } = videoNode console.log('inserted video', src) }, checkVideo: customCheckVideoFn, // 也支持 async 函数 parseVideoSrc: customParseVideoSrc, // 也支持 async 函数 } ``` ## 上传视频 上传视频的配置比较复杂,拆分为几个部分来讲解。可参考这个 [demo](https://github.com/wangeditor-next/server)。 ```ts{2} editorConfig.MENU_CONF['uploadVideo'] = { // 上传视频的配置 } ``` ### 服务端地址 使用内置上传器时 **必填**,否则上传视频会报错。 如果你使用 `customUpload` 或 `uploadAdapter` ,则不需要配置 `server` 。 ```ts editorConfig.MENU_CONF['uploadVideo'] = { server: '/api/upload', } ``` **【特别注意】服务端 response body 格式要求如下:**
上传成功的返回格式: ```json { "errno": 0, // 注意:值是数字,不能是字符串 "data": { "url": "xxx", // 视频 src ,必须 "poster": "xxx.png" // 视频封面图片 url ,可选 } } // 注意:@wangeditor-next/editor 版本 >= 5.1.8 才支持 video poster ``` 上传失败的返回格式: ```json { "errno": 1, // 只要不等于 0 就行 "message": "失败信息" } ``` :::tip 如果你的服务端 response body 无法按照上述格式,可以使用下文的 `customInsert` ::: ### 基本配置 ```ts editorConfig.MENU_CONF['uploadVideo'] = { // form-data fieldName ,默认值 'wangeditor-uploaded-video' fieldName: 'your-custom-name', // 单个文件的最大体积限制,默认为 10M maxFileSize: 5 * 1024 * 1024, // 5M // 最多可上传几个文件,默认为 5 maxNumberOfFiles: 3, // 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 [] allowedFileTypes: ['video/*'], // 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。 meta: { token: 'xxx', otherKey: 'yyy' }, // 将 meta 拼接到 url 参数中,默认 false metaWithUrl: false, // 自定义增加 http header headers: { Accept: 'text/x-json', otherKey: 'xxx' }, // 跨域是否传递 cookie ,默认为 false withCredentials: true, // 超时时间,默认为 30 秒 timeout: 15 * 1000, // 15 秒 // 视频不支持 base64 格式插入 } ``` ### 回调函数 ```ts editorConfig.MENU_CONF['uploadVideo'] = { // 上传之前触发 onBeforeUpload(file: File) { // TS 语法 // onBeforeUpload(file) { // JS 语法 // file 选中的文件,格式如 { key: file } return file // 可以 return // 1. return file 或者 new 一个 file ,接下来将上传 // 2. return false ,不上传这个 file }, // 上传进度的回调函数 onProgress(progress: number) { // TS 语法 // onProgress(progress) { // JS 语法 // progress 是 0-100 的数字 console.log('progress', progress) }, // 单个文件上传成功之后 onSuccess(file: File, res: any) { // TS 语法 // onSuccess(file, res) { // JS 语法 console.log(`${file.name} 上传成功`, res) }, // 单个文件上传失败 onFailed(file: File, res: any) { // TS 语法 // onFailed(file, res) { // JS 语法 console.log(`${file.name} 上传失败`, res) }, // 上传错误,或者触发 timeout 超时 onError(file: File, err: any, res: any) { // TS 语法 // onError(file, err, res) { // JS 语法 console.log(`${file.name} 上传出错`, err, res) }, } ``` ### 自定义功能 如果用于 Typescript ,则要定义插入函数的类型。 ```ts type InsertFnType = (url: string, poster: string = '') => void ``` #### 自定义插入 如果你的服务端 response body 无法按照上文规定的格式,则无法插入视频,提示失败。
但你可以使用 `customInsert` 来自定义插入视频。 ```ts editorConfig.MENU_CONF['uploadVideo'] = { // 自定义插入视频 customInsert(res: any, insertFn: InsertFnType) { // TS 语法 // customInsert(res, insertFn) { // JS 语法 // res 即服务端的返回结果 // 从 res 中找到 url poster ,然后插入视频 insertFn(url, poster) }, } ``` #### 自定义上传 如果你不想使用 wangEditor 自带的上传功能,例如你要上传到阿里云 OSS 。
可以通过 `customUpload` 来自定义上传。 ```ts editorConfig.MENU_CONF['uploadVideo'] = { // 自定义上传 async customUpload(file: File, insertFn: InsertFnType) { // TS 语法 // async customUpload(file, insertFn) { // JS 语法 // file 即选中的文件 // 自己实现上传,并得到视频 url poster // 最后插入视频 insertFn(url, poster) } } ``` #### 自定义上传适配器 如果你想替换底层上传实现,但仍然复用 wangEditor 内置的上传回调和插入链路,可以使用 `uploadAdapter` 。 - `customUpload` 表示你完全接管上传和插入 - `uploadAdapter` 表示你只替换上传器实现,编辑器仍然复用现有的进度、成功、失败、错误和插入流程 - 如果同时配置了 `customUpload` 和 `uploadAdapter` ,则优先使用 `customUpload` ```ts editorConfig.MENU_CONF['uploadVideo'] = { uploadAdapter({ config, editor }) { const files: File[] = [] return { addFiles(fileList) { files.push(...fileList.map(item => item.data as File)) }, async upload() { for (const file of files) { const fileInfo = { name: file.name, type: file.type, size: file.size, } try { const res = await myUpload(file, editor) config.onProgress?.(100) config.onSuccess(fileInfo, { errno: 0, data: { url: res.url, poster: res.poster || "", }, }) } catch (err) { config.onError(fileInfo, err, null) } } }, } }, } ``` :::tip `uploadAdapter` 需要在合适的时机主动调用 `config.onProgress`、`config.onSuccess`、`config.onError` 等回调。 如果你想复用编辑器默认插入逻辑,请给 `config.onSuccess` 传入和内置上传一致的 response body;如果你的返回格式不同,可以配合 `customInsert` 一起使用。 ::: #### 自定义选择视频 如果你不想使用 wangEditor 自带的选择文件功能,例如你有自己的图床,或者视频文件选择器。
可以通过 `customBrowseAndUpload` 来自己实现选择视频、上传视频,并插入视频。 ```ts editorConfig.MENU_CONF['uploadVideo'] = { // 自定义选择视频 customBrowseAndUpload(insertFn: InsertFnType) { // TS 语法 // customBrowseAndUpload(insertFn) { // JS 语法 // 自己选择文件 // 自己上传文件,并得到视频 url poster // 最后插入视频 insertFn(url, poster) } } ``` ## 表格 - `minWidth` 单元格最小宽度 - `tableHeader` 表头 - `tableFullWidth` 表格宽度自适应 - `widthExportMode` 表格宽度导出策略(`explicit` | `adaptive`,默认 `explicit`) - `insertTableCol.insertPosition` 插入列位置(`before` | `after`,默认 `before`) `tableFullWidth` 点击后会切换为 `width: 100%` 的响应式模式。后续容器宽度变化时,表格会自动跟随,无需再次点击。 ```ts editorConfig.MENU_CONF['insertTable'] = { minWidth: 60, // 表头 tableHeader: { selected: false, // 默认不启用表头 }, // 表格宽度自适应 tableFullWidth: { selected: true, // 默认启用表格宽度自适应 }, // 表格宽度导出策略 // explicit: 兼容历史行为,优先导出固定像素宽 // adaptive: 保留 width:auto(适合不希望自动固化列宽的场景) widthExportMode: 'explicit', } ``` 如果你希望 `setHtml` 导入 `width:auto` 的表格后,`getHtml` 仍然保持 `width:auto`,可以开启 `adaptive`: ```ts editorConfig.MENU_CONF['insertTable'] = { widthExportMode: 'adaptive', } ``` ```ts editorConfig.MENU_CONF['insertTableCol'] = { // 'before' 表示在当前列前插入(默认) // 'after' 表示在当前列后插入 insertPosition: 'after', } ``` ## 代码高亮 - `codeLangs` 配置代码语言 - `selected: true` 配置代码块默认语言 **(可选)** ```ts editorConfig.MENU_CONF['codeSelectLang'] = { // 代码语言 codeLangs: [ { text: 'CSS', value: 'css', selected: true }, { text: 'HTML', value: 'html' }, { text: 'XML', value: 'xml' }, // 其他 ] } ``` :::tip 配置代码语言时,只能从 `editor.getMenuConfig('codeSelectLang').codeLangs` 中选择,不能自己随意增加。 如有其他语言的需要,可以给我们提交 issue ,这需要修改源码。 ::: ## 代码块 - `showCopyButton` 是否显示代码块复制按钮(默认 `false`) ```ts editorConfig.MENU_CONF['codeBlock'] = { showCopyButton: true, } ``` ## 其他 其他菜单的配置,请参考上文的 [通用方法](#通用方法) 自行修改。 --- ## 10. API.md # 编辑器 API ## config 相关 ### getConfig 获取编辑器所有配置 ```ts editor.getConfig() ``` ### getMenuConfig 获取单个 menu 的配置。menu 配置相关的可参考[这里](./menu-config.md)。 ```ts editor.getMenuConfig(menuKey) ``` ### getAllMenuKeys 获取编辑器所有 menu 的 key ```ts editor.getAllMenuKeys() ``` ### alert 编辑器 alert ,可通过 [customAlert](./editor-config.md#customalert) 配置。 ```ts editor.alert('错误信息', 'error') ``` ## 内容处理 ### handleTab 控制编辑器按 tab 键时,输入什么。默认如下 ```ts editor.handleTab = () => editor.insertText(' ') ``` ### getHtml `editor.getHtml()` 获取**非格式化**的 html ```html

head

hello word

``` 你可以自行格式化 html ,如使用 [xml-formatter](https://www.npmjs.com/package/xml-formatter) ### getHtmlWithId `editor.getHtmlWithId(idKey?)` 获取带唯一标识属性的 HTML。 ```ts const html = editor.getHtmlWithId() // 默认属性名为 data-w-e-id const html2 = editor.getHtmlWithId('data-node-id') // 自定义属性名 ``` 适用于需要在服务端或展示层做“节点追踪 / 精确定位”的场景。默认不影响 `editor.getHtml()` 的输出。 ### getText 获取当前编辑器的纯文本内容 ```ts const text = editor.getText() ``` ### setHtml 重置编辑器的 HTML 内容。【注意】只能解析 `editor.getHtml()` 返回的 HTML 格式,不支持自定义 HTML 格式。 ```rs editor.setHtml('

hello

') ``` 如果想插入一段 HTML ,请使用 [dangerouslyInsertHtml](#dangerouslyinserthtml) ### isEmpty 判断当前编辑器内容是否为空(只有一个空段落) ```ts editor.isEmpty() ``` :::tip 该方法只能识别**只有一个空段落**情况,其他情况(如有一个空标题、空表格)请使用 `editor.getText()` 来判断。 ::: ### getSelectionText 获取选中的文本 ```ts editor.getSelectionText() ``` ### getElemsByType 通过 type 获取编辑器的 element 列表。 ```ts editor.getElemsByType('image') // 所有图片 editor.getElemsByType('link') // 所有链接 // 其他 ``` ### getElemsByTypePrefix 通过 type 前缀获取编辑器的 element 列表。 ```ts editor.getElemsByTypePrefix('header') // 获取所有标题 header1 header2 header3... // 其他 ``` ### deleteBackward 向后删除,相当于按 backspace 键。 ```ts editor.deleteBackward() ``` ### deleteForward 向后删除,相当于按 delete 键(部分键盘没有这个键) ```ts editor.deleteForward() ``` ### deleteFragment 删除选中的内容 ```ts editor.deleteFragment() ``` ### getFragment 获取选中的内容,json 格式 ```ts editor.getFragment() ``` ### insertBreak 在选区回车换行 ```ts editor.insertBreak() ``` ### insertText 在选区插入文本 ```ts editor.insertText('xxx') ``` ### dangerouslyInsertHtml - 如果是 `editor.getHtml()` 获取的 HTML 格式,可以完美解析。 - 如果是其他的 HTML 格式,则不能保证语义正确 —— **dangerously** 。 ```ts editor.dangerouslyInsertHtml(`

标题

文本 加粗

`) ``` :::tip 如果你想**重置**编辑器 HTML 内容,请使用 [setHtml](#sethtml) ::: ### clear 清空编辑器内容 ```ts editor.clear() ``` ### undo 撤销 ```ts editor.undo() ``` ### redo 重做 ```ts editor.redo() ``` ### clear history 清空 undo / redo 历史栈。 ```ts editor.clearHistory() ``` 常见用法是在 `editor.setHtml(...)` 或数据回显完成后执行一次,避免用户撤销回旧内容。 ### history without merging 如果你需要让某次程序化插入单独成为一条历史记录,而不是和前一次操作合并,可以直接使用 `slate-history` 的 `HistoryEditor.withoutMerging(...)`。 ```ts import { HistoryEditor } from 'slate-history' HistoryEditor.withoutMerging(editor as HistoryEditor, () => { editor.insertText('plain text') }) ``` 这类用法常见于“粘贴 HTML 后,用户选择转成纯文本再插入”的场景。因为编辑器实例运行时已经经过 `slate-history` 扩展,所以不需要额外等待 wangEditor 再封装一个新 API。 ## 节点操作 使用节点操作 API 前,请查看 [节点数据结构](./node-define.md) 。 ### insertNode 在选区插入一个节点 ```ts const node = { type: 'paragraph', children: [{ text: 'simple text' }] } editor.insertNode(node) ``` ### insertNodes 在选区插入多个节点 ```ts import { SlateTransforms } from '@wangeditor-next/editor' const node1 = { type: 'paragraph', children: [{ text: 'aaa' }] } const node2 = { type: 'paragraph', children: [{ text: 'bbb' }] } const nodeList = [node1, node2] SlateTransforms.insertNodes(editor, nodeList) ``` ### removeNodes 删除选区所在的节点 ```ts import { SlateTransforms } from '@wangeditor-next/editor' SlateTransforms.removeNodes(editor) ``` ### 获取选中节点 可使用 `SlateEditor.nodes` 获取选中的节点。详情可参考 [Slate.js](https://docs.slatejs.org/) 中的 `Editor.nodes` API 。 ```ts import { SlateEditor, SlateElement, SlateNode } from '@wangeditor-next/editor' const nodeEntries = SlateEditor.nodes(editor, { match: (node: SlateNode) => { // TS syntax // match: (node) => { // JS syntax if (SlateElement.isElement(node)) { if (node.type === 'paragraph') { return true // 匹配 paragraph } } return false }, universal: true, }) if (nodeEntries == null) { console.log('当前未选中的 paragraph') } else { for (let nodeEntry of nodeEntries) { const [node, path] = nodeEntry console.log('选中了 paragraph 节点', node) console.log('节点 path 是', path) } } ``` ### setNodes 设置选中节点的属性 ```ts import { SlateTransforms } from '@wangeditor-next/editor' SlateTransforms.setNodes(editor, { // @ts-ignore textAlign: 'right' }, { mode: 'highest' // 针对最高层级的节点 }) ``` ### getParentNode 获取一个节点的父节点 ```ts const parentNode = editor.getParentNode(node) // 返回 node 或者 null ``` ### toDOMNode 获取一个节点对应的 DOM 节点 ```ts const elem = editor.toDOMNode(node) // 返回 HTMLElement ``` ### isInline 判断一个节点是否是 inline ```ts const inline = editor.isInline(node) ``` ### isVoid 判断一个节点是否是 void ```ts const void = editor.isVoid(node) ``` :::tip void node 即没有子元素的节点(它本身就可以看作是一个特殊字符),例如 image video 。可参考 [html void element](https://www.w3.org/TR/2011/WD-html-markup-20110113/syntax.html#void-element) 定义。 你可以通过 `editor.isVoid` 自定义哪些元素是 void ,但需要详细学习 slate 。 ::: ### isText 判断一个节点是否是 text ```ts import { SlateText } from '@wangeditor-next/editor' SlateText.isText(node) // true/false ``` ### isElement 判断一个节点是否是 elem ```ts import { SlateElement } from '@wangeditor-next/editor' SlateElement.isElement(node) // true/false ``` ### addMark 为选中的文本添加标记(文本样式) ```ts editor.addMark('bold', true) // 加粗 editor.addMark('color', '#999') // 文本颜色 ``` ### removeMark 对选中的文字,取消标记(文本样式) ```ts editor.removeMark('bold') // 取消加粗 ``` ### marks 获取选中文字的标记(文本样式) ```ts import { SlateEditor } from '@wangeditor-next/editor' SlateEditor.marks(editor) // 例如 { bold: true, color: "#595959" } ``` ## DOM 相关 ### id 属性 获取编辑器 id ```ts editor.id // 如 'wangEditor-1' ``` ### isFullScreen 属性 编辑器是否全屏 ```ts editor.isFullScreen // true/false ``` ### focus 聚焦到编辑器 ```ts editor.focus() // editor.focus(true) // 选区定位到最后 ``` ### blur 失焦编辑器 ```ts editor.blur() ``` ### isFocused 判断当前编辑器是否聚焦? ```ts editor.isFocused() // true/false ``` ### updateView 强制更新视图 ```ts editor.updateView() ``` :::tip updateView 是内部 API ,不建议用户使用。如要使用,也请勿频繁执行。 ::: ### scrollToElem 滚动到指定元素,类似锚点。如滚动到某个标题的位置。可实现标题目录,参考 [demo](https://wangeditor-next.github.io/demo/catalog.html)。 可根据 `toDOMNode` 获取 node 对应的 DOM 元素。 ```ts editor.scrollToElem(elemId) ``` ### showProgressBar 显示进度条,一般用于上传功能 ```ts editor.showProgressBar(progress) // progress 为 0-100 的数字 ``` ### hidePanelOrModal 隐藏当前的弹框 (如插入链接) 和下拉列表(如设置标题、设置字体) ```ts editor.hidePanelOrModal() ``` ### fullScreen 设置为全屏 ```ts editor.fullScreen() ``` :::tip 全屏功能,有 html 结构的要求,请参考[这里](./getting-started.md#全屏) ::: ### unFullScreen 取消全屏 ```ts editor.unFullScreen() ``` ### disable 禁用编辑器,设置为只读 ```ts editor.disable() ``` ### isDisabled 判断当前编辑器是否只读? ```ts editor.isDisabled() // true/false ``` ### enable 取消禁用,取消只读 ```ts editor.enable() ``` ### destroy 销毁编辑器和工具栏 ```ts editor.destroy() ``` :::tip destroy 仅仅是移除编辑器、工具栏的 DOM 节点,全局绑定的事件等。
自己定义的变量,如 `const editor = createEditor({...})` ,这个 `editor` 还需要自己来销毁。 ::: ### 获取编辑区域容器 DOM 获取编辑区域容器 DOM 节点 ```ts editor.getEditableContainer() ``` ## selection 相关 selection 数据结构参考 [slate Location](https://docs.slatejs.org/concepts/03-locations) 。 ### selection 属性 获取编辑器当前的选区。如果未选中,则返回 `null` 。 ```ts editor.selection // selection 或 null ``` selection 数据结构如下: ```json { "anchor": { "path": [1,0], "offset":8 }, "focus": { "path": [1,0], "offset":10 } } ``` ### select 选中一个指定的选区。 ```ts const newSelection = { anchor: { path: [1,0], offset:8 }, focus: { path: [1,0], offset:10 } } editor.select(newSelection) ``` ### selectAll 选中所有内容 ```ts editor.selectAll() ``` ### deselect 取消选中 ```ts editor.deselect() ``` ### move 移动光标 ```ts editor.move(3) // 移动 3 个字符 ``` ### moveReverse 反向移动光标 ```ts editor.moveReverse(2) // 反向移动 2 个字符 ``` ### restoreSelection 恢复最近一次非 null 选区。如编辑器 blur 之后,再重新恢复选区。 ```ts editor.restoreSelection() ``` ### isSelectedAll 判断编辑器是否全部选中。 ```ts editor.isSelectedAll() // true/false ``` ### getSelectionPosition 获取选区的定位,**将视情况返回 `left` `right` `top` `bottom` 的其中几个**。 ```ts editor.getSelectionPosition() // 例如 { left: "80.15px", top: "116px" } ``` 【注意】该定位是**相对于编辑区域**的,而非 body 。
你可以获取编辑区域 DOM 元素的定位 `editor.getEditableContainer().getBoundingClientRect()` 从而计算出相对于 body 的定位。 ### getNodePosition 获取某个节点的定位,**将视情况返回 `left` `right` `top` `bottom` 的其中几个**。 ```ts editor.getNodePosition(node) // 例如 { left: "80.15px", top: "116px" } ``` 【注意】该定位是**相对于编辑区域**的,而非 body。
你可以获取编辑区域 DOM 元素的定位 `editor.getEditableContainer().getBoundingClientRect()` 从而计算出相对于 body 的定位。 ## 自定义事件 wangEditor 使用 [event-emitter](https://www.npmjs.com/package/event-emitter) 来做自定义事件。 ### on 监听某个事件 ```ts editor.on('event-key', fn) ``` ### off 取消监听 ```ts editor.off('event-key', fn) ``` ### once 只监听一次 ```ts editor.once('event-key', fn) ``` ### emit 触发事件 ```ts editor.emit('event-key') ``` ### 内置的事件 ```ts editor.on('fullScreen', () => { console.log('fullScreen') }) editor.on('unFullScreen', () => { console.log('unFullScreen') }) editor.on('scroll', () => { console.log('scroll') }) editor.on('modalOrPanelShow', modalOrPanel => { console.log(modalOrPanel) }) editor.on('modalOrPanelHide', () => { console.log('modalOrPanelHide') }) ``` ## 使用 slate 解锁更多 API > wangEditor 基于 [slate.js](https://docs.slatejs.org/)(但不依赖 React)开发 上文已列出了比较常用的 API ,但这并不是全部。 slate.js 还提供了更多 API ,可满足你的所有操作需求。 ### Transforms API 参考 [slate Transforms API](https://docs.slatejs.org/api/transforms) 使用如下方式即可得到 slate Transforms 对象,不用再单独安装 slate 。 ```ts import { SlateTransforms } from '@wangeditor-next/editor' ``` ### Node Editor API 参考 [slate Node API](https://docs.slatejs.org/api/nodes) 使用如下方式即可得到 slate Node 相关对象,不用再单独安装 slate 。 ```ts import { SlateEditor, SlateNode, SlateElement, SlateText } from '@wangeditor-next/editor' ``` ### Location API 参考 [slate Location API](https://docs.slatejs.org/api/locations) 使用如下方式即可得到 slate Location 相关对象,不用再单独安装 slate 。 ```ts import { SlateLocation, SlatePath, SlatePoint, SlateRange } from '@wangeditor-next/editor' ``` --- ## 11. node-define.md # 节点数据结构 wangEditor 是基于 slate.js 为内核开发的,所以学习本文之前,要先了解 [slate Node 设计](https://docs.slatejs.org/concepts/02-nodes) 。 ## 是什么 很多同学可能根本不知道本文要讲什么,对于这里的“节点”和“数据结构”也不知何意。
没关系,接下来通过几个问题,就可以让你快速入门。 我们通过 [API](./API.md) 的学习,已经知道了 wangEditor 有丰富的 API 可供使用。
那么问题来了: - `editor.addMark(key, value)` 可以设置文本样式,如何设置删除线呢?此时 `key` `value` 该怎么写? - `editor.insertNode(node)` 可以插入一个节点,如何插入一个链接呢?此时 `node` 该怎么写? - `SlateTransforms.setNodes(editor, {...})` 可以设置节点的属性,如何设置行高呢?此时 `{...}` 这个属性该怎么写? 通过上述问题,你大概知道了本文的目的 —— 就是告诉你,编辑器内所有内容、节点的数据结构 —— 它们都是由哪些数据构成的。 ## 快速了解 如果想快速了解各个节点的数据结构,其实方法很简单。 - 创建一个编辑器,操作一下 - 查看 `editor.children` 例如,写一段文字、设置一个标题或列表,查看 `editor.children` 即可看到他们的数据结构 ![](/image/数据结构-1.png) 再例如,对文字设置行高,设置文本样式,查看 `editor.children` 即可看到他们的数据结构 ![](/image/数据结构-2.png) ## Text Node 文本节点,例如 `{ text: 'hello' }` **必须有 `text` 属性**。还可以自定义属性,例如加粗的文本可表示为 `{ text: 'hello', bold: true }` ,其他属性可自行扩展。 注意,文本节点是底层节点,所以没有子节点,**没有 `children` 属性**。 ## Element Node 元素节点,例如 `{ type: 'header1', children: [ { text: 'hello' } ] }` **必须有两个属性 `type` 和 `children` 属性**。还可以自定义属性,例如居中对齐可表示为 `{ type: 'header1', textAlign: 'center', children: [ { text: 'hello' } ] }` ,其他属性自行扩展。 ## Inline Element 元素默认是 block 显示,即占满一整行。但有些元素需要变为 inline 显示,如 `` `` 等。 我们可以**通过[插件](./development.md#劫持编辑器事件和操作-插件)来修改 `isInline` 把一个元素改为 inline** ,参考链接元素的[插件源码](https://github.com/wangeditor-next/wangEditor-next/blob/master/packages/basic-modules/src/modules/link/plugin.ts)。 ## Void Element 有些元素需要定义为 void 类型(即没有子节点),例如 `` `
`(针对 block 元素) 或 ``(针对 inline 元素)等通用标签。**谨慎使用 `` `

` `` 等编辑器默认支持的标签,那可能会带来冲突**。 - 使用 `data-w-e-type` 记录元素 `type` ,以便解析 HTML 时(下文讲)能识别到 - 使用 `data-w-e-is-void` 标记元素是 void ,以便解析 HTML 时能识别 - 使用 `data-w-e-is-inline` 标记元素是 inline ,以便解析 HTML 时能识别 - HTML 结构尽量扁平、简洁,这样更容易解析 HTML ,更稳定 #### 注册 elemToHtml 到 wangEditor 先定义 elemToHtml 配置 ```ts const elemToHtmlConf = { type: 'attachment', // 新元素的 type ,重要!!! elemToHtml: attachmentToHtml, } ``` 然后注册到 wangEditor ,有两种方式 第一,如果你只想注册 elemToHtml ,没有其他需求,则推荐使用 `registerElemToHtml` ```js import { Boot } from '@wangeditor-next/editor' Boot.registerElemToHtml(elemToHtmlConf) ``` 第二,如果你除了注册 elemToHtml 之外,还需要注册其他功能,则推荐使用 `registerModule` ```ts import { Boot, IModuleConf } from '@wangeditor-next/editor' const module: Partial = { // TS 语法 // const module = { // JS 语法 // menus: [menu1Conf, menu2Conf, menu3Conf], // 菜单 // editorPlugin: withBreakAndDelete, // 插件 // renderElems: [renderElemConf], // renderElem elemsToHtml: [elemToHtmlConf, /* 其他元素... */] // elemToHtml // 其他功能,下文讲解... } Boot.registerModule(module) ``` :::tip - 必须在创建编辑器之前注册 - 全局只能注册一次,不要重复注册 ::: 此时,你再执行 `editor.getHtml()` 即可得到“附件”元素的 HTML 代码,显示 HTML 时可配合 JS 实现点击下载附件的效果。 ### 解析新元素 HTML 到编辑器 通过 `const html = editor.getHtml()` 可以得到正确的 HTML ,但再去设置 HTML `editor.setHtml(html)` 却无效。需要你自定义解析 HTML 的逻辑。 #### 定义 parseElemHtml 函数 ```ts import { IDomEditor, SlateDescendant, SlateElement } from '@wangeditor-next/editor' /** * 解析 HTML 字符串,生成“附件”元素 * @param domElem HTML 对应的 DOM Element * @param children 子节点 * @param editor editor 实例 * @returns “附件”元素,如上文的 myResume */ function parseAttachmentHtml(domElem: Element, children: SlateDescendant[], editor: IDomEditor): SlateElement { // TS 语法 // function parseAttachmentHtml(domElem, children, editor) { // JS 语法 // 从 DOM element 中获取“附件”的信息 const link = domElem.getAttribute('data-link') || '' const fileName = domElem.getAttribute('data-fileName') || '' // 生成“附件”元素(按照此前约定的数据结构) const myResume = { type: 'attachment', link, fileName, children: [{ text: '' }], // void node 必须有 children ,其中有一个空字符串,重要!!! } return myResume } ``` #### 注册 parseElemHtml 到 wangEditor 先定义 parseHtml 配置 ```js const parseHtmlConf = { selector: 'span[data-w-e-type="attachment"]', // CSS 选择器,匹配特定的 HTML 标签 parseElemHtml: parseAttachmentHtml, } ``` 然后把 `parseHtmlConf` 注册到 wangEditor ,有两种方式: 第一,如果你只想注册一个 parseElemHtml ,没有别的功能,则推荐 `registerParseElemHtml` ```ts import { Boot } from '@wangeditor-next/editor' Boot.registerParseElemHtml(parseHtmlConf) ``` 第二,如果你除了想注册 parseElemHtml ,还想注册其他功能,则推荐 `registerModule` ```ts import { Boot, IModuleConf } from '@wangeditor-next/editor' const module: Partial = { // TS 语法 // const module = { // JS 语法 // menus: [menu1Conf, menu2Conf, menu3Conf], // 菜单 // editorPlugin: withBreakAndDelete, // 插件 // renderElems: [renderElemConf], // renderElem // elemsToHtml: [elemToHtmlConf], // elemToHtml parseElemsHtml: [parseHtmlConf, /* 其他元素... */] // parseElemHtml } Boot.registerModule(module) ``` :::tip - 必须在创建编辑器之前注册 - 全局只能注册一次,不要重复注册 ::: 此时,再把获取的 HTML 设置到编辑器中 `editor.setHtml(html)` 即可成功显示“附件”元素。 ## 总结 一个模块常用代码文件如下,共选择参考(不一定都用到) - render-elem.ts - elem-to-html.ts - parse-elem-html.ts - plugin.ts - menu/ - Menu1.ts - Menu2.ts --- ## 13. i18n.md # 多语言 ## 切换语言 默认可支持中文和英文,默认为中文。 ```js import { i18nChangeLanguage } from '@wangeditor-next/editor' // 切换语言 - 'en' 或者 'zh-CN' i18nChangeLanguage('en') // 创建编辑器... ``` ## 获取语言 获取全部语言配置 ```js import { i18nGetResources } from '@wangeditor-next/editor' const resources = i18nGetResources('en') // 'en' 或 'zh-CN' ``` 获取单个词汇 ```js import { t } from '@wangeditor-next/editor' console.log( t('header.title') ) ``` ## 增加新语言 除了中文和英文,使用其他语言,需要先添加语言的词汇,然后再切换语言。 ```js import { i18nAddResources, i18nChangeLanguage, t } from '@wangeditor-next/editor' // 添加新语言,如日语 ja i18nAddResources('ja', { // 标题 header: { title: 'ヘッダー', text: 'テキスト', }, // ... 其他语言词汇,下文说明 ... }) // 切换为日语 ja i18nChangeLanguage('ja') // 获取单个词汇 console.log( t('header.title') ) // 创建编辑器... ``` --- ## 14. theme.md # 主题 可以通过 CSS vars 定义自己的主题,样式请参考[源码](https://github.com/wangeditor-next/wangEditor-next/blob/master/packages/editor/src/assets/index.less)。 ```css /* 暗色主题 */ html.dark { --w-e-textarea-bg-color: #333; --w-e-textarea-color: #fff; /* ...其他... */ } ``` --- ## 15. for-ts.md # 用于 Typescript 将 wangEditor 用于 Typescript 的注意事项。 ## 扩展类型 新建一个 `custom-types.d.ts` ,源码如下。注意,保证该文件在 `tsconfig.json` 的 `include` 中。 ```ts import { SlateDescendant, SlateElement, SlateText } from '@wangeditor-next/editor' declare module '@wangeditor-next/editor' { // 扩展 Text interface SlateText { text: string } // 扩展 Element interface SlateElement { type: string children: SlateDescendant[] } } ``` --- ## 16. plugins.md # 插件 - [`@` mention 提及](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-mention) - [formula 公式](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-formula) - [markdown](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-markdown) - [链接卡片](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-link-card) - [浮动图片](https://github.com/wangeditor-next/wangEditor-next/tree/master/packages/plugin-float-image) --- ## 17. video-course.md # 视频教程 ## 视频教程 关注 wangEditor [B 站账号](https://space.bilibili.com/697803545) - [如何选择富文本编辑器](https://www.bilibili.com/video/BV1XB4y1C7EP) - [wangEditor5教程01-使用入门](https://www.bilibili.com/video/BV1GU4y1q7ob) - [wangEditor5教程02-用于 Vue2](https://www.bilibili.com/video/BV1b34y1h7oj) - [wangEditor5教程03-用于 Vue3](https://www.bilibili.com/video/BV1xR4y1A7yJ) - [wangEditor5教程04-用于 React](https://www.bilibili.com/video/BV1E3411N7XB) - [wangEditor5教程05-设置和获取内容](https://www.bilibili.com/video/BV1vG4y1i7pH) - [wangEditor5教程06-展示HTML无样式怎么办](https://www.bilibili.com/video/BV15a411J7UC) - [wangEditor5教程07-上传图片](https://www.bilibili.com/video/BV1GU4y1S7RQ) - [wangEditor5教程08-工具栏配置](https://www.bilibili.com/video/BV18L4y1F7qA/) - [wangEditor5教程09-菜单配置](https://www.bilibili.com/video/BV1LS4y187eC/) - [wangEditor5教程10-编辑器配置](https://www.bilibili.com/video/BV1jF41177GD/) - [wangEditor5教程11-API(视频较长,耐心观看)](https://www.bilibili.com/video/BV1fu411z75r/) - [wangEditor5教程12-自定义扩展 part1(视频较长,耐心观看)](https://www.bilibili.com/video/BV17t4y1L71C) - [wangEditor5教程13-自定义扩展 part2(视频较长,耐心观看)](https://www.bilibili.com/video/BV16Y4y1A7iM/) 如需要其他视频教程,可去提交 [issue](https://github.com/wangeditor-next/wangEditor-next/issues) 反馈。