Monaco?Editor開發(fā)SQL代碼提示編輯器實例詳解
安裝
安裝依賴,這里請注意版本
yarn add monaco-editor@0.29.1 yarn add monaco-editor-webpack-plugin@5.0.0
配置 webpack 插件
// vue.config.js
...
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin')
module.export = {
...
configureWebpack: {
name: name,
resolve: {
alias: {
'@': resolve('src'),
},
},
plugins: [new MonacoWebpackPlugin()],
},
...
}
請注意 monaco-editor-webpack-plugin 和 monaco-editor 的對應關系,否則可能會出現(xiàn)無法運行的情況。
| monaco-editor-webpack-plugin | monaco-editor |
|---|---|
| 7.*.* | >= 0.31.0 |
| 6.*.* | 0.30.* |
| 5.*.* | 0.29.* |
| 4.*.* | 0.25.*, 0.26.*, 0.27.*, 0.28.* |
| 3.*.* | 0.22.*, 0.23.*, 0.24.* |
| 2.*.* | 0.21.* |
| 1.9.* | 0.20.* |
| 1.8.* | 0.19.* |
| 1.7.* | 0.18.* |
簡易 SQL 編輯器
先上干貨!
<template>
<div ref="codeContainer" class="editor-container" :style="{ height: height + 'px' }" />
</template>
<script>
import * as monaco from 'monaco-editor'
/**
* VS Code 編輯器
*
* 通過 getEditorVal 函數(shù)向外傳遞編輯器即時內(nèi)容
* 通過 initValue 用于初始化編輯器內(nèi)容。
* 編輯器默認 sql 語言,支持的語言請參考 node_modules\monaco-editor\esm\vs\basic-languages 目錄下~
* 編輯器樣式僅有 'vs', 'vs-dark', 'hc-black' 三種
*/
export default {
name: 'MonacoEditor',
props: {
initValue: {
type: String,
default: '',
},
readOnly: Boolean,
language: {
type: String,
default: 'sql',
},
height: {
type: Number,
default: 300,
},
theme: {
type: String,
default: 'vs',
},
},
data() {
return {
monacoEditor: null, // 語言編輯器
}
},
computed: {
inputVal() {
return this.monacoEditor?.getValue()
},
},
watch: {
inputVal() {
if (this.monacoEditor) {
this.$emit('change', this.monacoEditor.getValue())
}
},
theme() {
this.setTheme(this.theme)
},
height() {
this.layout()
},
},
mounted() {
this.initEditor()
},
beforeDestroy() {
if (this.monacoEditor) {
this.monacoEditor.dispose()
}
},
methods: {
initEditor() {
if (this.$refs.codeContainer) {
this.registerCompletion()
// 初始化編輯器,確保dom已經(jīng)渲染
this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, {
value: '', // 編輯器初始顯示文字
language: 'sql', // 語言
readOnly: this.readOnly, // 是否只讀 Defaults to false | true
automaticLayout: true, // 自動布局
theme: this.theme, // 官方自帶三種主題vs, hc-black, or vs-dark
minimap: {
// 關閉小地圖
enabled: false,
},
tabSize: 2, // tab縮進長度
})
}
this.setInitValue()
},
focus() {
this.monacoEditor.focus()
},
layout() {
this.monacoEditor.layout()
},
getValue() {
return this.monacoEditor.getValue()
},
// 將 initValue Property 同步到編輯器中
setInitValue() {
this.monacoEditor.setValue(this.initValue)
},
setTheme() {
monaco.editor.setTheme(this.theme)
},
getSelectionVal() {
const selection = this.monacoEditor.getSelection() // 獲取光標選中的值
const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
const model = this.monacoEditor.getModel()
return model.getValueInRange({
startLineNumber,
startColumn,
endLineNumber,
endColumn,
})
},
setPosition(column, lineNumber) {
this.monacoEditor.setPosition({ column, lineNumber })
},
getPosition() {
return this.monacoEditor.getPosition()
},
},
}
</script>
<style lang="scss" scoped></style>
相關功能
獲取選中代碼
getSelectionVal() {
const selection = this.monacoEditor.getSelection() // 獲取光標選中的值
const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
const model = this.monacoEditor.getModel()
return model.getValueInRange({
startLineNumber,
startColumn,
endLineNumber,
endColumn,
})
},
替換選中代碼
insertStringInTemplate(str) {
const selection = this.monacoEditor.getSelection() // 獲取光標選中的值
const { startLineNumber, endLineNumber, startColumn, endColumn } = selection
const model = this.monacoEditor.getModel()
const textBeforeSelection = model.getValueInRange({
startLineNumber: 1,
startColumn: 0,
endLineNumber: startLineNumber,
endColumn: startColumn,
})
const textAfterSelection = model.getValueInRange({
startLineNumber: endLineNumber,
startColumn: endColumn,
endLineNumber: model.getLineCount(),
endColumn: model.getLineMaxColumn(model.getLineCount()),
})
this.monacoEditor.setValue(textBeforeSelection + str + textAfterSelection)
this.monacoEditor.focus()
this.monacoEditor.setPosition({
lineNumber: startLineNumber,
column: startColumn + str.length,
})
},
處理光標位置
setPosition(column, lineNumber) {
this.monacoEditor.setPosition({ column, lineNumber })
},
getPosition() {
return this.monacoEditor.getPosition()
},
自定義 SQL 庫表提示,并保留原有 SQL 提示
首先由后端提供具體的庫表信息:
export const hintData = {
adbs: ['dim_realtime_recharge_paycfg_range', 'dim_realtime_recharge_range'],
dimi: ['ads_adid', 'ads_spec_adid_category'],
}
然后根據(jù)已有庫表信息進行自定義 AutoComplete
import * as monaco from 'monaco-editor'
import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'
const { keywords } = language
export default {
...
mounted() {
this.initEditor()
},
methods: {
...
registerCompletion() {
const _that = this
monaco.languages.registerCompletionItemProvider('sql', {
triggerCharacters: ['.', ...keywords],
provideCompletionItems: (model, position) => {
let suggestions = []
const { lineNumber, column } = position
const textBeforePointer = model.getValueInRange({
startLineNumber: lineNumber,
startColumn: 0,
endLineNumber: lineNumber,
endColumn: column,
})
const tokens = textBeforePointer.trim().split(/\s+/)
const lastToken = tokens[tokens.length - 1] // 獲取最后一段非空字符串
if (lastToken.endsWith('.')) {
const tokenNoDot = lastToken.slice(0, lastToken.length - 1)
if (Object.keys(_that.hintData).includes(tokenNoDot)) {
suggestions = [..._that.getTableSuggest(tokenNoDot)]
}
} else if (lastToken === '.') {
suggestions = []
} else {
suggestions = [..._that.getDBSuggest(), ..._that.getSQLSuggest()]
}
return {
suggestions,
}
},
})
},
// 獲取 SQL 語法提示
getSQLSuggest() {
return keywords.map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Enum,
insertText: key,
}))
},
getDBSuggest() {
return Object.keys(this.hintData).map((key) => ({
label: key,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: key,
}))
},
getTableSuggest(dbName) {
const tableNames = this.hintData[dbName]
if (!tableNames) {
return []
}
return tableNames.map((name) => ({
label: name,
kind: monaco.languages.CompletionItemKind.Constant,
insertText: name,
}))
},
initEditor() {
if (this.$refs.codeContainer) {
this.registerCompletion()
// 初始化編輯器,確保dom已經(jīng)渲染
this.monacoEditor = monaco.editor.create(this.$refs.codeContainer, {
value: '', // 編輯器初始顯示文字
language: 'sql', // 語言
readOnly: this.readOnly, // 是否只讀 Defaults to false | true
automaticLayout: true, // 自動布局
theme: this.theme, // 官方自帶三種主題vs, hc-black, or vs-dark
minimap: {
// 關閉小地圖
enabled: false,
},
tabSize: 2, // tab縮進長度
})
}
this.setValue(this.value)
},
}
}
編輯器 resize
resize() {
this.monacoEditor.layout()
},
編輯器設置主題
注意!設置主題并非在編輯器實例上修改的哦!
setTheme() {
monaco.editor.setTheme(this.theme)
},
SQL 代碼格式化
編輯器自身不支持 sql 格式化(試了下 JavaScript 是支持的),所以用到了 sql-formatter 這個庫。
import { format } from 'sql-formatter'
...
format() {
this.monacoEditor.setValue(
format(this.monacoEditor.getValue(), {
indentStyle: 'tabularLeft',
}),
)
},
...
右鍵菜單漢化
需要安裝以下兩個庫
npm install monaco-editor-nls --save npm install monaco-editor-esm-webpack-plugin --save-dev
具體用法可以直接去 www.npmjs.com/package/mon… 里面看,我就不搬運了~
記得銷毀編輯器對象哦
beforeDestroy() {
if (this.monacoEditor) {
this.monacoEditor.dispose()
}
},
踩坑
下面是我遇到的幾個坑。
- 最新版本的 Monaco Editor 已經(jīng)使用了 ES2022 的語法,所以老項目可能會出現(xiàn)編譯不過的問題。所以我把版本調(diào)低了一些。
- 在最初調(diào)試編輯器的時候出現(xiàn)了無法編輯的情況,后來發(fā)現(xiàn)是同事用到了 default-passive-events 這個庫來關閉 chrome 的 Added non-passive event listener to a scroll-blocking <some> event. Consider marking event handler as 'passive' to make the page more responsive 警告。結果攔截一些 event。
如何快速去看懂 Monaco Editor
一開始我看它的官方文檔是非常懵的,各種接口、函數(shù)、對象的定義,完全不像是個前端庫那么好理解。鼓搗了好久才慢慢找到門路。
- 先看示例
- 查看它的 playground,上面其實是有一些功能可以直接找到的。
- 查看它在 github 上的 /samples 目錄,里面也有不少示例。
- 去掘金這類網(wǎng)站上找別人寫的示例,能有不少啟發(fā)。
- 再看 API
- 了解了自己所需要的功能相關的代碼,再去看它文檔的 API 就會發(fā)現(xiàn)容易理解多了。逐步發(fā)散理解更多關聯(lián)功能。
參考資料
- 官方文檔
microsoft.github.io/monaco-edit…
相關庫
Monaco Editor www.npmjs.com/package/mon…
右鍵菜單漢化 www.npmjs.com/package/mon…
webpack 插件 www.npmjs.com/package/mon…
漢化 webpack 插件 www.npmjs.com/package/mon…
SQL 代碼格式化 www.npmjs.com/package/sql…
博客
http://www.dbjr.com.cn/article/258307.htm
http://www.dbjr.com.cn/article/258269.htm
以上就是Monaco Editor開發(fā)SQL編輯器實例詳解的詳細內(nèi)容,更多關于Monaco Editor開發(fā)SQL編輯器的資料請關注腳本之家其它相關文章!
相關文章
vue3中watch和watchEffect實戰(zhàn)梳理
這篇文章主要介紹了vue3中watch和watchEffect實戰(zhàn)梳理,watch和watchEffect都是vue3中的監(jiān)聽器,但是在寫法和使用上是有區(qū)別的。下文介紹他們之間的方法及區(qū)別,需要的朋友可以參考一下2022-07-07
vue3+ElementPlus使用lang="ts"報Unexpected?token錯誤的解決
最近開發(fā)中遇到了些問題,跟大家分享下,這篇文章主要給大家介紹了關于vue3+ElementPlus使用lang="ts"報Unexpected?token錯誤的解決辦法,需要的朋友可以參考下2023-01-01
Vue CLI項目 axios模塊前后端交互的使用(類似ajax提交)
這篇文章主要介紹了Vue-CLI項目-axios模塊前后端交互的使用詳解(類似ajax提交),本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-09-09

