Vue项目中使用高亮编辑器(Monaco)
前言虽然作为后台开发,但是前后端都是需要懂一些的~那么问题来了,最近在写前端的过程中有这么一个需求,需要对网页中的json进行高亮显示,考虑到自己手动通过匹配修改dom会花费很多时间,于是灵(准)机(备)一(偷)动(懒),通过直接在网页内嵌入代码编辑器实现高亮(顺便还提供了许多额外的功能,哈哈)然而配置过程并没有想象中那么顺利,这里记录配置的过程以及一些遇到的坑,方便以后在遇到时可以愉快的绕...
前言
虽然作为后台开发,但是前后端都是需要懂一些的~
那么问题来了,最近在写前端的过程中有这么一个需求,需要对网页中的json进行高亮显示,考虑到自己手动通过匹配修改dom会花费很多时间,于是灵(准)机(备)一(偷)动(懒),通过直接在网页内嵌入代码编辑器实现高亮(顺便还提供了许多额外的功能,哈哈)
然而配置过程并没有想象中那么顺利,这里记录配置的过程以及一些遇到的坑,方便以后在遇到时可以愉快的绕过去,前端使用vue,版本2.5.17。
下面开始进入正题~
编辑器的选择
本人期间一共就调研了2款web代码编辑器(如果第一个部署成功就没有第二个了……)
两款编辑器分别是:
首先,两款编辑器在本地都是能够成功配置运行的,由于只有monaco成功部署了,那么这里主要讲讲为什么ace部署失败,以及vue中搭载monaco编辑器的一些编码配置细节。
想要尝试vue中使用ace编辑器的小伙伴可以参考这篇文章
ace部署失败主要原因是不支持webpack打包,ace在本地搭建完成后,会有一个小型的worker帮助我们对内容进行渲染,webpack打包之后就变成完完全全的静态资源了,动态渲染的效果也就没了(按找官网的意思是可以支持的,但是我实践了很多方法,均以失败告终),而monaco帮助我们解决了这个问题。
网上有很多封装好的monaco组件,看的头疼,个人不推荐大家使用那些组件,可维护性不佳。
monaco安装与使用
npm install monaco-editor -S
等待安装完毕,现在我们就可以开始正式的配置了
本人倾向于将monaco封装成组件后在进行使用,在vue项目中新建一个.vue文件,我在这里直接命名为MonacoEditor.vue
,html模版内容如下:
<template>
<div class="monaco-container">
<div ref="container" class="monaco-editor"></div>
</div>
</template>
可以看到标签非常的简单~
接着,我们继续在js代码部分进行编写
import * as monaco from 'monaco-editor'
MonacoEditor
接受从父组件传递下来的数据,在props中配置如下:
props: {
// 编辑器中呈现的内容
codes: {
type: String,
default: function () {
return ''
}
},
readOnly: {
type: Boolean,
default: function () {
return false
}
},
// 主要配置
editorOptions: {
type: Object,
default: function () {
return {
selectOnLineNumbers: true,
roundedSelection: false,
readOnly: this.readOnly, // 只读
cursorStyle: 'line', // 光标样式
automaticLayout: false, // 自动布局
glyphMargin: true, // 字形边缘
useTabStops: false,
fontSize: 28, // 字体大小
autoIndent: false // 自动布局
}
}
}
}
选择在页面渲染时初始化monaco实例
mounted () {
this.monacoEditor = monaco.editor.create(this.$refs.container, {
value: this.codes, // 见props
language: 'python',
theme: 'vs-dark', // 编辑器主题:vs, hc-black, or vs-dark,更多选择详见官网
editorOptions: this.editorOptions // 同codes
})
}
这里的this.$refs.container
拿到的上html模版中的标记ref
为container
的dom
节点,不推荐在dom
中使用id
,可能会导致后期打包出现问题。
至此一个简陋的编辑器组件就完成了: )
注册调用
这里选择将其注册为一个全局的组件供项目使用,新建一个install.js
,写入如下内容:
import Monaco from 'path / of / your / MonacoEditor.vue' // 这里导入文件名不带后缀亦可
export default {
install: function (Vue, options) {
Vue.component('monaco', Monaco)
}
}
在main.js
中添加如下代码:
import componentsInstall from 'path / of / your /install.js'
Vue.use(componentsInstall)
我们在父组件这么调用它
<monaco
:codes="content"
:readOnly="false"
>
</monaco>
至此,一个简单的monaco编辑器组件嵌入就完成了。
改进和优化
下面针对一些场景对这个组件做一些优化
- 场景1
当多个组件中嵌入了monaco时,在单页面应用下进行切换会出现编辑器呈现内容不会跟随当前组件内容进行变化,这里有两种解决方案:
(1)使用updated生命周期函数钩子(未实践过)
(2)使用watch监测数据变化,触发更新函数
这里采用第二种,在MonacoEditor
中添加侦听器watch,侦听数据可以为codes
,但是不推荐,这里我向MonacoEditor
传递一个变量值current
,当切换组件时,该值产生变化,从而在watch中出触发更新内容的函数。添加的代码如下
<!-- 父组件 -->
<monaco
:codes="content"
:current="current"
>
</monaco>
// MonacoEditor.vue
//..
props: {
//...
//...
current: Object // 类型随意,需要保证不同个组件编辑器切换时current值变化
}
//..
//..
watch: {
current () {
this.monacoEditor.setValue(this.codes)
}
},
其中setValue
是一个较为重要的函数,顾名思义,他可以根据传递进来的参数值设置编辑器中初始呈现的内容。
- 场景2
此时的MonacoEditor在展示方面已经足够好了,但是光有展示还是不够的,作为一个编辑器,监听用户的输入,获取其值也是一项必不可少的功能。
如何监听,这里牵扯到一个重要的函数,onDidChangeModelContent
,他可以用来绑定编辑事件,我们在MonacoEditor.vue
中添加如下代码:
mounted () {
// ...
this.monacoEditor.onDidChangeModelContent((event) => {
let changeContent = this.monacoEditor.getValue()
this.$emit('update:contentBody', changeContent)
})
},
其中onDidChangeModelContent
为monaco实例所带的方法,event
是一个IModelContentChangedEvent
对象,他包含了非常非常详细的变更信息,包括操作的类型(撤销、恢复,还是手动输入引发的文本变更),变更的文本位置,变更的文本内容等。
当用户在编辑器中输入内容时,onDidChangeModelContent
监听到内容变化,通过调用getValue()
方法我们可以获取当前最新的内容,拿到最新内容后,我们可以对相应的值进行更新(本例中拿到最新内容changeContent
后,传递给父组件进行赋值更新或者进行别的操作)
这里有个小坑,由于无法通过v-model绑定codes值,所以通过上述代码来更新值。
安装使用 monaco-editor-webpack-plugin
这个插件帮助我们解决了webpack在打包过程中可能会遇到的很多问题,这些问题在这篇文章中有比较详细的解释,本篇中不做过多重复的介绍。
下载安装monaco-editor-webpack-plugin
npm install monaco-editor-webpack-plugin -S
安装完成后,在vue.config.js添加如下配置内容(若无此文件,则自己新建一个)
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.exports = {
//...
//...
configureWebpack: {
plugins: [
new MonacoWebpackPlugin()
]
}
}
本地调试完成以后webpack打包部署,没有问题~来看看最终的效果:
OK~大功告成~:)
更多推荐
所有评论(0)