前言
近期要完成一个代码编辑器的内容,用的vue3.0+ts+vite架构,学习尚浅,
常在插件上遇坑
特此记录下在monaco-editor的使用
需求:yaml和sql的文件的高亮、补全
- 1
- 2
- 3
- 4


实现
- 安装
// ^0.34.1
yarn add monaco-editor
- 1
- 2
- 在vite.config.js中配置(如果不需要ts\js\html就不需要这么做)
// 强制预构建插件包
optimizeDeps: {
include: [
`monaco-editor/esm/vs/language/json/json.worker`,
`monaco-editor/esm/vs/language/css/css.worker`,
`monaco-editor/esm/vs/language/html/html.worker`,
`monaco-editor/esm/vs/language/typescript/ts.worker`,
`monaco-editor/esm/vs/editor/editor.worker`
],
},
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 组件封装与使用
monacoEditor.vue组件<template> <div ref="codeEditBox" class="codeEditBox" :class="hightChange&&''codeEditBox1''" /> </template> <script lang="ts"> import { defineComponent, onBeforeUnmount, onMounted, ref, watch, } from ''vue'' import JsonWorker from ''monaco-editor/esm/vs/language/json/json.worker?worker'' import CssWorker from ''monaco-editor/esm/vs/language/css/css.worker?worker'' import HtmlWorker from ''monaco-editor/esm/vs/language/html/html.worker?worker'' import TsWorker from ''monaco-editor/esm/vs/language/typescript/ts.worker?worker'' import EditorWorker from ''monaco-editor/esm/vs/editor/editor.worker?worker'' import * as monaco from ''monaco-editor'' import { language as sqlLanguage } from ''monaco-editor/esm/vs/basic-languages/sql/sql.js''; import { language as yamlLanguage } from ''monaco-editor/esm/vs/basic-languages/yaml/yaml.js''; import ''monaco-editor/esm/vs/basic-languages/sql/sql.contribution'' import { editorProps } from ''./monacoEditorType'' export default defineComponent({ name: ''MonacoEditor'', props: editorProps, emits: [''update:modelValue'', ''change'', ''editor-mounted''], setup(props, { emit }) { (self as any).MonacoEnvironment = { getWorker(_: string, label: string) { if (label === ''json'') { return new JsonWorker() } if ([''css'', ''scss'', ''less''].includes(label)) { return new CssWorker() } if ([''html'', ''handlebars'', ''razor''].includes(label)) { return new HtmlWorker() } if ([''typescript'', ''javascript''].includes(label)) { return new TsWorker() } return new EditorWorker() }, } let editor: any const codeEditBox = ref() const init = () => { monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({ noSemanticValidation: true, noSyntaxValidation: false, }) monaco.languages.typescript.javascriptDefaults.setCompilerOptions({ target: monaco.languages.typescript.ScriptTarget.ES2020, allowNonTsExtensions: true, }) monaco.languages.registerCompletionItemProvider(''sql'', { provideCompletionItems() { const suggestions:any = []; // 这个keywords就是sql.js文件中有的 sqlLanguage.keywords.forEach((item:any) => { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Keyword, insertText: item, }); }) sqlLanguage.operators.forEach((item:any) => { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Operator, insertText: item, }); }) sqlLanguage.builtinFunctions.forEach((item:any) => { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Function, insertText: item, }); }) sqlLanguage.builtinVariables.forEach((item:any) => { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Variable, insertText: item, }); }) return { // 最后要返回一个数组 suggestions, }; }, }) monaco.languages.registerCompletionItemProvider(''yaml'', { provideCompletionItems() { const suggestions:any = []; // 这个keywords就是python.js文件中有的 yamlLanguage.keywords.forEach((item:any) => { suggestions.push({ label: item, kind: monaco.languages.CompletionItemKind.Keyword, insertText: item, }); }) return { // 最后要返回一个数组 suggestions, }; }, }) editor = monaco.editor.create(codeEditBox.value, { value: props.modelValue, language: props.language, readOnly: props.readOnly, theme: props.theme, ...props.options, }) // 监听值的变化 editor.onDidChangeModelContent(() => { const value = editor.getValue() // 给父组件实时返回最新文本 emit(''update:modelValue'', value) emit(''change'', value) }) emit(''editor-mounted'', editor) } watch( () => props.modelValue, (newValue) => { if (editor) { const value = editor.getValue() if (newValue !== value) { editor.setValue(newValue) } } }, ) watch( () => props.options, (newValue) => { editor.updateOptions(newValue) }, { deep: true }, ) watch( () => props.readOnly, () => { console.log(''props.readOnly'', props.readOnly) editor.updateOptions({ readOnly: props.readOnly }) }, { deep: true }, ) watch( () => props.language, (newValue) => { monaco.editor.setModelLanguage(editor.getModel()!, newValue) }, ) onBeforeUnmount(() => { editor.dispose() }) onMounted(() => { init() }) return { codeEditBox } }, }) </script> <style lang="scss" scoped> .codeEditBox { width: 100%; flex: 1; min-height: 100px; // height: 200px; overflow-y: auto; } .codeEditBox1{ height: calc(100% - 323px); } </style>- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- monacoEditorType.ts类型定义文件
import { PropType } from ''vue'' export type Theme = ''vs'' | ''hc-black'' | ''vs-dark'' export type FoldingStrategy = ''auto'' | ''indentation'' export type RenderLineHighlight = ''all'' | ''line'' | ''none'' | ''gutter'' export interface Options { automaticLayout: boolean // 自适应布局 foldingStrategy: FoldingStrategy // 折叠方式 auto | indentation renderLineHighlight: RenderLineHighlight // 行亮 selectOnLineNumbers: boolean // 显示行号 placeholder:string minimap: { // 关闭小地图 enabled: boolean } // readOnly: Boolean // 只读 fontSize: number // 字体大小 scrollBeyondLastLine: boolean // 取消代码后面一大段空白 overviewRulerBorder: boolean // 不要滚动条的边框 } export const editorProps = { modelValue: { type: String as PropType<string>, default: null, }, hightChange: { type: Boolean, default: false, }, width: { type: [String, Number] as PropType<string | number>, default: ''100%'', }, height: { type: [String, Number] as PropType<string | number>, default: ''100%'', }, language: { type: String as PropType<string>, default: ''javascript'', }, readOnly: { type: Boolean, default: false, }, theme: { type: String as PropType<Theme>, validator(value: string): boolean { return [''vs'', ''hc-black'', ''vs-dark'', ''hc-light''].includes(value) }, default: ''vs'', }, options: { type: Object as PropType<Options>, default() { return { automaticLayout: true, // foldingStrategy: ''indentation'', foldingStrategy: ''indentation'', // 折叠方式 auto | indentation // renderLineHighlight: ''all'', renderLineHighlight: ''all'' || ''line'' || ''none'' || ''gutter'', // 行亮 selectOnLineNumbers: true, // 显示行号 minimap: { // 关闭小地图 enabled: false, }, placeholder: ''ss'', // readOnly: false, // 只读 fontSize: 16, // 字体大小 scrollBeyondLastLine: false, // 取消代码后面一大段空白 overviewRulerBorder: false, // 不要滚动条的边框 } }, }, }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 在父组件中使用
<monacoEditor v-model="value" :language="language" :hight-change="hightChange" :read-only="tablist.length===0" width="100%" height="100%" @editor-mounted="editorMounted" /> import monacoEditor from ''./monacoEditor.vue'' const value = ref(''-- select * from infrastructure;'') const language = ref(''sql'') const hightChange = ref<any>(false) const editorMounted = (editor: any) => { console.log(''editor实例加载完成'', editor) }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
value:编辑器代码显示的值 language:要加载的语言类型 hightChange:改变编辑器的高度(可去掉,我这边有个sql查表的实现,需要在编辑区下面加一个sql查询的表格,所以需要这个参数) read-only:编辑区是否是只读的,当左侧文件树为空时没有文件,编辑区不可写 editorMounted:加载完成操作- 1
- 2
- 3
- 4
- 5
//记得在env.d.ts配置包的 declare module ''monaco-editor''; declare module ''monaco-editor/esm/vs/language/typescript/ts.worker?worker'' declare module ''monaco-editor/esm/vs/basic-languages/sql/sql.js''; declare module ''monaco-editor/esm/vs/basic-languages/yaml/yaml.js''; declare module ''monaco-editor/esm/vs/language/json/json.worker?worker'' declare module ''monaco-editor/esm/vs/language/css/css.worker?worker'' declare module ''monaco-editor/esm/vs/language/html/html.worker?worker'' declare module ''monaco-editor/esm/vs/editor/editor.worker?worker'' declare module ''monaco-editor/esm/vs/basic-languages/javascript/javascript.js''- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
打包报错的处理
yarn build 时会发生下述报错
- 1

我在这里看到了解决办法https://github.com/microsoft/monaco-editor/blob/main/docs/integrate-esm.md
在monacoEditor.vue组件,重写getWorker方法
<template>
<div
ref="codeEditBox"
class="codeEditBox"
:class="hightChange&&''codeEditBox1''"
/>
</template>
<script lang="ts">
import {
defineComponent, onBeforeUnmount, onMounted, ref, watch,
} from ''vue''
// 减去以下包
-- import JsonWorker from ''monaco-editor/esm/vs/language/json/json.worker?worker''
-- import CssWorker from ''monaco-editor/esm/vs/language/css/css.worker?worker''
-- import HtmlWorker from ''monaco-editor/esm/vs/language/html/html.worker?worker''
-- import TsWorker from ''monaco-editor/esm/vs/language/typescript/ts.worker?worker''
-- import EditorWorker from ''monaco-editor/esm/vs/editor/editor.worker?worker''
import * as monaco from ''monaco-editor''
import { language as sqlLanguage } from ''monaco-editor/esm/vs/basic-languages/sql/sql.js'';
import { language as yamlLanguage } from ''monaco-editor/esm/vs/basic-languages/yaml/yaml.js'';
import ''monaco-editor/esm/vs/basic-languages/sql/sql.contribution''
import ''monaco-editor/esm/vs/basic-languages/yaml/yaml.contribution''
import ''monaco-editor/esm/vs/basic-languages/javascript/javascript.contribution''
import { editorProps } from ''./monacoEditorType''
export default defineComponent({
name: ''MonacoEditor'',
props: editorProps,
emits: [''update:modelValue'', ''change'', ''editor-mounted''],
setup(props, { emit }) {
(self as any).MonacoEnvironment = {
++ getWorker: (_: string, label: string) => {
++ const getWorkerModule = (moduleUrl:string, label:string) => new Worker((self as any).MonacoEnvironment.getWorkerUrl(moduleUrl), {
++ name: label,
++ type: ''module'',
++ });
++ switch (label) {
++ case ''json'':
++ return getWorkerModule(''/monaco-editor/esm/vs/language/json/json.worker?worker'', label);
++ case ''css'':
++ case ''scss'':
++ case ''less'':
++ return getWorkerModule(''/monaco-editor/esm/vs/language/css/css.worker?worker'', label);
++ case ''html'':
++ case ''handlebars'':
++ case ''razor'':
++ return getWorkerModule(''/monaco-editor/esm/vs/language/html/html.worker?worker'', label);
++ case ''typescript'':
++ case ''javascript'':
++ return getWorkerModule(''/monaco-editor/esm/vs/language/typescript/ts.worker?worker'', label);
++ default:
++ return getWorkerModule(''/monaco-editor/esm/vs/editor/editor.worker?worker'', label);
++ }
++ },
// 原来的减去
-- getWorker(_: string, label: string) {
-- if (label === ''json'') {
-- return new JsonWorker()
-- }
-- if ([''css'', ''scss'', ''less''].includes(label)) {
-- return new CssWorker()
-- }
-- if ([''html'', ''handlebars'', ''razor''].includes(label)) {
-- return new HtmlWorker()
-- }
-- if ([''typescript'', ''javascript''].includes(label)) {
-- return new TsWorker()
-- }
-- return new EditorWorker()
-- },
}
let editor: any
const codeEditBox = ref()
const init = () => {
monaco.languages.typescript.javascriptDefaults.setDiagnosticsOptions({
noSemanticValidation: true,
noSyntaxValidation: false,
})
monaco.languages.typescript.javascriptDefaults.setCompilerOptions({
target: monaco.languages.typescript.ScriptTarget.ES2020,
allowNonTsExtensions: true,
})
monaco.languages.registerCompletionItemProvider(''sql'', {
provideCompletionItems() {
const suggestions:any = [];
// 这个keywords就是sql.js文件中有的
sqlLanguage.keywords.forEach((item:any) => {
suggestions.push({
label: item,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: item,
});
})
sqlLanguage.operators.forEach((item:any) => {
suggestions.push({
label: item,
kind: monaco.languages.CompletionItemKind.Operator,
insertText: item,
});
})
sqlLanguage.builtinFunctions.forEach((item:any) => {
suggestions.push({
label: item,
kind: monaco.languages.CompletionItemKind.Function,
insertText: item,
});
})
sqlLanguage.builtinVariables.forEach((item:any) => {
suggestions.push({
label: item,
kind: monaco.languages.CompletionItemKind.Variable,
insertText: item,
});
})
return {
// 最后要返回一个数组
suggestions,
};
},
})
monaco.languages.registerCompletionItemProvider(''yaml'', {
provideCompletionItems() {
const suggestions:any = [];
// 这个keywords就是python.js文件中有的
yamlLanguage.keywords.forEach((item:any) => {
suggestions.push({
label: item,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: item,
});
})
return {
// 最后要返回一个数组
suggestions,
};
},
})
editor = monaco.editor.create(codeEditBox.value, {
value: props.modelValue,
language: props.language,
readOnly: props.readOnly,
theme: props.theme,
...props.options,
})
// 监听值的变化
editor.onDidChangeModelContent(() => {
const value = editor.getValue() // 给父组件实时返回最新文本
emit(''update:modelValue'', value)
emit(''change'', value)
})
emit(''editor-mounted'', editor)
}
watch(
() => props.modelValue,
(newValue) => {
if (editor) {
const value = editor.getValue()
if (newValue !== value) {
editor.setValue(newValue)
}
}
},
)
watch(
() => props.options,
(newValue) => {
editor.updateOptions(newValue)
},
{ deep: true },
)
watch(
() => props.readOnly,
() => {
console.log(''props.readOnly'', props.readOnly)
editor.updateOptions({ readOnly: props.readOnly })
},
{ deep: true },
)
watch(
() => props.language,
(newValue) => {
monaco.editor.setModelLanguage(editor.getModel()!, newValue)
},
)
onBeforeUnmount(() => {
editor.dispose()
})
onMounted(() => {
init()
})
return { codeEditBox }
},
})
</script>
<style lang="scss" scoped>
.codeEditBox {
width: 100%;
flex: 1;
min-height: 100px;
// height: 200px;
overflow-y: auto;
}
.codeEditBox1{
height: calc(100% - 323px);
}
</style>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
- 217
- 218
- 219
