概述
MonacoEdit 是微软提供的在线编辑器库,vscode 就是基于这个实现,现在实现将 MonacoEdit 集成到 vue3 项目中
方案采用 vite + vue3
实现方法
首先初始化一个 vue3 项目
npm init @vitejs/app editor-proj --template vue
创建完得到一个如下目录结构的项目
editor-proj
├─ public
│ └─ favicon.ico
├─ src
│ ├─ assets
│ │ └─ logo.png
│ ├─ components
│ │ └─ HelloWorld.vue
│ ├─ App.vue
│ └─ main.js
├─ index.html
├─ package.json
└─ vite.config.js
接下来安装 monaco-editor 依赖
yarn add monaco-editor
实现一个 json 编辑器组件并在 App.vue 中引用,最终目录结构如下
editor-proj
├─ public
│ └─ favicon.ico
├─ src
│ ├─ assets
│ │ └─ logo.png
│ ├─ components
│ │ └─ JsonEditor.vue
│ ├─ App.vue
│ └─ main.js
├─ index.html
├─ package.json
├─ vite.config.js
└─ yarn.lock
其中 JsonEditor.vue 的实现如下
<template>
<div class="editor" ref="dom"></div>
</template>
<script setup>
import { onMounted, defineProps, defineEmit, ref } from 'vue';
import * as monaco from 'monaco-editor';
const props = defineProps({
modelValue: String,
});
const emit = defineEmit(['update:modelValue']);
const dom = ref();
let instance;
onMounted(() => {
const jsonModel = monaco.editor.createModel(
props.modelValue,
'json',
monaco.Uri.parse('json://grid/settings.json')
);
instance = monaco.editor.create(dom.value, {
model: jsonModel,
tabSize: 2,
automaticLayout: true,
scrollBeyondLastLine: false,
});
instance.onDidChangeModelContent(() => {
const value = instance.getValue();
emit('update:modelValue', value);
});
});
</script>
<style scoped>
.editor {
height: 100%;
}
</style>
App.vue 的引用方法如下
<template>
<div class="container">
<JsonEditor v-model="code"></JsonEditor>
</div>
</template>
<script setup>
import { ref } from 'vue';
import JsonEditor from './components/JsonEditor.vue';
const code = ref('');
</script>
<style scoped>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
}
.container {
position: fixed;
height: 100%;
width: 100%;
}
</style>
这时候运行项目 yarn run dev
已经可以看到编辑器效果
但是存在一些问题,输入的 json 数据没法格式化,而且浏览器报错
Error: Unexpected usage
at EditorSimpleWorker.loadForeignModule (editorSimpleWorker.js:454)
at webWorker.js:38
这是由于 monaco-editor 的实现是基于 Web Worker 实现的,编辑器对语法的处理是通过开启 Worker 异步处理来提高效率,这时候还没有加载任何用来处理语法的 Worker
问题以及解决方案可以参考 github.com/vitejs/vite…
monaco-editor 会去获取全局变量里的 MonacoEnvironment
对象并执行 getWorker
getWorkerUrl
来实现加载对应的语法处理
json 数据的语法处理,修改 JsonEditor.vue 文件,加入如下实现
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
self.MonacoEnvironment = {
getWorker(workerId, label) {
if (label === 'json') {
return new JsonWorker();
}
return new EditorWorker();
},
};
最终 JsonEditor.vue 的实现修改如下
<template>
<div class="editor" ref="dom"></div>
</template>
<script setup>
import { onMounted, defineProps, defineEmit, ref } from 'vue';
import * as monaco from 'monaco-editor';
import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
self.MonacoEnvironment = {
getWorker(workerId, label) {
if (label === 'json') {
return new JsonWorker();
}
return new EditorWorker();
},
};
const props = defineProps({
modelValue: String,
});
const emit = defineEmit(['update:modelValue']);
const dom = ref();
let instance;
onMounted(() => {
const jsonModel = monaco.editor.createModel(props.modelValue, 'json');
instance = monaco.editor.create(dom.value, {
model: jsonModel,
tabSize: 2,
automaticLayout: true,
scrollBeyondLastLine: false,
});
instance.onDidChangeModelContent(() => {
const value = instance.getValue();
emit('update:modelValue', value);
});
});
</script>
<style scoped>
.editor {
height: 100%;
}
</style>
再次运行项目时发现已经可以正常处理 json 数据,包括快捷键格式化都正常运行
如果想支持更多的语法只要引用相应 Worker
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
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';
填坑
项目在开发时可以运行,但是构建发布之后发现又出现异常
Uncaught ReferenceError: window is not defined
at editor.worker.bfe8d272.js:1
Error: Unexpected usage
at xm.loadForeignModule (editor.worker.bfe8d272.js:1)
at editor.worker.bfe8d272.js:1
之前提到过,编辑器使用了 webworker 而在 webworker 里无法获取 window 和 document 对象,分析可能是打包的时候将功能独立的代码打包到一起了,现在没时间去研究如何解决,给出一个折中的解决方案
使用 @monaco-editor/loader
这个包,这个包的作用是直接加载 monaco-editor 相关库的 cdn 或远程版本,修改构建和实现如下
vite.config.js 里复制编辑器的 min 版本到输出目录
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import jsx from '@vitejs/plugin-vue-jsx'
import copy from 'rollup-plugin-copy'
export default defineConfig({
build: {
rollupOptions: {
plugins: [
copy({
targets: [
{
src: 'node_modules/monaco-editor/min/vs/**/*',
dest: 'dist/assets/monaco-editor/vs',
},
],
hook: 'writeBundle',
}),
],
},
},
plugins: [vue(), jsx()],
})
JsonEditor 的实现里使用 @monaco-editor/loader
加载编辑器,加载的路径就是前面设置的输出目录
<template>
<div class="editor" ref="dom"></div>
</template>
<script setup>
import { onMounted, defineProps, defineEmit, ref } from 'vue';
import { editor } from 'monaco-editor';
import loader from '@monaco-editor/loader'
if (import.meta.env.PROD) {
// 从静态资源加载编辑器
loader.config({ paths: { vs: '/assets/monaco-editor/vs' } })
}
const props = defineProps({
modelValue: String,
});
const emit = defineEmit(['update:modelValue']);
const dom = ref();
let instance: editor.IStandaloneCodeEditor;
onMounted(async () => {
const monaco = await loader.init()
instance = monaco.editor.create(dom.value, {
model: monaco.editor.createModel(props.modelValue, 'json'),
tabSize: 2,
automaticLayout: true,
scrollBeyondLastLine: false,
})
instance?.onDidChangeModelContent(() => {
ctx.emit('update:modelValue', instance?.getValue())
})
});
</script>
<style scoped>
.editor {
height: 100%;
}
</style>
在本地调试时候 @monaco-editor/loader
会加载 cdn 的资源,编译之后加载的是本地资源
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!