Monaco-editor 在Vue3当中使用姿势
项目介绍
项目环境:vue3、typescript、vite
monaco-editor
官网地址:microsoft.github.io/monaco-edit…
由于官网看起来晦涩难懂,指路官方文档看法指导:www.cnblogs.com/jdWu-d/p/16…
使用方式
-
安装:
pnpm install monaco-editor@0.34.0
-
main.js
当中: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' // @ts-ignore: worker self.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() } }
-
封装
VMonacoEditor
组件:<template> <div ref="codeEditBox" class="codeEditBox"></div> </template> <script lang="ts"> import { defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue' import { editorProps } from '../monacoEditorType' import * as monaco from 'monaco-editor' export default defineComponent({ name: 'MonacoEditor', props: editorProps, emits: ['update:modelValue', 'change', 'editor-mounted'], setup(props, { emit }) { let editor: monaco.editor.IStandaloneCodeEditor 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 }) editor = monaco.editor.create(codeEditBox.value, { value: props.modelValue, language: props.language, 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.language, newValue => { monaco.editor.setModelLanguage(editor.getModel()!, newValue) } ) onBeforeUnmount(() => { editor.dispose() }) onMounted(() => { init() }) return { codeEditBox } } }) </script> <style lang="scss" scoped> .codeEditBox { width: v-bind(width); height: v-bind(height); } </style>
-
类型定义文件:
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 // 显示行号 minimap?: { // 关闭小地图 enabled: boolean } readOnly: boolean // 只读 contextmenu: boolean fontSize?: number // 字体大小 scrollBeyondLastLine?: boolean // 取消代码后面一大段空白 overviewRulerBorder?: boolean // 不要滚动条的边框 } export const editorProps = { modelValue: { type: String as PropType<string>, default: null }, 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' }, theme: { type: String as PropType<Theme>, validator(value: string): boolean { return ['vs', 'hc-black', 'vs-dark'].includes(value) }, default: 'vs-dark' }, options: { type: Object as PropType<Options>, default() { return { automaticLayout: true, foldingStrategy: 'indentation', renderLineHighlight: 'all', selectOnLineNumbers: true, minimap: { enabled: true }, readOnly: false, contextmenu: true, fontSize: 16, scrollBeyondLastLine: false, overviewRulerBorder: false } } } }
-
项目中使用:
<script lang="ts" setup> import * as monaco from 'monaco-editor' const content = ref('{"text":"1"}') const language = ref('json') const editorMounted = (editor: monaco.editor.IStandaloneCodeEditor) => { console.log('editor实例加载完成', editor) } </script> <template> <VMonacoEditor v-model="content" :language="language" height="500px" style="margin-top: 40px" @editor-mounted="editorMounted" ></VMonacoEditor> </template>
diff效果的编辑器封装
<template>
<div
ref="container"
class="monaco-editor"
:style="`height: ${height}px`"
></div>
</template>
<script>
import * as monaco from 'monaco-editor'
export default {
name: 'AcMonaco',
props: {
opts: {
type: Object,
default() {
return {}
}
},
oldValue: {
type: String,
default: ''
},
newValue: {
type: String,
default: ''
},
height: {
type: Number,
default: 300
}
},
data() {
return {
// 主要配置
defaultOpts: {
value: '', // 编辑器的值
// theme: 'vs-dark', // 编辑器主题:vs, hc-black, or vs-dark,更多选择详见官网
roundedSelection: true, // 右侧不显示编辑器预览框
autoIndent: true // 自动缩进
},
// 编辑器对象
monacoEditor: {}
}
},
watch: {
opts: {
handler() {
this.init()
},
deep: true
}
},
mounted() {
this.init()
},
methods: {
init() {
// 初始化container的内容,销毁之前生成的编辑器
this.$refs.container.innerHTML = ''
// 生成编辑器配置
const editorOptions = Object.assign(this.defaultOpts, this.opts)
// 初始化编辑器实例
this.monacoDiffInstance = monaco.editor.createDiffEditor(
this.$refs['container'],
editorOptions
)
this.monacoDiffInstance.setModel({
// oldValue为以前的值
original: monaco.editor.createModel(
this.oldValue,
editorOptions.language
),
// oldValue为新的值
modified: monaco.editor.createModel(
this.newValue,
editorOptions.language
)
})
},
// 供父组件调用手动获取值
getVal() {
return this.monacoEditor.getValue()
}
}
}
</script>
//使用
<script setup lang="ts">
//VMonacoCompare
const opts = reactive({
readOnly: true,
contextmenu: false,
automaticLayout: true,
language: 'yaml'
})
const content = ref(''),
previousContent = ref(''),
language = ref('yaml')
</script>
<template>
<VMonacoCompare
:newValue="content"
:oldValue="previousContent"
:opts="opts"
:height="450"
></VMonacoCompare>
</template>
<style lang="scss" scoped>
</style>
主题颜色切换
主题配置库: github.com/brijeshb42/…
pnpm install monaco-editor-webpack-plugin
pnpm install monaco-themes
可以在仓库中选择一个自己喜欢的主题,这里以Dawn.json
为例,在本地新建dawn.ts
文件,将仓库当中的源码拷贝到ts文件中,例如:
export const Dawn = {
base: 'vs',
inherit: true,
rules: [
{
background: 'F9F9F9',
token: ''
},
{
foreground: '5a525f',
fontStyle: 'italic',
token: 'comment'
},
{
foreground: '811f24',
fontStyle: 'bold',
token: 'constant'
},
{
foreground: 'bf4f24',
token: 'entity'
},
{
foreground: '794938',
token: 'keyword'
},
{
foreground: 'a71d5d',
fontStyle: 'italic',
token: 'storage'
},
{
foreground: '0b6125',
token: 'string | punctuation.definition.string'
},
{
foreground: '691c97',
token: 'support'
},
{
foreground: '234a97',
token: 'variable'
},
{
foreground: '794938',
token: 'punctuation.separator'
},
{
foreground: 'b52a1d',
fontStyle: 'bold italic underline',
token: 'invalid.deprecated'
},
{
foreground: 'f8f8f8',
background: 'b52a1d',
fontStyle: 'italic underline',
token: 'invalid.illegal'
},
{
foreground: '080808',
background: '6f8bba26',
token: 'string source'
},
{
foreground: '696969',
fontStyle: 'bold',
token: 'string constant'
},
{
foreground: '234a97',
token: 'string variable'
},
{
foreground: 'cf5628',
token: 'string.regexp'
},
{
foreground: 'cf5628',
fontStyle: 'bold italic',
token: 'string.regexp.character-class'
},
{
foreground: 'cf5628',
fontStyle: 'bold italic',
token: 'string.regexp constant.character.escaped'
},
{
foreground: 'cf5628',
fontStyle: 'bold italic',
token: 'string.regexp source.ruby.embedded'
},
{
foreground: 'cf5628',
fontStyle: 'bold italic',
token: 'string.regexp string.regexp.arbitrary-repitition'
},
{
foreground: '811f24',
fontStyle: 'bold',
token: 'string.regexp constant.character.escape'
},
{
background: '6f8bba26',
token: 'text source'
},
{
foreground: '693a17',
token: 'support.function'
},
{
foreground: 'b4371f',
token: 'support.constant'
},
{
foreground: '234a97',
token: 'support.variable'
},
{
foreground: '693a17',
token: 'markup.list'
},
{
foreground: '19356d',
fontStyle: 'bold',
token: 'markup.heading | markup.heading entity.name'
},
{
foreground: '0b6125',
background: 'bbbbbb30',
fontStyle: 'italic',
token: 'markup.quote'
},
{
foreground: '080808',
fontStyle: 'italic',
token: 'markup.italic'
},
{
foreground: '080808',
fontStyle: 'bold',
token: 'markup.bold'
},
{
foreground: '080808',
fontStyle: 'underline',
token: 'markup.underline'
},
{
foreground: '234a97',
fontStyle: 'italic underline',
token: 'markup.link'
},
{
foreground: '234a97',
background: 'bbbbbb30',
token: 'markup.raw'
},
{
foreground: 'b52a1d',
token: 'markup.deleted'
},
{
foreground: '19356d',
background: 'dcdcdc',
fontStyle: 'bold',
token: 'meta.separator'
}
],
colors: {
'editor.foreground': '#080808',
'editor.background': '#F9F9F9',
'editor.selectionBackground': '#275FFF4D',
'editor.lineHighlightBackground': '#2463B41F',
'editorCursor.foreground': '#000000',
'editorWhitespace.foreground': '#4B4B7E80'
}
}
在组件中引入:
//import { Dawn } from '../dawn'
//monaco.editor.defineTheme('myTheme', Dawn)
//monaco.editor.setTheme('myTheme')
//完整代码如下:
<template>
<div ref="codeEditBox" class="codeEditBox"></div>
</template>
<script lang="ts">
import { defineComponent, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { editorProps } from '../monacoEditorType'
import * as monaco from 'monaco-editor'
import { Dawn } from '../dawn'
export default defineComponent({
name: 'MonacoEditor',
props: editorProps,
emits: ['update:modelValue', 'change', 'editor-mounted'],
setup(props, { emit }) {
let editor: monaco.editor.IStandaloneCodeEditor
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
})
//@ts-ignore
monaco.editor.defineTheme('myTheme', Dawn)
monaco.editor.setTheme('myTheme')
editor = monaco.editor.create(codeEditBox.value, {
value: props.modelValue,
language: props.language,
// 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.language,
newValue => {
monaco.editor.setModelLanguage(editor.getModel()!, newValue)
}
)
onBeforeUnmount(() => {
editor.dispose()
})
onMounted(() => {
init()
})
return { codeEditBox }
}
})
</script>
<style lang="scss" scoped>
.codeEditBox {
width: v-bind(width);
height: v-bind(height);
}
</style>
其它使用参考:blog.csdn.net/weixin_4444…