一、前言前段时间碰到了一个 Keybinding 相关的问题,于是探究了一番,首先大家可能会有两个问题:Monaco Editor 是啥?Keybinding 又是啥?
- Monaco Editor:微软开源的一个代码编辑器,为 VS Code 的编辑器提供支持,Monaco Editor 核心代码与 VS Code 是共用的(都在 VS Code github 仓库中) 。
- Keybinding:Monaco Editor 中实现快捷键功能的机制(其实准确来说,应该是部分机制),可以使得通过快捷键来执行操作,例如打开命令面板、切换主题以及编辑器中的一些快捷操作等 。
文中使用的代码版本:
Monaco Editor:0.30.1
VS Code:1.62.1
二、举个这里使用 monaco-editor 创建了一个简单的例子,后文会基于这个例子来进行介绍 。
import React, { useRef, useEffect, useState } from "react";import * as monaco from "monaco-editor";import { codeText } from "./help";const Editor = () => {const domRef = useRef<HTMLDivElement>(null);const [actionDispose, setActionDispose] = useState<monaco.IDisposable>();useEffect(() => {const editorIns = monaco.editor.create(domRef.current!, {value: codeText,language: "typescript",theme: "vs-dark",});const action = {id: 'test',label: 'test',precondition: 'isChrome == true',keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyL],run: () => {window.alert('chrome: cmd + k');},};setActionDispose(editorIns.addAction(action));editorIns.focus();return () => {editorIns.dispose();};}, []);const onClick = () => {actionDispose?.dispose();window.alert('已卸载');};return (<div><div ref={domRef} className='editor-container' /><button className='cancel-button' onClick={onClick}>卸载keybinding</button></div>);};export default Editor;
三、原理机制1. 概览根据上面的例子,Keybinding 机制的总体流程可以简单的分为以下几步:- 初始化:主要是初始化服务以及给 dom 添加监听事件
- 注册:注册 keybinding 和 command
- 执行:通过按快捷键触发执行对应的 keybinding 和 command
- 卸载:清除注册的 keybinding 和 command
const editorIns = monaco.editor.create(domRef.current!, {value: codeText,language: "typescript",theme: "vs-dark",});
初始化过程如下:文章插图
创建 editor 之前会先初始化 services,通过实例化 DynamicStandaloneServices 类创建服务:
let services = new DynamicStandaloneServices(domElement, override);
在 constructor 函数中会执行以下代码注册 keybindingService:let keybindingService = ensure(IKeybindingService, () =>this._register(new StandaloneKeybindingService(contextKeyService,commandService,telemetryService,notificationService,logService,domElement)));
其中 this._register 方法和 ensure 方法会分别将 StandaloneKeybindingServices 实例保存到 disposable 对象(用于卸载)和 this._serviceCollection 中(用于执行过程查找keybinding) 。实例化 StandaloneKeybindingService,在 constructor 函数中添加 DOM 监听事件:
this._register(dom.addDisposableListener(domNode,dom.EventType.KEY_DOWN,(e: KeyboardEvent) => {const keyEvent = new StandardKeyboardEvent(e);const shouldPreventDefault = this._dispatch(keyEvent,keyEvent.target);if (shouldPreventDefault) {keyEvent.preventDefault();keyEvent.stopPropagation();}}));
以上代码中的 dom.addDisposableListener 方法,会通过 addEventListener 的方式,在 domNode 上添加一个 keydown 事件的监听函数,并且返回一个 DomListener 的实例,该实例包含一个用于移除事件监听的 dispose 方法 。然后通过 this._register 方法将 DomListener 的实例保存起来 。
经验总结扩展阅读
- 海藻宋思明是什么电视剧中的人物?
- 海藻是什么电视剧中的人物?
- 浦卞是哪个电视剧中的人物?
- 沙晓丽是什么电视剧中的人物?
- 汪美丽是什么电视剧中的人物?
- 沈岸初夏是什么电视剧中的人物?
- 沈梅妮妮是什么电视剧中的人物?
- 江天蓝茉莉是什么电视剧中的人物?
- 江德才和边凤梅是什么电视剧中的人物?
- 毛克利是什么电影中的人物?