vue3+vite+ts使用monaco-editor編輯器的簡單步驟
前言
近期要完成一個代碼編輯器的內(nèi)容,用的vue3.0+ts+vite架構(gòu),學(xué)習(xí)尚淺, 常在插件上遇坑
特此記錄下在monaco-editor的使用
需求:yaml和sql的文件的高亮、補全


實現(xiàn)
1.安裝
// ^0.34.1 yarn add monaco-editor
2.在vite.config.js中配置(如果不需要ts\js\html就不需要這么做)
// 強制預(yù)構(gòu)建插件包
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`
],
},
3.組件封裝與使用
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 {
// 最后要返回一個數(shù)組
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 {
// 最后要返回一個數(shù)組
suggestions,
};
},
})
editor = monaco.editor.create(codeEditBox.value, {
value: props.modelValue,
language: props.language,
readOnly: props.readOnly,
theme: props.theme,
...props.options,
})
// 監(jiān)聽值的變化
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>
4.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 // 自適應(yīng)布局
foldingStrategy: FoldingStrategy // 折疊方式 auto | indentation
renderLineHighlight: RenderLineHighlight // 行亮
selectOnLineNumbers: boolean // 顯示行號
placeholder:string
minimap: {
// 關(guān)閉小地圖
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: {
// 關(guān)閉小地圖
enabled: false,
},
placeholder: 'ss',
// readOnly: false, // 只讀
fontSize: 16, // 字體大小
scrollBeyondLastLine: false, // 取消代碼后面一大段空白
overviewRulerBorder: false, // 不要滾動條的邊框
}
},
},
}
5.在父組件中使用
<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)
}
value:編輯器代碼顯示的值
language:要加載的語言類型
hightChange:改變編輯器的高度(可去掉,我這邊有個sql查表的實現(xiàn),需要在編輯區(qū)下面加一個sql查詢的表格,所以需要這個參數(shù))
read-only:編輯區(qū)是否是只讀的,當(dāng)左側(cè)文件樹為空時沒有文件,編輯區(qū)不可寫
editorMounted:加載完成操作
//記得在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'
打包報錯的處理
yarn build 時會發(fā)生下述報錯

我在這里看到了解決辦法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 {
// 最后要返回一個數(shù)組
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 {
// 最后要返回一個數(shù)組
suggestions,
};
},
})
editor = monaco.editor.create(codeEditBox.value, {
value: props.modelValue,
language: props.language,
readOnly: props.readOnly,
theme: props.theme,
...props.options,
})
// 監(jiān)聽值的變化
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>

總結(jié)
到此這篇關(guān)于vue3+vite+ts使用monaco-editor編輯器的文章就介紹到這了,更多相關(guān)vue3+vite+ts使用monaco-editor內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Vue3中的toRef和toRefs的區(qū)別和用法示例小結(jié)
toRef和toRefs可以用來復(fù)制reactive里面的屬性然后轉(zhuǎn)成 ref,它保留了響應(yīng)式,也保留了引用,也就是你從 reactive 復(fù)制過來的屬性進行修改后,除了視圖會更新,原有 ractive 里面對應(yīng)的值也會跟著更新,本文介紹Vue3中toRef和toRefs的區(qū)別和用法,需要的朋友可以參考下2024-08-08
使用Vue3和Pinia實現(xiàn)網(wǎng)頁刷新功能
在現(xiàn)代 Web 開發(fā)中,保持用戶界面的動態(tài)性和響應(yīng)性至關(guān)重要,當(dāng)用戶觸發(fā)某些操作時,例如點擊按鈕或者完成表單提交,我們往往需要刷新頁面的一部分來展示最新的數(shù)據(jù),本文將介紹如何使用 Vue 3 和 Pinia 來實現(xiàn)這一功能,需要的朋友可以參考下2024-08-08
vue自定義橫向滾動條css導(dǎo)航兩行排列布局實現(xiàn)示例
這篇文章主要為大家介紹了vue自定義橫向滾動條css導(dǎo)航兩行排列布局實現(xiàn)示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08
vue中radio單選框如何實現(xiàn)取消選中狀態(tài)問題
這篇文章主要介紹了vue中radio單選框如何實現(xiàn)取消選中狀態(tài)問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-05-05

