VSCode外掛開發全攻略(六)開發除錯技巧

xal發表於2018-10-18

更多文章請戳VSCode外掛開發全攻略系列目錄導航

前言

在介紹完一些比較簡單的內容點之後,我覺得有必要先和大家介紹一些開發中遇到的一些細節問題以及技巧,特別是後面一章節將要介紹WebView的知識,這個坑會比較多,避免大家走彎路。

開發方式

最理想的方式是準備雙顯示器,一個寫程式碼,一個執行外掛,實踐證明這種方式開發效率會提升很多,每次修改完程式碼之後直接Ctrl+R重新載入即可,非常方便。

日誌檢視

就我目前遇到的情況來看,vscode日誌主要有這5種:

舊視窗的除錯控制檯

擴充套件裡的console.log()日誌一般輸出在這裡,但是有很大的限制,結構稍微深一點的物件在這裡了就顯示不了:

Unable to log remote console arguments Output omitted for an object that cannot be inspected (Error: [sxei.vscode-plugin-demo]: Proposed API is only available when running out of dev or with the following command line switch: –enable-proposed-api sxei.vscode-plugin-demo)

這裡只能看成是新視窗開發者控制檯日誌的一種快捷顯示,以下是舊視窗除錯控制檯顯示的內容:

_W1136xH352_

而對應的內容在新視窗的開發者控制檯顯示如下:

_W1990xH796_

可以看到,結構較深的物件即使在控制檯也無法顯示,目前發現的唯一比較好的方法就是在輸出的地方打一個斷點,然後執行的時候會自動卡在這裡,滑鼠懸停就可以檢視物件的內容。

_W1156xH988_

新視窗的除錯控制檯

一般沒什麼擴充套件相關日誌會輸出在這裡。

舊視窗的開發者控制檯

快捷鍵Ctrl+Alt+I,這裡一般顯示vscode本身一些日誌,和擴充套件相關的不會顯示在這裡,所以這個也不用太多關心。

新視窗的開發者控制檯

快捷鍵也是Ctrl+Alt+I,不記得的可以從幫助 -> 切換開發人員工具找到。這個控制檯很重要,有時候如果發現你的程式碼莫名其妙沒生效,很有可能是報錯了,這種報錯是不會顯示在舊視窗除錯控制檯的,如果你不知道到這裡來檢視日誌,那麼你只能一臉懵逼的到處亂試了,除錯控制檯只列印常規日誌,語法錯誤並不會顯示在這裡。

例如,我在跳轉定義實現前人為製造一個錯誤:

function provideDefinition(document, position, token) {
    console.log(aaf);
    const fileName    = document.fileName;
    // 省略其它程式碼
}

執行後就會發現點選跳轉不生效,但是也沒有什麼報錯提示,此時只能開啟控制檯檢視才能發現問題:

_W1106xH678_

WebView控制檯

WebView我們會在下一章節介紹,這裡先提一下。Webview的控制檯比較特殊,需要特殊的命令才能開啟,按下Ctrl+Shift+P然後執行開啟Webview開發工具,英文版應該是Open Webview Developer Tools

_W906xH526_

開發時我們把它當成一個普通的網頁來看就好了。

除錯

vscode外掛的除錯非常簡單方便,只需要在需要除錯的地方打個斷點,然後按F5執行即可:

_W996xH632_

幾個除錯快捷鍵:

  • F5執行
  • Ctrl+F2停止執行
  • F6下一步跳過(類似於Chrome的F10
  • F5下一步跳入
  • F8跳過

如何快速找到我想找的內容

剛開始只能先大概對整個vscode的api有一個大概瞭解,瞭解了之後就大概清楚一般什麼功能會怎麼實現,該去什麼地方找,所有的vscode的api都可以在vscode.d.ts檔案裡面找到:

_W844xH662_

不得不佩服,正規大型專案的註釋寫的真的不是一般的詳細,官網的API文件肯定也是基於這個自動生成的,反正把這個ts檔案吃透了,基本上你想實現什麼功能要怎麼實現都瞭如指掌了。

檢視外掛存放目錄

外掛安裝後根據作業系統不同會放在如下目錄:

  • Windows系統:%USERPROFILE%.vscodeextensions
  • Mac/Linux:~/.vscode/extensions

想要學習檢視其它外掛的程式碼可以找到這個目錄:

_W780xH644_

一些個人經驗分享

除錯控制檯日誌不可靠

vscode有一個很坑爹的地方,這裡特別要注意,當require一個function進來並列印輸出時,雖然列印在控制檯顯示為null,但其實是有值的,不知道的人很容易被誤導,直接就是被這個現象騙了很久,切記切記

test-require-function.js:

function testRequireFunction(a, b) {
    console.log(`進入testRequireFunction方法`);
    console.log(a, b);
}
module.exports = testRequireFunction;

extension.js

exports.activate = function(context) {
    const testFn = require(`./test-require-function`);
    console.log(testFn); // vscode的日誌輸出不可靠,這裡竟然會列印null?!
    testFn(1, 2); // 1, 2
};

輸出結果:

null
進入testRequireFunction方法
1 2

程式碼為什麼沒生效

程式碼沒生效一般從這幾個地方去查詢:

  • activationEvents裡面新增了嗎?開發的時候如果老是忘記可以直接設定成*
  • 程式碼是不是報錯了?如前文所說,很多錯誤是不會暴露出來的,需要手動開啟控制檯檢視;
  • 程式碼是不是忘記引入了?有時候拆分多個檔案之後可能忘了引入;
  • 邏輯是不是寫錯了?最好的辦法就是debug,這是找問題最快的方法;
  • 版本衝突

這裡重點說一下最後面的版本衝突,這個甚至可以說是vscode本身的一些bug,經常發現程式碼莫名其妙地沒生效,怎麼除錯都不對,後來發現執行的根本就不是我們正在開發的那個版本,特別是當你的外掛已經發了一版到應用市場並安裝後,本地再按F5執行,理論上說debug執行的會覆蓋已安裝的,但有時候還是會出現異常情況,所以為了以防萬一,當出現這種情況時可以先把已經安裝的給解除安裝。

還有一個問題就是,有時候明明安裝了版本更加新的那個,結果執行的卻是舊的,開啟擴充套件目錄會發現很多並存的同名不同版本外掛,或者可能先是通過vsix方式安裝了一個版本,然後又從應用市場安裝一個,總之解決這類問題最好的方法就是:先解除安裝再安裝,實在不行手動去外掛目錄刪除之!

開啟檔案

開啟檔案是vscode.window.showTextDocument而不是vscode.workspace.openTextDocument,這個根據字面意思很容易搞錯,原來老外也有命名不準確的時候啊,哈哈。

  • vscode.workspace.openTextDocument僅僅是載入文件並返回一個TextDocument物件,但是並不在vscode中開啟;
  • vscode.window.showTextDocument則是在vscode中開啟一個文件;

其實:

vscode.workspace.openTextDocument(`someFilePath`).then(document => {
    vscode.window.showTextDocument(document, editor => {
        // 可以操作文件的editor物件
    });
})

等價於:

vscode.window.showTextDocument(vscode.Uri.file(`someFilePath`), editor => {
    // 可以操作文件的editor物件
});

工程根目錄的獲取

被這個問題踩過很多次坑,所有重點介紹一下。

有的人的vscode工作空間是這樣的,每一個工程一個個地單獨拖入:

_W670xH686_

也有的人是直接用開啟資料夾的方式把存放程式碼的父資料夾給開啟:

_W622xH454_

但是如果此時你點選將工作區另存為儲存了工作區之後就變成這樣了(請注意圖示的變化):

_W628xH450_

所以,即便拿到了某個檔案的完整路徑也不好獲取這個檔案的工程路徑,因為不知道工作區的這個資料夾名字是你的工程名還是存放工程的父資料夾的名字。

已知:

  • vscode以前有一個vscode.workspace.rootPath,由於後來vscode支援multipleRoot模式,所以這個欄位已經過時作廢了。
  • vscode.workspace.workspaceFolders可以獲取當前工作區所有根資料夾陣列;

之前我寫了一個簡單粗暴的獲取工程目錄方式:

/**
 * 獲取當前所在工程根目錄,有3種使用方法:<br>
 * getProjectPath(uri) uri 表示工程內某個檔案的路徑<br>
 * getProjectPath(document) document 表示當前被開啟的檔案document物件<br>
 * getProjectPath() 會自動從 activeTextEditor 拿document物件,如果沒有拿到則報錯
 * @param {*} document 
 */
getProjectPath(document) {
    if (!document) {
        document = vscode.window.activeTextEditor ? vscode.window.activeTextEditor.document : null;
    }
    if (!document) {
        this.showError(`當前啟用的編輯器不是檔案或者沒有檔案被開啟!`);
        return ``;
    }
    const currentFile = (document.uri ? document.uri : document).fsPath;
    let projectPath = null;

    let workspaceFolders = vscode.workspace.workspaceFolders.map(item => item.uri.path);
    // 由於存在Multi-root工作區,暫時沒有特別好的判斷方法,先這樣粗暴判斷
    // 如果發現只有一個根資料夾,讀取其子資料夾作為 workspaceFolders
    if (workspaceFolders.length == 1 && workspaceFolders[0] === vscode.workspace.rootPath) {
        const rootPath = workspaceFolders[0];
        var files = fs.readdirSync(rootPath);
        workspaceFolders = files.filter(name => !/^./g.test(name)).map(name => path.resolve(rootPath, name));
        // vscode.workspace.rootPath會不準確,且已過時
        // return vscode.workspace.rootPath + `/` + this._getProjectName(vscode, document);
    }
    workspaceFolders.forEach(folder => {
        if (currentFile.indexOf(folder) === 0) {
            projectPath = folder;
        }
    })
    if (!projectPath) {
        this.showError(`獲取工程根路徑異常!`);
        return ``;
    }
    return projectPath;
},

這種方式生效的前提是,如果是按照第一種方式存放工作空間的,工程的數目必須大於等於2,但是這種判斷方式不用說肯定會不準確。

後來換成了另外一種方式,考慮到工作接觸到的專案無論是node端還是前端都會有package.json檔案在根目錄,所以就根據哪個資料夾有這個檔案來判斷,也只能是這樣了。


相關文章