使用monaco-editor,registerCompletionItemProvider多次註冊,最終導致展示的提示內容重複

賣女孩的小火柴發表於2021-09-25

最近專案實現一個提示功能,輸入某個符號,展示匹配的內容,和程式碼提示功能類似。最終選擇了monaco-editor,微軟開發的js庫,vscode也是基於這個庫開發的。

在開發過程中,遇到些問題,由於文件不是很友好,花了半天才解決問題。下面主要記錄一下問題以及解決辦法,希望以後遇到這些問題的童鞋能快速避坑。

遇到的問題以及解決辦法

問題:

在Antd Modal中使用monaco-editor,顯示Modal後關閉Modal,再次開啟Modal,monaco-editor的提示內容就會重複,顯示幾次Modal,對應的內容就會重複幾次,如下圖:

demo

解決辦法

思路1:
經過除錯發現,registerCompletionItemProvider方法多次執行,首先猜到的可能是外掛多次建立,沒有銷燬造成的,查閱對應的文件後,發現有外掛提供了一個銷燬的方法:

monaco.editor.dispose()

於是,嘗試在元件銷燬之前呼叫上面的方法,但是執行後發現並不生效,上述問題依舊存在,此法不通~

思路2:
既然registerCompletionItemProvider多次執行,那麼給元件中新增一個全域性的計數器count,元件註冊一次後執行一次count+1,只有當count===0時,才走對應的外掛註冊邏輯,否則直接取快取的資料。部分程式碼片段如下:

 let count = 0;
      monaco.languages.registerCompletionItemProvider(languageName, {
        triggerCharacters: ['['],
        provideCompletionItems: function (model, position, context) {
          count=count+1;
          if(count===1){
            let suggestions = [];
            if (context.triggerCharacter === '[') {
              [...dimensions, ...modules].forEach((item, index) => {
                suggestions.push({
                  label: item.customName,
                  insertText: `${item.customName}]`,
                  kind: 12,
                });
              });
            }
            return { suggestions };
          }
        },
      });

這種方式簡單粗暴,如果需要外掛提示的內容是固定的,直接用快取的資料是可以解決這個問題。但是!!!我遇到的需求是,提示的內容是根據介面動態取的。所以,此法依舊不通~

思路3:
由於谷歌和百度都沒用找到滿意的答案,於是去monaco-editorGitHubissues中查詢答案,剛開始根據registerCompletionItemProvider關鍵字搜尋,但是並沒有類似的問題,然後又嘗試搜尋provideCompletionItems關鍵詞,最終在issues中找到了2個類似的問題,連結如下:

https://github.com/microsoft/monaco-editor/issues/2217

https://github.com/microsoft/monaco-editor/issues/2084

最終解決辦法:
registerCompletionItemProvider註冊建立時,將建立的物件儲存起來,如下:

 monacoProviderRef.current = monaco.languages.registerCompletionItemProvider(languageName, {

在元件銷燬時,將editorregisterCompletionItemProvider生成的物件一同銷燬。程式碼如下:

useEffect(() => {
      // todo
      // xxxxxx
      return () => {
        // 銷燬
        monacoProviderRef.current?.dispose();
        monacoRef.current?.dispose();
        
      };
    }, []);

最後測試,bug完美解決!

問題:

設定editor的預設主題,但是Modal第一次展示時,預設主題沒有生效,第一次之後主題才會生效,如下圖:

設定預設主題程式碼如下:

monaco.editor.defineTheme('myCoolTheme', {
        base: 'vs',
        inherit: false,
        colors: { token: 'value', foreground: '#00c1de' },
        rules: [{ token: 'value', foreground: '#00c1de' }],
      });

解決辦法

去官方文件中找設定主題的api,發現以上程式碼是對主題的定義操作,如果要首次執行生效,還需要手動設定一次上面對應定義的主題

monaco.editor.setTheme('myCoolTheme');

最終,問題解決,效果如下:

願天下沒有難解的bug!祝大家中秋節快樂!!!

相關文章