概述
本文说的是如何通过monaco-editor
实现一个类似于codepen一样的在线代码测试工具。
微软之前有个项目叫做Monaco Workbench,后来这个项目变成了VSCode,而Monaco Editor(下文简称monaco)就是从这个项目中成长出来的一个web编辑器,他们很大一部分的代码(monaco-editor-core)都是共用的,所以monaco和VSCode在编辑代码,交互以及UI上几乎是一摸一样的,有点不同的是,两者的平台不一样,monaco基于浏览器,而VSCode基于electron,所以功能上VSCode更加健全,并且性能比较强大。
实现效果
实现代码
1、引入包
npm install monaco-editor --save
2、封装一个组件
<template>
<div class="the-code-editor-container" ref="container"></div>
</template>
<script>
import * as monaco from "monaco-editor";
export default {
name: "codeEditor",
props: {
options: {
type: Object,
default () {
return {
language: "javascript",
readOnly: true
};
}
},
value: {
type: String,
default: ""
}
},
data() {
return {
monacoInstance: null,
provider: null
};
},
mounted() {
this.init();
},
beforeDestroy() {
this.dispose();
},
methods: {
dispose() {
if (this.monacoInstance) {
if (this.monacoInstance.getModel()) {
this.monacoInstance.getModel().dispose();
}
this.monacoInstance.dispose();
this.monacoInstance = null;
if (this.provider) {
this.provider.dispose();
this.provider = null
}
}
},
init() {
let that = this;
// console.log(monaco.languages.CompletionItemKind)
this.dispose();
let createCompleters = textUntilPosition => {
//过滤特殊字符
let _textUntilPosition = textUntilPosition
.replace(/[\*\[\]@\$\(\)]/g, "")
.replace(/(\s+|\.)/g, " ");
//切割成数组
let arr = _textUntilPosition.split(" ");
//取当前输入值
let activeStr = arr[arr.length - 1];
//获得输入值的长度
let len = activeStr.length;
//获得编辑区域内已经存在的内容
let rexp = new RegExp('([^\\w]|^)' + activeStr + '\\w*', "gim");
let match = that.value.match(rexp);
let _hints = !match ? [] : match.map(ele => {
let rexp = new RegExp(activeStr, "gim");
let search = ele.search(rexp);
return ele.substr(search)
})
//查找匹配当前输入值的元素
let hints = Array.from(new Set([...that.hints, ..._hints])).sort().filter(ele => {
let rexp = new RegExp(ele.substr(0, len), "gim");
return match && match.length === 1 && ele === activeStr || ele.length === 1 ?
false :
activeStr.match(rexp);
});
//添加内容提示
let res = hints.map(ele => {
return {
label: ele,
kind: that.hints.indexOf(ele) > -1 ? monaco.languages.CompletionItemKind.Keyword : monaco.languages.CompletionItemKind.Text,
documentation: ele,
insertText: ele
};
});
return res;
};
this.provider = monaco.languages.registerCompletionItemProvider("sql", {
provideCompletionItems(model, position) {
var textUntilPosition = model.getValueInRange({
startLineNumber: position.lineNumber,
startColumn: 1,
endLineNumber: position.lineNumber,
endColumn: position.column
});
var suggestions = createCompleters(textUntilPosition);
return {
suggestions: suggestions
};
return createCompleters(textUntilPosition);
}
});
// 初始化编辑器实例
this.monacoInstance = monaco.editor.create(this.$refs["container"], {
value: this.value,
theme: "vs-dark",
autoIndex: true,
...this.options
});
// 监听编辑器content变化事件
this.monacoInstance.onDidChangeModelContent(() => {
this.$emit("contentChange", this.monacoInstance.getValue(), this.options.language);
});
}
}
};
</script>
<style scope lang="stylus">
.the-code-editor-container
width: 100%
height: 100%
overflow: hidden
border: 1px solid #eaeaea
text-align: left
.monaco-editor .scroll-decoration
box-shadow: none
</style>
3.引用实现
<template>
<div class="container">
<div class="panel">
<div class="code">
<div class="title">html</div>
<code-editor
:options="htmlOptions"
:value="htmlContent"
class="code-panel"
@contentChange="contentChange"
></code-editor>
</div>
<div class="code">
<div class="title">css</div>
<code-editor
:options="cssOptions"
:value="cssContent"
class="code-panel"
@contentChange="contentChange"
></code-editor>
</div>
<div class="code">
<div class="title">js</div>
<code-editor
:options="jsOptions"
:value="jsContent"
class="code-panel"
@contentChange="contentChange"
></code-editor>
</div>
</div>
<div class="panel">
<iframe class="view-panel" id="preview" frameborder="0"></iframe>
</div>
</div>
</template>
<script>
import CodeEditor from '@/components/codeview/index.vue'
export default {
name: 'HelloWorld',
components: {
CodeEditor
},
data() {
return {
htmlContent: `<div id="map"></div>`,
cssContent: `html,
body,
#map {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}`,
jsContent: `var osm = new ol.layer.Tile({
source: new ol.source.OSM()
});
var map = new ol.Map({
controls: ol.control.defaults({
attribution: false
}),
target: 'map',
layers: [osm],
view: new ol.View({
minZoom: 2,
maxZoom: 18,
center: [0, 0],
zoom: 3
})
});`,
htmlOptions: {
language: "html"
},
cssOptions: {
language: "css"
},
jsOptions: {
language: "javascript"
}
}
},
methods: {
contentChange(val, type) {
switch (type) {
case 'html': {
this.htmlContent = val;
break;
}
case 'css': {
this.cssContent = val;
break;
}
default: {
this.jsContent = val;
break;
}
}
this.runAllCodes();
},
runAllCodes() {
var html = this.htmlContent;
var css = this.cssContent;
var js = this.jsContent;
var code = "<!DOCTYPE html>\n" +
"<html lang=\"en\">\n" +
"<head>\n" +
" <meta charset=\"UTF-8\">\n" +
" <title>Editor</title>\n" +
" <style>";
code += "\n" + css;
code +=
"\n </style>\n" +
"</head>\n" +
"<body>\n";
code += "\n" + html;
code +=
"\n <script>\n";
code += "\n" + js;
code +=
"\n <\/script>\n" +
"<\/body>\n" +
"</html>";
const preview = document.getElementById('preview')
preview.setAttribute("srcdoc", code);
}
}
}
</script>
<style scope lang="stylus">
$font-size = 30px
$color = red
.container
width: 100%
height: 100%
margin: 0
padding: 0
.view-panel
width: 100%
height: 100%
.panel
width: 100%
height: 50%
.title
height: 30px
line-height: 30px
padding: 0 10px
border-left: 4px solid #00f
.code
width: calc(100% / 3 - 2px)
height: 100%
float: left
margin-left: 3px
&:first-child
margin-left: 0
.code-panel
width: 100%
height: calc(100% - 30px)
</style>
技术博客
CSDN:http://blog.csdn.NET/gisshixisheng
联系方式
类型 | 内容 |
---|---|
1004740957 | |
公众号 | lzugis15 |
niujp08@qq.com | |
webgis群 | 452117357 |