monaco-editor瀏覽器中的網(wǎng)頁代碼編輯器在項目中集成

瀏覽器中的編輯器

我們已經(jīng)習(xí)慣了使用編輯器來編寫代碼,作為一名程序員無論選擇什么編輯器來開發(fā),都是為了提高我們的工作效率,以及擼代碼爽的程度。

作為一名前端開發(fā)人員,有時候難免會遇到需要在瀏覽器中書寫代碼的場景。無論是用戶需要還是我們自己為了開發(fā)便利,這都將是有益的。

在網(wǎng)頁中編輯很容易,但是能不能讓我們像在本地編輯器中那樣編寫代碼,提供豐富的功能呢?目前有很多個開源項目針對此問題實現(xiàn)了各自的一套解決方案。在此就以monaco-editor集成到vue項目中為例,為大家解決此類問題,以及期望能夠幫助大家快速入手實踐。

擴(kuò)展功能

如上圖所示:這是在網(wǎng)頁中編輯代碼的效果,沒錯,這不是在vscode中,就是瀏覽器中的頁面上。不但提供了代碼高亮、代碼提示、代碼補(bǔ)全、搜索、替換等功能,還有右鍵格式化等其他功能,以及命令面板的功能等??梢哉f是能力豐富、功能強(qiáng)大。下面就逐步說明從開始到應(yīng)用的集成步驟。

一、安裝

我一共用到了五個npm包:

npminstallmonaco-editor -Snpminstallmonaco-editor-nls -Snpminstallmonaco-editor-webpack-plugin-Dnpminstallmonaco-editor-esm-webpack-plugin-Dnpminstalljs-beautify -S

下面來對這五個包做一下簡要的說明:

monaco-editor:是網(wǎng)頁編輯器的核心包,整體非常大,因為支持了很多的語言與很多的擴(kuò)展功能。

monaco-editor-webpack-plugin:因為monaco-editor直接單獨(dú)引入的情況下所支持的基本使用不能滿足我們的需求,我們還需要支持智能提示等功能,所以需要額外單獨(dú)做一些配置操作,雖然官網(wǎng)文檔說明已經(jīng)很清晰,但是配置起來還是不免比較繁瑣,因此提供了這個webpack插件來幫助我們自動處理這些事情,簡化我們的操作。

monaco-editor-nls:是對整個編輯器的漢化處理,如果需要進(jìn)行漢化,那么需要安裝此包。

monaco-editor-esm-webpack-plugin:針對漢化包所做的webpack插件,需要和漢化包配合使用。

js-beautify:是用來做代碼美化的,主要是做一些格式化的工作。

安裝完成之后,就可以配置到我們的項目中進(jìn)行應(yīng)用啦。

二、配置

vue.config.js:首先要在這個文件中引入插件,并設(shè)置相應(yīng)的配置項。

vue.config.js配置

首先將插件引入進(jìn)來。

const?MonacoWebpackPlugin?=?require('monaco-editor-esm-webpack-plugin')

然后設(shè)置對應(yīng)的屬性。

module: {rules: [? ? {test: /\.js/,enforce:'pre',include: /node_modules[\\\/]monaco-editor[\\\/]esm/,use: MonacoWebpackPlugin.loader? ? }? ]},plugins: [? new MonacoWebpackPlugin({languages: ['javascript','typescript','html','css','json']? })]

其中l(wèi)anguages指定要支持的語言,因為完整版支持的語言非常多,如果使用默認(rèn)的配置,那么包會非常的大,因此只選擇我們需要的語言。其中javascript和typescript必須同時出現(xiàn)。根據(jù)官網(wǎng)的說法是:由于是使用web worker的方式進(jìn)行實現(xiàn)的,每個web worker負(fù)責(zé)的工作內(nèi)容不同,某些語言可能會共用同一個webwork,但是有一些語言需要額外引入其他語言的支持來負(fù)責(zé)實例化該語言的共享工作進(jìn)程。如下:

需要額外的語言支持的部分語言

即:要引入language語言,必須引入instantiator。

另外還有一個跟languages同級的features屬性來精確的配置要使用的特性。這里默認(rèn)使用全部。

項目vue文件:在自己的項目vue文件中,引入monaco-editro及其他相關(guān)項。

constbeautify =require('js-beautify')import{ setLocaleData }from'monaco-editor-nls'importzh_CNfrom'monaco-editor-nls/locale/zh-hans'setLocaleData(zh_CN)// import * as monaco from 'monaco-editor'constmonaco =require('monaco-editor')constbeautify_js = beautify.jsconstbeautify_css = beautify.cssconstbeautify_html = beautify.html

其中:

const?beautify?=?require('js-beautify')

引入美化代碼工具,可分別對js、css、html代碼進(jìn)行格式化。

import?{?setLocaleData?}?from?'monaco-editor-nls'

import?zh_CN?from?'monaco-editor-nls/locale/zh-hans'

setLocaleData(zh_CN)

完成monaco-editor的漢化操作。

const?monaco?=?require('monaco-editor')

引入編輯器核心包,注意:如果引入了漢化包,那么必須要用require的形式引入該項,否則可以使用import的形式。而且必須要在漢化完成之后再引入,不然漢化將不生效。

在template中設(shè)定編輯器元素,作為根元素來渲染整個編輯器,注意該元素不能有子元素存在。

editorBox = monaco.editor.create(document.getElementById("monaco-editor"), {value:"let a = 1;const b = 2",language:"javascript",? theme:"vs-dark",? tabSize:2})

實例化編輯器,此時可以在頁面中進(jìn)行使用。第一個參數(shù)為要掛載的元素,第二個參數(shù)為配置選項。

value:編輯器的內(nèi)容。

language:編輯器的當(dāng)前實例所支持的語言。

theme:編輯器的主題樣式,支持vs、vs-dark、hc-black三種。

tabSize:表示縮進(jìn)的距離。

更多配置項可以查閱官網(wǎng)進(jìn)行設(shè)置。

到此為止我們就可以在網(wǎng)頁中編輯代碼啦!

三、高級功能

右鍵:

editorBox.addAction({id:'formateCodeForce',? label:'強(qiáng)制格式化',? keybindings: [monaco.KeyMod.CtrlCmd|monaco.KeyMod.Alt|monaco.KeyCode.KeyF],? contextMenuGroupId:'2_customCommand',run(ed, opt) {? ? let a = editorBox.getValue()? ? let b =beautify_js(a)? ? // editorBox.setValue(b)? ? editorBox.executeEdits("", [? ? ? {? ? ? ? range: new monaco.Range(1,1, editorBox.getModel().getLineCount() +1,1),? ? ? ? text: b? ? ? }? ? ])? }})

可以在右鍵面板中增加自定義功能:

id:該選項的唯一標(biāo)識。

label:選項顯示的名稱。

keybindings:綁定的快捷鍵,多個快捷鍵用豎線分割。每個按鍵要使用monaco的內(nèi)置枚舉類型來設(shè)定。

contextMenuGroupId:選項所屬組的id,內(nèi)置了三個組id(navigation:該組是右鍵的第一欄,1_modification:修改組,9_cutcopypaste:剪切復(fù)制粘貼組)。

run:選擇該選項之后的回調(diào)函數(shù),第一個參數(shù)為editorBox實例,第二個參數(shù)為一個剩余參數(shù)的數(shù)組,注意如果通過快捷鍵觸發(fā)那么第二個參數(shù)不存在。因為我實現(xiàn)了一個格式化代碼的功能,所以我在run函數(shù)中主要進(jìn)行了三步操作:

1.使用getValue獲取編輯器內(nèi)容,這里有一個注意的點(diǎn),如果實例化的編輯器是用vue中的data里面定義的數(shù)據(jù)接收的,由于vue的響應(yīng)式更新,會造成死循環(huán)而無法獲取導(dǎo)致頁面卡死,可以用三個方法解決,根據(jù)不同的情況選擇不同的方式,第一如果是用this.editorBox接收的,那么editorBox不要寫在data里面,這樣就不會做數(shù)據(jù)追蹤,第二可以使用非vue實例化的屬性。第三可以用toRaw來獲取原始數(shù)據(jù),也可以解決。

2.使用beautify_js對代碼進(jìn)行格式化。

3.將格式化后的代碼重新回寫到編輯器中,這里有兩種方式setValue和executeEdits。區(qū)別是setValue不能撤銷回上一步,而executeEdits可以。其中range表示選擇的范圍,我這里從第一行第一列一直到最后一行的下一行的第一列,相當(dāng)于是全部文檔,然后用text的值,也就是格式化后的代碼來進(jìn)行替換。

右鍵面板

自定義補(bǔ)全:

monaco.languages.registerCompletionItemProvider('javascript', {? provideCompletionItems(model, pos) {return{? ? ? suggestions: [? ? ? ? {? ? ? ? ? label:"row-col",? ? ? ? ? insertText: beautify_js(`[{"cols":[{"span":6},{"span":6}]}]`),? ? ? ? ? kind: monaco.languages.CompletionItemKind["Snippet"],? ? ? ? ? detail:"插入行列",? ? ? ? },? ? ? ? {? ? ? ? ? label:"for-full",? ? ? ? ? insertText: beautify_js(`for(leti =0; i < len; i++) {}`),? ? ? ? ? kind: monaco.languages.CompletionItemKind["Snippet"],? ? ? ? ? detail:"完整for循環(huán)",? ? ? ? }? ? ? ],? ? ? dispose() {constline = pos.lineNumberconstcolumn = pos.columnif(model.getValueInRange(new monaco.Range(line, column -1, line, column)) !=="/") {return}? ? ? ? editorBox.executeEdits("", [? ? ? ? ? {? ? ? ? ? ? range:newmonaco.Range(line, column -1, line, column),? ? ? ? ? ? text:null}? ? ? ? ])? ? ? }? ? }? },? triggerCharacters: ['/']})

triggerCharacters表示觸發(fā)補(bǔ)全操作的快捷鍵,編輯器會提示出suggestions中設(shè)置的內(nèi)容。注意回車之后輸入的快捷鍵仍然是存在的。

suggestions中的label表示提示的名稱,insertText表示回車之后輸入的內(nèi)容,kind用來設(shè)定提示片段的類型,即最前面的圖標(biāo)標(biāo)識,detail是提示的說明,位于提示行的末尾。

效果如下圖所示:

補(bǔ)全提示

由于使用triggerCharacters設(shè)定的快捷鍵輸入,回車之后該快捷鍵依然存在,對于此場景來說相當(dāng)于多余的字符,所以我做了一下處理,在回車之后將"/"刪除掉,dispose回調(diào)函數(shù)就是用來做這個事情的。由于其他正常提示的情況下是不需要刪除符號的,因此做了一下判斷。

(提示:自定義的補(bǔ)全提示功能,除了使用triggerCharacters定義的快捷鍵會觸發(fā)定義的一組提示,輸入label中的首個字符,也會觸發(fā)相應(yīng)提示,這種情況不會產(chǎn)生多余的字符)

差異對比:

letcurModel = monaco.editor.createModel("let a = 1\nlet c = 3\nlet n = 2","text/plain")letoldModel = monaco.editor.createModel("let a = 1\nlet n = 2","text/plain")letdiffEditor = monaco.editor.createDiffEditor(document.getElementById("monaco-editor"), {theme:"vs-dark"})diffEditor.setModel({original: oldModel,modified: curModel})

通過創(chuàng)建兩個不同的model來對比代碼的變化

代碼差異對比

注意事項:

由于版本的不同可能會導(dǎo)致無法使用的問題。

對于較低版本vue-cli創(chuàng)建的項目,默認(rèn)使用webpack4,那么對于最新的monaco-editor,則會出現(xiàn)問題,最大的支持版本為0.30.1,其他包的版本也需要相應(yīng)調(diào)整。

對于高版本vue-cli創(chuàng)建的項目,默認(rèn)使用webpack5,或者使用vite創(chuàng)建的項目是可以支持最新的monaco-editor版本的。

對于版本的支持,做如下標(biāo)示:

較低版本monaco-editor@0.30.1monaco-editor-webpack-plugin@6.0.0monaco-editor-nls@2.0.0

較高版本monaco-editor@0.32.0//或更新monaco-editor-webpack-plugin//已經(jīng)與monaco-editor統(tǒng)一管理,最新版本即可monaco-editor-nls@3.0.0//或更新

總的來說

monaco-editor支持的功能非常豐富,幾乎包含了vscode所有的基礎(chǔ)功能,大家可以在此基礎(chǔ)上自由發(fā)揮,實現(xiàn)更多有用的功能。希望能為各位開發(fā)者們略盡我的綿薄之力,更好的幫助大家完成工作需要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容