博客
关于我
如何制作一款在线编译器
阅读量:407 次
发布时间:2019-03-05

本文共 3972 字,大约阅读时间需要 13 分钟。

在文章开始之前先展示一下我自己做的在线编译器 JS-Encoder:

大概三四个月之前我开始有了制作在线编译器的想法,在此之前我接触过很多的在线编译器,如、、等,这些都非常优秀且有着庞大的用户群体的编译器。

我一直对在线编译器的实现抱有浓厚兴趣,这些在线编译器支持很多种语言,代码变色,诸多的快捷键以及一些个性化设置,这使得在线编译器看上去和我们在本地下载的编译器软件也不会有太大的区别,我完全不知道这些复杂的功能要怎么实现,于是我观察 CodePenJsBin 代码发现他俩都使用了一个叫 的工具。

codemirror

codemirror 是一个用于浏览器的 JavaScript 实现的多功能文本编辑器。它专门用于编辑代码,并带有许多语言模式和插件 ,可实现更高级的编辑功能。

原来这些编译器是依靠 codemirror 来实现的,codemirror 是一个非常复杂的工具,以至于我花了两天时间才熟悉它的配置项。codemirror 本身是采用直接操作 DOM 的方式,而我的项目是使用 Vue + Webpack 构建的,这违反了 Vue 数据驱动 的宗旨,于是我在 npm 上发现了 这个工具,采用 Vue 的方式构建代码编辑器

codemirror 有许多配置项,我在自己的项目中用到了如下配置,如果你想看全部配置,可以看

cmOptions: {        // codemirror config        flattenSpans: false, // 默认情况下,CodeMirror会将使用相同class的两个span合并成一个。通过设置此项为false禁用此功能        tabSize: 2, // tab缩进空格数        mode: '', // 模式        theme: 'monokai', // 主题        smartIndent: true, // 是否智能缩进        lineNumbers: true, // 显示行号        matchBrackets: true, // 匹配符号        lineWiseCopyCut: true, // 如果在复制或剪切时没有选择文本,那么就会自动操作光标所在的整行        indentWithTabs: true, // 在缩进时,是否需要把 n*tab宽度个空格替换成n个tab字符        electricChars: true, // 在输入可能改变当前的缩进时,是否重新缩进        indentUnit: 2, // 缩进单位,默认2        autoCloseTags: true, // 自动关闭标签        autoCloseBrackets: true, // 自动输入括弧        foldGutter: true, // 允许在行号位置折叠        cursorHeight: 1, // 光标高度        keyMap: 'sublime', // 快捷键集合        extraKeys: {          'Ctrl-Alt': 'autocomplete',          'Ctrl-Q': cm => {            cm.foldCode(cm.getCursor())          }        }, //智能提示        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'], // 用来添加额外的gutter        styleActiveLine: true // 激活当前行样式      },

这些配置只是一小部分,但足够实现我想要的功能了

mode 表示当前编辑器使用的语言

theme 表示编辑器使用的配色,官方支持很多种配色,但确没有配色预览,所以我直接使用我熟悉的 monokai 作为主题,因为我比较喜欢 vscode 的配色,所以我找到 monokai.css 文件并修改了许多样式,虽然最后还是和真正的 vscode 主题有差异,但我真的尽力了😭

keymap 我设置为 sublimesublime上大部分快捷键都是可用的

其他的配置我在注释里应该已经说明白了,这里就不解释了

codemirror 的效果还是不错的

有了 codemirror 这个神器,可以说最难的问题已经解决了,但是还有很多数不清的小问题需要解决

布局

布局方面有很多是参考 JsBin 的,因为我觉得它的界面看起来很简洁,舒服

JsBin 的布局是酱婶儿的:

分为五个窗口,鼠标放到两个窗口的边界上可以拖动改变窗口大小

鼠标的拖动会使得一个窗口宽度增加,而另一个窗口宽度减少,但是两个窗口宽度之和是不会改变的

我的思路是:

在点击边界的时候获取两个相邻窗口的宽度,鼠标拖动的时候计算鼠标水平移动距离,并对两个窗口的宽度进行相应增减

由于这五个窗口都是同级的子组件,一个窗口获取另外一个窗口的宽度比较麻烦,于是我将这五个窗口的宽度都放在 Vuex 中储存以便使用,每一个窗口的宽度都随着 Vuex 中宽度信息的改变而改变

成功实现效果:

为了避免两个窗口重合问题,我设置了 min-width: 100px; 的样式

除了两个窗口的问题之外,还要做到所有窗口宽度随着浏览器宽度变化而改变:

这个效果也很容易实现,只要在浏览器宽度改变的时候每个窗口的宽度加上或减去 改变宽度/窗口数量 就可以了