amis Editor 代码编辑器组件详解:基于 Monaco 的多语言高亮、事件与动作完全实践指南
【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis
本文基于 amis 官方文档中的 Editor 编辑器组件说明展开,系统讲解该组件在表单中的基本用法、语言高亮配置、只读与全屏模式、monaco 选项控制以及深度定制能力,并结合当前仓库中的源码实现(amis 表单层渲染器、amis-ui 基础编辑器组件)补充其默认配置、高度自适应与资源清理等底层机制。读完本文,你可以直接在生产表单中配置出带语法高亮、可全屏、可联动的代码编辑器,并掌握通过editorDidMount获取 monaco 实例实现自动补全等高级定制的方法。
组件定位
amis 的editor是表单中的一个代码编辑表单项,底层基于 monaco-editor 开发,适合收集脚本片段、配置文本、JSON/SQL 等代码内容。如果业务场景是富文本编辑(如公告、文章正文),应改用 Rich-Text 组件,二者定位不同。
从源码结构看,该组件分为两层:
- 表单层:EditorControl 是注册到 amis 表单体系的
FormItem,负责取值、派发事件(change/focus/blur)、响应特性动作(clear/reset/focus)、高度自适应等; - UI 层:amis-ui 的 Editor 组件 封装了 monaco 的加载、初始化、全屏切换与占位符展示,表单层通过
LazyComponent以懒加载方式引用它,避免首屏引入整个 monaco 运行时。
基本用法
最简配置如下,将editor放入form的body中,通过name收集提交值:
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "editor", "name": "editor", "label": "编辑器", "placeholder": "function() {\n console.log('hello world')\n}" } ] }其中placeholder在编辑器没有值时以占位文本形式展示。对应到 UI 层实现,Editor 组件的 render 方法 只有在this.editor && placeholder && !value同时满足时才渲染占位符节点,因此占位内容仅在初始空值状态下可见,输入后立即消失。
支持的语言
通过language属性指定语法高亮语言,支持的语言列表如下(与源码中 availableLanguages 常量 完全一致):
bat、c、coffeescript、cpp、csharp、css、dockerfile、fsharp、go、handlebars、html、ini、java、javascript、json、less、lua、markdown、msdax、objective-c、php、plaintext、postiats、powershell、pug、python、r、razor、ruby、sb、scss、shell、sol、sql、swift、typescript、vb、xml、yaml
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "editor", "name": "editor", "label": "JSON编辑器", "language": "json" } ] }因为性能原因,上面的例子不支持实时修改
language生效——monaco 的语言模式在编辑器创建时绑定,运行中切换需要重建编辑器实例。
当然,你也可以使用xxx-editor这种类型简写形式,例如"type": "json-editor":
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "json-editor", "name": "editor", "label": "JSON编辑器" } ] }从源码看,EditorControls 注册逻辑 会遍历availableLanguages数组,为每种语言动态生成一个${lang}-editor类型的FormItem渲染器,其defaultProps.language固定为对应语言,因此json-editor、python-editor等类型与editor + language的效果等价。此外还额外注册了 js-editor 与 ts-editor 两个别名类型。
language支持通过${xxx}变量取值。源码中 render 方法 会先判断language是否为纯变量(isPureVariable),如果是则从当前数据中解析出真实语言,便于根据外部数据动态选择高亮模式(首次渲染时生效)。
值得一提的是,当language为json时,初始化阶段会自动开启 monaco 的 JSON 诊断校验(validate: true、allowComments: true),并对字符串值自动做JSON.parse后重新格式化为两空格缩进,使 JSON 编辑器开箱即带语法检查与美化能力。
只读模式
使用disabled: true让编辑器变为只读:
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "json-editor", "name": "editor", "disabled": true, "label": "JSON编辑器" } ] }实现上,表单层会将disabled透传为 monaco 的readOnly选项;当disabled状态发生变化时,UI 层会在 componentDidUpdate 中调用 updateOptions 动态切换,无需重建编辑器。注意options中的readOnly字段不应自行设置,只读统一由disabled控制。
全屏模式
设置allowFullscreen属性为true,编辑器右上角会显示全屏开关,点击后编辑器进入全屏模式:
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "editor", "name": "editor", "label": "支持全屏模式的编辑器", "allowFullscreen": true } ] }源码行为:handleFullscreenModeChange 切换isFullscreen状态,全屏样式由 SCSS 中的.is-fullscreen规则实现(position: fixed占满视口,见 _editor.scss);退出全屏时会保存并恢复进入全屏前的宽高,调用editor.layout()重新布局,避免退出后溢出父容器。
编辑器展现控制(options)
通过options属性透传 monaco 编辑器的其它配置,例如关闭行号:
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "editor", "name": "editor", "label": "编辑器", "options": { "lineNumbers": "off" } } ] }options即 monaco 官方IEditorOptions的透传入口(具体可选字段请查阅 monaco 官方文档),但不支持通过它设置readOnly,只读模式必须使用disabled: true。
结合源码可以看到,options会与两层内置默认配置合并。表单层 EditorControl.defaultProps 提供:
| 默认配置 | 值 | 作用 |
|---|---|---|
automaticLayout | true | 容器尺寸变化时自动重新布局 |
selectOnLineNumbers | true | 点击行号选中整行 |
scrollBeyondLastLine | false | 禁止滚动越过最后一行 |
folding | true | 启用代码折叠 |
minimap.enabled | false | 默认关闭小地图 |
UI 层的 monacoFactory 还会再补一层 monaco 级默认值:autoIndent: true、formatOnType: true、formatOnPaste: true、bracketPairColorization.enabled: true(括号对彩色高亮)、scrollbar.alwaysConsumeMouseWheel: false等。用户配置的options展开在最外层,可覆盖上述任何默认项。
编辑器自定义开发(editorDidMount)
如果想进行深度定制,比如实现自动完成功能,可以通过自定义editorDidMount属性获取 monaco 实例。该属性支持两种写法:
- 在 JS 中直接写函数;
- 写一个字符串,源码会将其包装为
new Function('editor', 'monaco', ...)执行(适配纯 JSON schema 场景)。
示例:
{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "editor", "name": "editor", "label": "编辑器", "language": "myLan", "editorDidMount": (editor, monaco) => { // editor 是 monaco 实例,monaco 是全局的名称空间 const dispose = monaco.languages.registerCompletionItemProvider('myLan', { /// 其他细节参考 monaco 手册 }); // 如果返回一个函数,这个函数会在编辑器组件卸载的时候调用,主要用于清理资源 return dispose; } } ] }关键约定:
- 回调参数
(editor, monaco)中,editor是 monaco 编辑器实例,monaco是 monaco 全局命名空间,可访问languages、editor、KeyMod等全部 API; - 若回调返回一个函数,该函数会在编辑器组件卸载时被调用,用于清理资源(如注销补全提供器、事件监听)。源码实现见 handleEditorMounted:返回的
dispose被推入toDispose数组,随componentWillUnmount统一执行,避免语言提供器泄漏。
从源码结构看,底层 UI 组件还提供了editorWillMount(monaco)(编辑器创建前,可修改 monaco 全局配置)与editorWillUnmount(editor, monaco)(实例销毁前)两个更底层的钩子(见 EditorBaseProps),表单层目前对外暴露的是editorDidMount。
属性表
除了支持 普通表单项属性表 中的配置(name、label、value、disabled、visible等)以外,editor 还支持以下专属配置:
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
language | string | javascript | 编辑器高亮的语言,支持通过${xxx}变量获取 |
size | string | md | 编辑器高度,取值可以是md、lg、xl、xxl(源码样式中还额外支持sm) |
allowFullscreen | boolean | false | 是否显示全屏模式开关 |
options | object | 见上文默认配置 | monaco 编辑器的其它配置,比如是否显示行号等,可参考 monaco 官方IEditorOptions文档,不过无法设置readOnly,只读模式需要使用disabled: true |
placeholder | string | - | 占位描述,没有值的时候展示 |
关于size,源码的 样式定义 中各档位对应的最小高度为:sm100px、md250px、lg300px、xl400px、xxl500px。编辑器默认无固定最大高度,会随内容行数增长——表单层的 updateContainerSize 通过监听onDidChangeModelDecorations(覆盖输入与折叠两类变化)计算最后一行位置 + 一行行高,动态设置容器高度并调用editor.layout(),实现"内容多高、编辑器多高"的自适应效果。
事件表
当前组件会对外派发以下事件,可以通过onEvent来监听这些事件,并通过actions来配置执行的动作,在actions中可以通过${事件参数名}或${event.data.[事件参数名]}来获取事件产生的数据,详细请查看事件动作。
[name]表示当前组件绑定的名称,即name属性,如果没有配置name属性,则通过value取值。
| 事件名称 | 事件参数 | 说明 |
|---|---|---|
change | [name]: string组件的值 | 代码变化时触发 |
focus | [name]: string组件的值 | 输入框获取焦点时触发 |
blur | [name]: string组件的值 | 输入框失去焦点时触发 |
源码层面,三个事件分别由 handleChange / handleFocus / handleBlur 通过dispatchEvent派发,且均支持被上层preventDefault拦截:若事件被阻止,则不会继续执行onChange或onFocus/onBlur回调,可用于校验场景下禁止值更新。
动作表
当前组件对外暴露以下特性动作,其他组件可以通过指定actionType: 动作名称、componentId: 该组件id来触发这些动作,动作配置可以通过args: {动作配置项名称: xxx}来配置具体的参数,详细请查看事件动作。
| 动作名称 | 动作配置 | 说明 |
|---|---|---|
clear | - | 清空 |
reset | - | 将值重置为初始值。6.3.0 及以下版本为resetValue |
focus | - | 获取焦点 |
setValue | value: string更新的值 | 更新数据 |
动作处理逻辑集中在表单层的 doAction 方法:clear直接onChange('');reset优先取表单 pristine 值,其次取resetValue,最后回退为空字符串;focus则调用 monaco 的editor.focus()并恢复最近一次光标位置(通过getPosition/setPosition实现),使程序化聚焦的体验与用户手动点击一致。
clear
{ "type": "form", "debug": true, "body": [ { "type": "editor", "name": "editor", "label": "编辑器", "id": "clear_text", "value": "hello" }, { "type": "button", "label": "清空", "onEvent": { "click": { "actions": [ { "actionType": "clear", "componentId": "clear_text" } ] } } } ] }reset
如果配置了resetValue,则重置时使用resetValue的值,否则使用初始值。
{ "type": "form", "debug": true, "body": [ { "type": "editor", "id": "reset_text", "name": "editor", "label": "编辑器", "value": "hello" }, { "type": "button", "label": "重置", "onEvent": { "click": { "actions": [ { "actionType": "reset", "componentId": "reset_text" } ] } } } ] }focus
{ "type": "form", "debug": true, "body": [ { "type": "editor", "id": "focus_text", "name": "editor", "label": "编辑器", "value": "hello" }, { "type": "button", "label": "聚焦", "onEvent": { "click": { "actions": [ { "actionType": "focus", "componentId": "focus_text" } ] } } } ] }setValue
{ "type": "form", "debug": true, "body": [ { "type": "editor", "id": "setvalue_text", "name": "editor", "label": "编辑器", "value": "hello" }, { "type": "button", "label": "赋值", "onEvent": { "click": { "actions": [ { "actionType": "setValue", "componentId": "setvalue_text", "args": { "value": "amis go go go!" } } ] } } } ] }外部赋值与源码补充说明
除动作表中的setValue外,表单数据变化(如service拉取到数据、表单initFetch)也会更新编辑器内容。UI 层的 componentDidUpdate 对外部值变更做了专门处理:当props.value与编辑器当前值不一致时,通过pushEditOperations整体替换模型内容,并用pushUndoStop包裹成一次独立的撤销步骤——这意味着外部赋值后按一次Ctrl+Z即可整体回退,而不会逐字符撤销;同时置位preventTriggerChangeEvent标志位,避免程序化赋值反向触发onChange,防止表单数据循环更新。若语言为json,外部传入的值还会先经JSON.parse再序列化为两空格缩进格式后写入。
另一个工程细节是 monaco 的 Web Worker 配置:amis-ui 在模块加载时初始化window.MonacoEnvironment.getWorkerUrl,按语言标签(json/css/html/typescript/javascript)映射到json.worker.js、css.worker.js、html.worker.js、ts.worker.js等 worker 地址;如果地址是 http(s) 开头,则包装为data:协议的内联importScripts形式加载,保证在 SDK 部署路径不可预知的场景下 worker 也能正常启动。
小结
amis 的editor组件以极薄的 JSON 配置面覆盖了三类典型需求:
- 常规代码输入:
type: editor(或xxx-editor简写)+language即可获得带高亮、折叠、括号配色的代码输入框,JSON 类型还自带诊断校验; - 展示与只读:
disabled: true+size档位 +options透传 monaco 选项,满足"只读代码块"展示; - 深度定制与联动:
editorDidMount暴露 monaco 实例用于注册补全、校验等能力,配合onEvent(change/focus/blur)与特性动作(clear/reset/focus/setValue)可完成与其他表单控件的完整联动。
实现上所有关键行为(只读切换、光标恢复、外部赋值的撤销分组、高度自适应、资源清理)均有源码支撑,位于 packages/amis/src/renderers/Form/Editor.tsx 与 packages/amis-ui/src/components/Editor.tsx,样式与高度档位定义在 packages/amis-ui/scss/components/form/_editor.scss,可作为二次开发时的权威参考。
【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考