二、配置vite.config.js
import { defineConfig } from 'vite'
import monacoEditorPlugin from "vite-plugin-monaco-editor"
export default defineConfig({
plugins: [
monacoEditorPlugin()
// 全局导入
import * as monaco from 'monaco-editor'
// 局部导入需要的功能和依赖
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
2.封装组件
<template>
<div id="code"></div>
</template>
<script setup>
import * as monaco from 'monaco-editor/esm/vs/editor/edcore.main'
import 'monaco-editor/esm/vs/basic-languages/sql/sql.contribution'
// import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'
// language.keywords插件自带关键词的不全,网上找了一份自己维护
import sqlKeywords from './sqlKeywords.js'
const props = defineProps({
database: {
type: String,
default: ''
const editor = ref(null)
const initEditor = () => {
// 初始化编辑器,确保dom已经渲染
editor.value = monaco.editor.create(document.getElementById('code'), {
//初始化配置
value: props.value,
theme: 'vs-dark',
autoIndex: true,
language: 'sql', // 语言类型
tabCompletion: 'on',
cursorSmoothCaretAnimation: true,
formatOnPaste: true,
mouseWheelZoom: true,
folding: true, //代码折叠
autoClosingBrackets: 'always',
autoClosingOvertype: 'always',
autoClosingQuotes: 'always',
automaticLayout: 'always'
// 父组件获取值
const handleValue = () => {
return toRaw(editor.value).getValue()
// 父组件设置值
const setValue = (content) => {
toRaw(editor.value).setValue(content)
onMounted(() => {
initEditor()
defineExpose({
handleValue,
setValue
</script>
自定义提示
const suggestion = ref(null)
// 数据库对应的表名
const hintData = reactive({
a: ['group', 'area'],
b: ['user', 'client']
// 表对应的字段名
const tableData = reactive({
user: ['age', 'gender'],
group: ['id', 'name']
// 关键字提示
const getSQLSuggest = () => {
return sqlKeywords.map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Keyword,
insertText: key,
detail: 'keyword'
// 表名提示
const getTableSuggest = (dbName) => {
const tableNames = hintData[dbName]
if (!tableNames) {
return []
return tableNames.map((name) => ({
label: name,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: name,
detail: dbName
// 字段名提示
const getParamSuggest = (tableName) => {
const params = tableData[tableName]
if (!params) {
return []
return params.map((name) => ({
label: name,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: name,
detail: 'param'
// 数据库名提示
const getDBSuggest = () => {
return Object.keys(hintData).map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Enum,
insertText: key,
detail: 'database'
const initEditor = () => {
suggestion.value = monaco.languages.registerCompletionItemProvider('sql', {
// 触发条件,也可以不写,不写的话只要输入满足label就会提示
// 只能配置单字符
triggerCharacters: ['.', ' '],
provideCompletionItems: (model, position) => {
let suggestions = []
const { lineNumber, column } = position
const textBeforePointer = model.getValueInRange({
startLineNumber: lineNumber,
startColumn: 0,
endLineNumber: lineNumber,
endColumn: column,
const tokens = textBeforePointer.toLocaleLowerCase().trim().split(/\s+/)
const lastToken = tokens[tokens.length - 1] // 获取最后一段非空字符串
const word = model.getWordUntilPosition(position)
const range = {
startLineNumber: position.lineNumber,
endLineNumber: position.lineNumber,
startColumn: word.startColumn,
endColumn: word.endColumn
if (lastToken.endsWith('.')) {
// 提示该数据库下的表名
const tokenNoDot = lastToken.slice(0, lastToken.length - 1)
if (Object.keys(hintData).includes(tokenNoDot)) {
suggestions = [...getTableSuggest(tokenNoDot)]
} else if (lastToken === '.') {
suggestions = []
} else if (textBeforePointer.endsWith(' ')) {
if (textBeforePointer.endsWith('select * from ')) {
// select * from 提示指定数据库的表名
suggestions = getTableSuggest(props.database)
} else if (lastToken === 'where') {
// select * from tableName where 提示指定表的字段名
const lastToken2 = tokens[tokens.length - 2]
const lastToken3 = tokens[tokens.length - 3]
const lastToken4 = tokens[tokens.length - 4]
const lastToken5 = tokens[tokens.length - 5]
if (lastToken5 + lastToken4 + lastToken3 === 'select*from') {
suggestions = [...getParamSuggest(lastToken2)]
} else {
suggestions = []
}else {
suggestions = []
} else {
// 提示数据库名和关键词
suggestions = [...getDBSuggest(), ...getSQLSuggest()]
return {
suggestions
reg += '/'
color.value = monaco.languages.setMonarchTokensProvider('sql', {
ignoreCase: true,
tokenizer: {
root: [
{ token: 'keyword' },
], //蓝色
/[+]|[-]|[*]|[/]|[%]|[>]|[<]|[=]|[!]|[:]|[&&]|[||]/,
{ token: 'string' },
], //红色
[/'.*?'|".*?"/, { token: 'string.escape' }], //橙色
[/#--.*?\--#/, { token: 'comment' }], //绿色
[/null/, { token: 'regexp' }], //粉色
[/[{]|[}]/, { token: 'type' }], //青色
[/[\u4e00-\u9fa5]/, { token: 'predefined' }],//亮粉色
[/''/, { token: 'invalid' }],//红色
[/[\u4e00-\u9fa5]/, { token: 'number.binary' }],//浅绿
[/(?!.*[a-zA-Z])[0-9]/, { token: 'number.hex' }], //浅绿
[/[(]|[)]/, { token: 'number.octal' }], //浅绿
[/[\u4e00-\u9fa5]/, { token: 'number.float' }],//浅绿
格式化代码&标记错误
yarn add sql-formatter -D
import { format } from 'sql-formatter'
const initEditor = () => {
// 改写插件自带格式化功能
formatProvider.value = monaco.languages.registerDocumentFormattingEditProvider('sql', {
provideDocumentFormattingEdits(model) {
return [{
text: formatSql(1),
range: model.getFullModelRange()
// 格式化代码
const formatSql = (needValue) => {
clearMistake()
try {
setValue(format(toRaw(editor.value).getValue()))
} catch (e) {
const {message} = e
const list = message.split(' ')
const line = list.indexOf('line')
const column = list.indexOf('column')
markMistake({
startLineNumber: Number(list[line + 1]),
endLineNumber: Number(list[line + 1]),
startColumn: Number(list[column + 1]),
endColumn: Number(list[column + 1])
}, 'Error', message)
if (needValue) {
return toRaw(editor.value).getValue()
// 标记错误信息
const markMistake = (range, type, message) => {
const {startLineNumber, endLineNumber, startColumn, endColumn} = range
monaco.editor.setModelMarkers(
toRaw(editor.value).getModel(),
'eslint',
startLineNumber,
endLineNumber,
startColumn,
endColumn,
severity: monaco.MarkerSeverity[type], // type可以是Error,Warning,Info
message
// 清除错误信息
const clearMistake = () => {
monaco.editor.setModelMarkers(
toRaw(editor.value).getModel(),
'eslint',
const initEditor = () => {
toRaw(editor.value).onDidChangeModelContent(() => {
console.log('value', toRaw(editor.value).getValue())
销毁编辑器及其配置,防止自定义提示数据重复
onBeforeUnmount(() => {
if (editor.value) {
clearMistake()
toRaw(editor.value).dispose()
toRaw(color.value).dispose()
toRaw(suggestion.value).dispose()
toRaw(formatProvider.value).dispose()
Monaco Editor是一个非常强大的代码编辑器,它支持自定义代码补全。下面是在Vue中配置自定义代码补全的步骤:
1. 安装monaco-editor和monaco-editor-webpack-plugin
npm install monaco-editor monaco-editor-webpack-plugin --save-dev
2. 在vue.config.js中添加配置:
const MonacoEditorPlugin = require('monaco-editor-webpack-plugin');
module.exports = {
configureWebpack: {
plugins: [
new MonacoEditorPlugin({
// 你的语言模型文件路径
languages: ['path/to/your/language-model'],
3. 将monaco-editor组件添加到你的Vue组件:
<template>
<monaco-editor :language="language" :options="editorOptions" v-model="code" />
</template>
<script>
import * as monaco from 'monaco-editor';
export default {
name: 'MyComponent',
data() {
return {
code: '',
language: 'javascript',
editorOptions: {
// 自定义代码补全
suggestOnTriggerCharacters: true,
suggest: {
customProvider: (model, position) => {
// 这里是你的代码补全逻辑
return {
suggestions: [
label: 'console.log',
kind: monaco.languages.CompletionItemKind.Function,
documentation: 'Log output to console',
insertText: 'console.log(${1:object})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
range: new monaco.Range(position.lineNumber, 1, position.lineNumber, position.column),
</script>
这里的suggest.customProvider是自定义代码补全的逻辑,你可以根据自己的需要进行改变。另外,你需要提供你的语言模型文件路径。