首頁 >開發工具 >VSCode >深入了解vscode中markdown預覽的實作原理

深入了解vscode中markdown預覽的實作原理

青灯夜游
青灯夜游轉載
2021-09-01 18:13:173106瀏覽

深入了解vscode中markdown預覽的實作原理

vscode 的 markdown 預覽是我們整天都在用的功能,有沒有想過它是怎麼實現的。或許有一天你會接到個客製化 markdown 預覽的需求,該怎麼做呢? 【推薦學習:《vscode教學》】

其實整體想法比較簡單,就是創建一個webview panel,設定內容為markdown 產生的html,之後在markdown 更新的時候同步修改webview的html 就可以了。

思路分析

透過 vscode.window.createWebviewPanel 建立一個 webview,指定在側邊打開,之後透過該  panel 物件的 webview.html 屬性來設定 html。

html 是透過編輯器的 markdown 內容產生的, 編輯器內容透過 editor.document.getText() 拿到,然後呼叫第三方的 markdown 轉 html 的函式庫來產生。

這樣就完成了 markdown 的預覽。

預覽之後需要更新,監聽 vscode.workspace.onDidSaveTextDocument 和 vscode.workspace.onDidChangeTextDocument 的事件,在文件更新和儲存的時候,拿到編輯器的內容,重新產生 html,然後設定到 webview。

webviewPanel 支援 webview.postMessage(message); 的方式傳遞訊息,支援 updateHTML 等一系列 command,可以透過傳遞訊息來觸發。

但是怎麼知道哪個文件更新哪個 webview 呢?

可以維護一個 map,在建立 webviewPanel 的時候記錄到 map 中,key 為檔案路徑,這樣更新的時候就能找出對應的 webview 來更新。

這樣,就完成了 markdown 內容的更新。

其實整體思路還是比較簡單的,下面我們來寫下程式碼

程式碼實作

我們看下vscode-markdown-preview-enhanced 的插件的程式碼,這也是預覽markdown 的插件,程式碼還算簡潔,可以用來學習。

(以下程式碼是簡化後的程式碼)

首先,外掛程式要指定觸發的條件,也就是在package.json 裡面指定activationEvents:

"activationEvents": [
    "onLanguage:markdown",
    "onCommand:markdown-preview-enhanced.openPreviewToTheSide"
],

這裡一個是編輯markdown 內容的時候被激活,一個是執行command 的時候激活。

具體啟動的邏輯在 active 方法裡:

export function activate(context: vscode.ExtensionContext) {

  const contentProvider = new MarkdownPreviewEnhancedView(context);

  context.subscriptions.push(
    vscode.commands.registerCommand(
      "markdown-preview-enhanced.openPreviewToTheSide",
      openPreviewToTheSide,
    ),
  );
  
  function openPreviewToTheSide(uri?: vscode.Uri) {
    let resource = uri;
    if (!(resource instanceof vscode.Uri)) {
      if (vscode.window.activeTextEditor) {
        resource = vscode.window.activeTextEditor.document.uri;
      }
    }
    contentProvider.initPreview(resource, vscode.window.activeTextEditor, {
      viewColumn: vscode.ViewColumn.Two,
      preserveFocus: true,
    });
  }
}

我們註冊了那個 command,執行 command 會拿到目前 editor 的 url,然後進行 markdown 的 preview。

preview 的所有邏輯都集中定義在了 MarkdownPreviewEnhancedView 的實例物件中,在 command 觸發時執行 initPreivew。

public async initPreview(
    sourceUri: vscode.Uri,
    editor: vscode.TextEditor,
    viewOptions: { viewColumn: vscode.ViewColumn; preserveFocus?: boolean },
) {
    // 创建 webview
    let previewPanel: vscode.WebviewPanel = vscode.window.createWebviewPanel(
        "markdown-preview-enhanced",
        `Preview ${path.basename(sourceUri.fsPath)}`,
        viewOptions
    );

    // 监听 webview 的消息
    previewPanel.webview.onDidReceiveMessage((message) => {});

    // 记录 webview 到 map 中
    this.previewMaps[sourceUri.fsPath] = previewPanel;
    
    // 拿到编辑器的文本,生成 html
    const text = editor.document.getText();
    engine
      .generateHTMLTemplateForPreview({inputString: text})
      .then((html) => {
        // 设置 html 到 previewPanel
        previewPanel.webview.html = html;
      });
}

在 initWebivew 裡面建立 webviewPanel,同時把 webviewPanel 儲存到 map 中,key 為文件的檔案路徑。拿到編輯器文字來產生 html,設定到 webview.html,這樣就完成了 markdown 的預覽。

這條路徑走通之後,我們就實作了 markdown 的預覽。

但只預覽一次不行,更新文件之後需要自動更新,我們繼續在 active 方法裡加入事件監聽:

  context.subscriptions.push(
    vscode.workspace.onDidSaveTextDocument((document) => {
      if (isMarkdownFile(document)) {
        contentProvider.updateMarkdown(document.uri, true);
      }
    }),
  );

  context.subscriptions.push(
    vscode.workspace.onDidChangeTextDocument((event) => {
      if (isMarkdownFile(event.document)) {
        contentProvider.update(event.document.uri);
      }
    }),
  );

監聽文字修改和儲存的時候,呼叫 update 方法來更新。

public updateMarkdown(sourceUri: Uri) {

    // 从 map 中根据文件路径取出对应的 webviewPanel
    const previewPanel = this.previewMaps[sourceUri.fsPath];
    
    // 生成最新的 html 传递给 webview
    const text = document.getText();
    engine
        .parseMD(text)
        .then(({ markdown, html }) => {
            previewPanel.webview.postMessage({
              command: "updateHTML",
              html
            });
        }

}

這裡是透過 webview.postMessage 給 html 內容傳遞 updateHTML 的 command 訊息,觸發 html 內容的更新。

這樣,我們就實作了 markdown 的同步刷新。

總結

vscode 裡面markdown 的預覽是一個常用但實現起來並不難的功能,我們看了下vscode-markdown-preview-enhanced 插件的源碼,理清了整體的流程:

  • 透過vscode.window.createWebviewPanel 建立webviewPanel 來顯示html
  • html 透過editor.document.getText() 拿到文字內容之後透過第三方包生成,設定到webviewPanel
  • 監聽workspace.onDidSaveTextDocument 和workspace.onDidChangeTextDocument,來拿到最新內容,之後產生html 透過webview.postMessage 傳遞udpateHTML 的訊息來更新到webview。
  • 要注意的是,需要記錄一個map 來保存uri.fsPath 和webviewPanel 的對應關係,實現文本內容改變更新對應的webview

markdown 的預覽是一個常見但是並不難的需求,也比較適合入門vscode 外掛的開發,希望這篇文章能幫大家理清思緒。

更多程式相關知識,請造訪:程式設計入門! !

以上是深入了解vscode中markdown預覽的實作原理的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除