1. Installation
yarn add monaco-editor
或
npm install monaco-editor --save
Two, use
<div ref="editorContainer" style="height:100%;"></div>
import * as monaco from 'monaco-editor';
const editorContainer = ref<any>(null)
const editor = ref<any>(null)
onMounted(() => {
editor.value = monaco.editor.create(editorContainer.value,{
value: "test",
language:"javascript",
folding: true, // 是否折叠
foldingHighlight: true, // 折叠等高线
foldingStrategy: "indentation", // 折叠方式 auto | indentation
showFoldingControls: "always", // 是否一直显示折叠 always | mouseover
disableLayerHinting: true, // 等宽优化
emptySelectionClipboard: false, // 空选择剪切板
selectionClipboard: false, // 选择剪切板
automaticLayout: true, // 自动布局
codeLens: false, // 代码镜头
scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
colorDecorators: true, // 颜色装饰器
accessibilitySupport: "off", // 辅助功能支持 "auto" | "off" | "on"
lineNumbers: "on", // 行号 取值: "on" | "off" | "relative" | "interval" | function
lineNumbersMinChars: 5, // 行号最小字符 number
readOnly: false, //是否只读 取值 true | false
})
})
3. Custom highlighting
monaco.languages.register({
id: 'mylanguages' })
monaco.languages.setMonarchTokensProvider('mylanguages', {
ignoreCase: true, // 忽略大小写
tokenizer: {
root:[
[/curl/, {
token: "string.escape"}],
[/-X|-H|-d/, {
token: "keyword"}],
[/POST|GET|DELETE|PATCH|PUT/, {
token: "comment.doc"}],
],
}
})
The highlighting rules are in the root. [/curl/, {token: “string.escape”}]: Indicates that the highlight color of 'curl' is pink.
Highlight color reference: https://microsoft.github.io/monaco-editor/monarch.html
Effect:
4. Custom prompts
monaco.languages.registerHoverProvider('mylanguages', {
// 光标移入提示功能
provideHover: function (model, position, token) {
const lineword = model.getLineContent(position.lineNumber) // 获取光标悬停所在行的所有内容
const word = model.getWordAtPosition(position)?.word // 获取光标悬停的单词
if (word === "name") {
return {
contents: [
{
value: `${
word}` },
{
value: [
"这是name的一些介绍",
"这是name的一些介绍",
].join("\n\n"),
},
],
};
} else if(undefined !== word){
return {
contents: [
{
value: `${
word}` },
{
value: [
lineword
].join("\n\n"),
},
],
}
}
},
});
Effect:
Five, complete code
1. Parent component:
The height, initial content, highlight type, and read-only required by the parent component of HomeView.vue passed to the child component. The
child component passes the value to the parent component in real time through the editorChange method
<template>
<div>
<monaco
ref="monacoEdit"
:value="data"
:readonly="false"
type="curl"
:height="300"
@editorChange="editorChange"
></monaco>
</div>
</template>
<script setup lang="ts">
import monaco from '../components/MonacoView.vue'
import {
ref, toRefs, reactive } from "vue"
const data = ref('test')
function editorChange(val: string) {
//val:子组件实时传过来的值
console.log(val)
}
</script>
<style scoped>
</style>
2. Subcomponent: MonacoView.vue
<template>
<div class="editorapp">
<div ref="editorContainer" :style="{height:editor_height}"></div>
</div>
</template>
<script setup lang="ts">
import {
onMounted, ref, toRaw, watchEffect } from "vue"
// 引入方法一
import * as monaco from 'monaco-editor';
// 引入方法二( 这种引入方法体积小但没有鼠标悬停方法registerHoverProvider)
// import * as monaco from 'monaco-editor/esm/vs/editor/editor.api.js';
// import 'monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution'
const emit = defineEmits(['contentChange'])
const props = defineProps({
value: {
type: String,
default: '',
},
height: {
// 编辑器height
type: [String, Number],
default: 500,
},
readonly: {
// 是否只读
type: Boolean,
default: false,
},
type: {
// 高亮类型(javascript、curl等,javascript:自带的,curl:自定义的高亮规则)
type: String,
default: 'javascript',
}
})
const editorContainer = ref<any>(null)
const editor = ref<any>(null)
const data = ref(props.value)
const editor_height = ref(`${
props.height}px`)
onMounted(() => {
// 初始化编辑器,确保dom已经渲染
if(props.type === 'curl'){
// 如果是curl 类型则重新定义高亮规则,否则使用自带的高亮语言
monaco.languages.register({
id: props.type })
monaco.languages.setMonarchTokensProvider(props.type, {
ignoreCase: true,
tokenizer: {
root:[
[/curl/, {
token: "string.escape"}],
[/-X|-H|-d/, {
token: "keyword"}],
[/POST|GET|DELETE|PATCH|PUT/, {
token: "comment.doc"}],
],
}
})
}
monaco.languages.registerHoverProvider(props.type, {
// 光标移入提示功能
provideHover: function (model, position, token) {
const lineword = model.getLineContent(position.lineNumber) // 获取光标悬停所在行的所有内容
const word = model.getWordAtPosition(position)?.word // 获取光标悬停的单词
if (word === "name") {
return {
contents: [
{
value: `${
word}` },
{
value: [
"这是name的一些介绍",
"这是name的一些介绍",
].join("\n\n"),
},
],
};
} else if(undefined !== word){
return {
contents: [
{
value: `${
word}` },
{
value: [
lineword
].join("\n\n"),
},
],
}
}
},
});
editor.value = monaco.editor.create(editorContainer.value,{
value: data.value,
language:props.type,
folding: true, // 是否折叠
foldingHighlight: true, // 折叠等高线
foldingStrategy: "indentation", // 折叠方式 auto | indentation
showFoldingControls: "always", // 是否一直显示折叠 always | mouseover
disableLayerHinting: true, // 等宽优化
emptySelectionClipboard: false, // 空选择剪切板
selectionClipboard: false, // 选择剪切板
automaticLayout: true, // 自动布局
codeLens: false, // 代码镜头
scrollBeyondLastLine: false, // 滚动完最后一行后再滚动一屏幕
colorDecorators: true, // 颜色装饰器
accessibilitySupport: "off", // 辅助功能支持 "auto" | "off" | "on"
lineNumbers: "on", // 行号 取值: "on" | "off" | "relative" | "interval" | function
lineNumbersMinChars: 5, // 行号最小字符 number
readOnly: props.readonly, //是否只读 取值 true | false
})
editor.value.onDidChangeModelContent((val: any) => {
//内容改变时给父组件实时返回值
emit('editorChange', toRaw(editor.value).getValue())
})
})
watchEffect(()=>{
// 监听父组件值的变化,重新赋值给编辑器
if(editor.value)
toRaw(editor.value).setValue(props.value)
})
</script>
<style scoped>
.editorapp {
height: 100%;
width: 100vh;
}
</style>
Effect:
6. Practical issues
Two-way binding is not implemented. It will be troublesome for the child component to pass the value to the parent component. If many pages are used, it is necessary to repeatedly write a lot of methods for receiving parameters, and repeatedly define many additional parameters to receive the value of the child component.
The reason for defining additional parameters in the parent component to receive the passed value of the child component:
Modification:
In the parent component: changed to v-model, the editorChange method is no longer needed
<monaco-editor ref="monacoEdit" v-model="data" :readonly="false" main="javascript" bgcolor="vs-dark'" />
In the subcomponent: (only the modified part is displayed)
// 编辑器避免重复赋值
watchEffect(() => {
if (editor.value && toRaw(editor.value).getValue() !== props.modelValue)
toRaw(editor.value).setValue(props.modelValue)
})
const emit = defineEmits<{
(e: 'update:modelValue', value: string): void
}>()
// 监听值的变化
editor.value.onDidChangeModelContent((val: any) => {
// 给父组件实时返回最新文本
emit('update:modelValue', toRaw(editor.value).getValue())
})