更多文章請戳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)
這里只能看成是新窗口開發者控制台日志的一種快捷顯示,以下是舊窗口調試控制台顯示的內容:
而對應的內容在新窗口的開發者控制台顯示如下:
可以看到,結構較深的對象即使在控制台也無法顯示,目前發現的唯一比較好的方法就是在輸出的地方打一個斷點,然后運行的時候會自動卡在這里,鼠標懸停就可以查看對象的內容。
新窗口的調試控制台
一般沒什么擴展相關日志會輸出在這里。
舊窗口的開發者控制台
快捷鍵Ctrl+Alt+I
,這里一般顯示vscode本身一些日志,和擴展相關的不會顯示在這里,所以這個也不用太多關心。
新窗口的開發者控制台
快捷鍵也是Ctrl+Alt+I
,不記得的可以從幫助
-> 切換開發人員工具
找到。這個控制台很重要,有時候如果發現你的代碼莫名其妙沒生效,很有可能是報錯了,這種報錯是不會顯示在舊窗口調試控制台
的,如果你不知道到這里來查看日志,那么你只能一臉懵逼的到處亂試了,調試控制台只打印常規日志,語法錯誤並不會顯示在這里。
例如,我在跳轉定義實現前人為制造一個錯誤:
function provideDefinition(document, position, token) {
console.log(aaf);
const fileName = document.fileName;
// 省略其它代碼
}
運行后就會發現點擊跳轉不生效,但是也沒有什么報錯提示,此時只能打開控制台查看才能發現問題:
WebView控制台
WebView
我們會在下一章節介紹,這里先提一下。Webview
的控制台比較特殊,需要特殊的命令才能打開,按下Ctrl+Shift+P
然后執行打開Webview開發工具
,英文版應該是Open Webview Developer Tools
:
開發時我們把它當成一個普通的網頁來看就好了。
調試
vscode插件的調試非常簡單方便,只需要在需要調試的地方打個斷點,然后按F5
執行即可:
幾個調試快捷鍵:
F5
運行Ctrl+F2
停止運行F6
下一步跳過(類似於Chrome的F10
)F5
下一步跳入F8
跳過
如何快速找到我想找的內容
剛開始只能先大概對整個vscode的api有一個大概了解,了解了之后就大概清楚一般什么功能會怎么實現,該去什么地方找,所有的vscode的api都可以在vscode.d.ts
文件里面找到:
不得不佩服,正規大型項目的注釋寫的真的不是一般的詳細,官網的API文檔肯定也是基於這個自動生成的,反正把這個ts
文件吃透了,基本上你想實現什么功能要怎么實現都了如指掌了。
查看插件存放目錄
插件安裝后根據操作系統不同會放在如下目錄:
- Windows系統:
%USERPROFILE%\.vscode\extensions
- Mac/Linux:
~/.vscode/extensions
想要學習查看其它插件的代碼可以找到這個目錄:
一些個人經驗分享
調試控制台日志不可靠
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工作空間是這樣的,每一個工程一個個地單獨拖入:
也有的人是直接用打開文件夾的方式把存放代碼的父文件夾給打開:
但是如果此時你點擊將工作區另存為
保存了工作區之后就變成這樣了(請注意圖標的變化):
所以,即便拿到了某個文件的完整路徑也不好獲取這個文件的工程路徑,因為不知道工作區的這個文件夾名字是你的工程名還是存放工程的父文件夾的名字。
已知:
- 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
文件在根目錄,所以就根據哪個文件夾有這個文件來判斷,也只能是這樣了。