VS Code 團隊本身就是 JavaScript 的使用者,同時 VS Code 還是 TypeScript 項目非常早期的用戶,可以說是和 TypeScript 一起成長起來的。無論是 VS Code 還是 TypeScript 團隊,都極其重視 VS Code 上的 JavaScript 使用體驗,因為這是他們工作的很大一部分。
接下來我們在這個文件夾下創(chuàng)建一個 JavaScript 文件index.js,內容如下:
function foo() {
bar("Hello World");
}
function bar(str) {
console.log(str);
}
JavaScript
這段 JavaScript 代碼中定義兩個函數(shù) foo 和 bar,其中 foo 函數(shù)內部調用了 bar 這個函數(shù)。根據(jù)我們之前學習的知識,我們可以使用下面這些命令:
1、轉到定義(F12)
2、格式化文件(Format Document)
3、符號跳轉(Cmd + Shift + O)
4、函數(shù)建議列表和參數(shù)建議
如果你是跟著專欄一章一章學習過來的話,相信你已經非常熟悉這些操作了。
我們知道 JavaScript 是一門動態(tài)類型語言,一個對象的類型在最終運行的時候才會決定和進行檢測。而 TypeScript 這門語言的出現(xiàn),允許我們在書寫和編譯代碼時,就對代碼中對象的類型和使用進行規(guī)范和約束,以降低因類型錯誤而導致的 bug。尤其是當項目逐漸龐大后,擁有一個比較完善的類型體系,無論是對于代碼質量,還是代碼的維護難度,都有很多好處。
到這里你可能會問,“那是不是說,如果要使用上類型系統(tǒng),我就一定得把項目全部用 TypeScript 重寫呢?”答案當然是:不用。這里我教你兩招。
第一招是使用工具JSDoc,第二招是typings/d.ts。下面我們來分別闡述其妙處。
JSDoc 跟 Javadoc 或者 phpDocumentor 很類似,它是一個文檔規(guī)范工具,我們通過在代碼里寫上注釋,記錄好各個類、函數(shù)、對象的作用,然后就可以生成相應的 API 文檔了。同時,我們還能夠在注釋里標記對象的 JavaScript 類型,這樣我們在閱讀和使用這段代碼時就很方便了。
下面,讓我們來給 index.js 里的 bar 函數(shù)添加上 JSDoc,新的代碼如下:
function foo() {
bar("Hello World");
}
/**
* bar
* @param {string} str
*/
function bar(str) {
console.log(str);
}
JavaScript
上面的代碼注釋的意思是,bar 這個函數(shù),它需要傳入一個參數(shù) str,同時這個參數(shù)的類型是 string。
此時當我們在調用 bar 函數(shù)時,參數(shù)建議就會告訴我們需要傳入一個 string 類型的參數(shù)。
VS Code 里的 JavaScript 語言服務,會讀取 JavaScript 文件里的 JSDoc 注釋,然后根據(jù)注釋里提供的類型信息,來對類型進行檢查和建議。所以,如果你希望給你的 JavaScript 項目增加類型,并且有比較好的開發(fā)體驗,JSDoc 就是一個不錯的選擇。關于更多 JSDoc 的知識,請參考文檔。
看到這里,你的下一個問題可能是:如果我使用的函數(shù)是來自某個 npm 模塊,也就是說這是別人寫的代碼,VS Code 還能知道這個函數(shù)的參數(shù)類型嗎?
這里就必須要再提一次 TypeScript 的類型系統(tǒng)了。TypeScript 的一大特點就是靜態(tài)類型,一般一個 TypeScript 項目,發(fā)布的時候會編譯成 JavaScript,同時也會發(fā)布一個d.ts文件,這個文件記錄了發(fā)布的這個 JavaScript 文件里的對象類型。
接著 VS Code 則會通過讀取這個 d.ts 文件,來為這個模塊里的函數(shù)和對象提供類型建議。VS Code 是使用下面幾種方式去尋找 d.ts 文件:
文件,如果有的話就直接使用。使用 TypeScript 書寫的項目一般都會有
d.ts
文件,而很多知名的 JavaScript 框架,雖然并不是使用 TyepScript 來維護的,也提供了
d.ts
文件。
在我的日常開發(fā)工作中,上面的第三種方式是最常發(fā)生的,而且值得稱道的是,第三種方式并不需要用戶做任何的事情。VS Code 會自動搜索,找到合適的 d.ts 文件,然后下載下來,接著就能夠提供智能提示了。這個功能又被叫做自動類型采集(Auto Type Acquisition)。
回到我們的示例代碼中,假如說我們想在 index.js 里使用 lodash 這個 npm 包,我們會在代碼的最上方,添加如下的模塊引用:
const _ = require('lodash');
JavaScript
添加完這段引用后,當我們輸入 _. 時,VS Code 就會立刻給我們建議 lodash 里提供的各種函數(shù)了。
在上面的動圖中,你可以看到,除了建議列表,在輸入?yún)?shù)時,我們還可以看到參數(shù)類型建議和相關的文檔信息。
而如果我們在 lodash 的這個函數(shù)上運行“跳轉定義”命令,會發(fā)現(xiàn) VS Code 跳轉到了 lodash 對應的 d.ts 文件(object.d.ts)中。
通過上面的兩個例子,相信你已經明白了,雖然我們沒有書寫 TypeScript,但是 VS Code 會通過查找模塊相關的 d.ts 文件,來努力給我們提供類型相關的建議。而我們也可以通過書寫 JSDoc 格式的注釋,主動地給 VS Code 提供類型信息。
很多同學很喜歡TypeScript 的類型系統(tǒng)和代碼檢查,但是另一方面,又覺得要想把整個項目遷移到 TypeScript 工作量太大了。TypeScript 團隊也考慮到了這一點,為了能夠讓 JavaScript 社區(qū)漸進地使用 TypeScript 的工具鏈,他們?yōu)?JavaScript 提供了一個新的功能,叫做ts-check,也就是在 JavaScript 代碼中,手動地申明開啟更強的代碼審核。
我們可以在上面的示例代碼的第一行,添加下面這段注釋:
// @ts-check
JavaScript
此時整段代碼顯示為如下:
// @ts-check
const _ = require('lodash');
function foo() {
bar("Hello World");
}
/**
* bar
* @param {string} str
*/
function bar(str) {
console.log(str);
}
JavaScript
在這段代碼中,我們使用了 JSDoc 來申明 bar 這個函數(shù),必須傳入一個參數(shù),而且是 string。如果我們在使用 bar 函數(shù)時沒有遵守的話,TypeScript 就會提供錯誤信息。
比如,我們輸入 bar() ,就能看到錯誤信息 :[ts] 應有 1 個參數(shù),但獲得 0 個。而如果我們輸入 bar(1),則能看到: [ts] 類型“1”的參數(shù)不能賦給類型“string”的參數(shù)。
有了 ts-check后,我們就可以一個文件一個文件地給代碼添加類型信息,然后為這個文件單獨開啟錯誤檢查了。這樣既不用擔心全部遷移 TypeScript 帶來的工作量和各種阻力,也能享受到類型系統(tǒng)所帶來的好處了。
上面我們介紹的代碼示例,是一個單文件,我們能夠在其中引用第三方 npm 模塊。而如果我們的文件夾中有多個 JavaScript 代碼,并且彼此之間互相引用的話,VS Code 還能夠理解嗎?我們不妨試試。
我們先在項目下新創(chuàng)建一個 app.js 文件,內容如下:
export function app() {
console.log("Hello World");
}
JavaScript
而在 index.js 中,我們替換掉 lodash 的引用,而引用 app 這個模塊。
const { app } = require('./app');
JavaScript
引用后,當我們在 index.js 書寫代碼時,就能夠得到 app 這個函數(shù)的提示了。
對于 app.js 這個模塊,VS Code 的處理思路跟處理 lodash 那個 npm 包的引用是一樣的。VS Code 先是通過 ./app 這個相對路徑找到模塊,然后看這個模塊里 export 哪些函數(shù)和對象,接著在 index.js 提供提示。
但是,JavaScript 程序員都知道,JavaScript 世界里的模塊系統(tǒng),從來都沒有一個廣泛接受的標準,即使有標準,社區(qū)也會使用一些前沿的、還在測試探討階段的新語法,常見的模塊有: AMD、CommonJS、ECMAScript 6 Module,等等。而社區(qū)廣泛使用的打包工具 Webpack,更是允許你各種自定義模塊的引用方式。這時VS Code 的 JavaScript 語言服務,要想同時支持所有的模塊系統(tǒng),可就頭疼了。
舉個最簡單的例子——模塊絕對路徑引用。在上面的例子里,我們引用 app 這個模塊時,使用的是相對地址:
const { app } = require('./app');
JavaScript
這個很好處理,我們到同一個目錄下去找這個 app 模塊就行了。而如果使用了絕對路徑,比如:
const { app } = require('app');
JavaScript
這個就比較麻煩了。JavaScript 的語言就不知道,究竟是把這個當作 npm 包,然后到 node_modules 文件夾下去尋找呢,還是從當前項目的根目錄下去尋找這個模塊呢?如果項目中使用了 babel 或者 webpack 的話,又該怎么去理解這些工具對模塊的處理呢?
此時,在 VS Code 的編輯器里,我們已經能看到錯誤提示了。
jsconfig
為了解決這個問題,我們需要引入一個特殊的配置文件 jsconfig.json。這個配置文件用于提示 JavaScript 語言服務,當前項目的 JavaScript 代碼,哪些文件是屬于這個項目的?JS 語法是哪個版本?而又該去哪里尋找模塊?
下面,讓我們在文件夾下創(chuàng)建如下內容的 jsconfig.json 文件:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"baseUrl": "."
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}
JSON
這個文件告訴 VS Code ,當前項目里的 JavaScript 語法是使用 es2016 的,引用模塊時,如果模塊的名稱是絕對路徑,那么請從根目錄(也就是 .)開始尋找。
有了這個文件之后,VS Code 的 JavaScript 語言服務立刻就找到 app 這個模塊了。
jsconfig 甚至還允許指定多個文件夾依次進行模塊的查找。下面我們創(chuàng)建一個文件夾 common,然后把 app.js 挪入到 common 中。
然后在 jsconfig 中,進行如下的修改:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"baseUrl": ".",
"paths": {
"*": [
"*",
"common/*"
]
}
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}
JSON
我們在上面的 jsconfig 里添加一個 paths 屬性,這里面就是對模塊路徑的映射。VS Code 會閱讀這些映射,依次去尋找模塊。此時當我們再打開 index.js ,將鼠標移動到 require(‘app’) 代碼上時,我們能夠看到,此時 VS Code 正確地將 app 這個模塊定位到了 common/app.js 這個文件。
在前面的專欄里,我們在介紹 VS Code 的智能語言服務功能時,有讀者提了一個非常好的問題,那就是如果項目中使用了 Webpack 而且 Webpack 中設置了 resolve,那么 VS Code 里的代碼跳轉就不工作了。相信現(xiàn)在你應該已經明白是為什么了,如果你的項目里為 Webpack 做了特殊的模塊地址映射,那么你也需要在 jsconfig 里做同樣的映射,這樣 VS Code 就知道如何找到模塊了。
既然說到模塊,接下來就介紹兩個非常有用的模塊相關的功能。
首先我們把 index.js 最上面的模塊引用刪除。然后在 index.js 的末尾,輸入 app() 這段代碼。
你可以看到,VS Code 自動為我們添加了模塊的引用。這是因為 VS Code 已經知道當前項目里有哪些模塊,每個模塊提供了哪些方法,當你使用它們時,就會幫你將引用填入文件開頭。
這個功能又叫自動模塊引用(Auto Imports)。
JavaScript 的模塊地址很多都是跟文件地址相關的,閱讀性很好,但是當我們移動文件地址的時候,可就麻煩了。因為這意味著我們需要把所有跟這個文件相關的模塊引用全部修改一遍。不過好在VS Code提供了模塊地址自動更新的功能。
下面我們把 app.js 從 common 這個文件夾下,移動到根目錄下。
從上面的動圖中,你能夠看到,VS Code 會問你是否要自動修改模塊的引用,在選擇“是”之后,VS Code 就會把 app 這個模塊的引用地址,從 “common/app” 改成了 “app”。是不是非常實用呢?
上面我們提到了,可以通過在 JavaScript 文件里寫入 @ts-check 注釋,讓 VS Code 對單個文件進行代碼審查。不過如果項目不算大,而你打算對所有的 JavaScript 文件進行代碼檢查的話,那么也可以在 jsconfig.json 文件的 compilerOptions 里添加 checkJs 屬性,打開對 JavaScript 的檢查。修改后的 jsconfig.json 如下:
{
"compilerOptions": {
"module": "commonjs",
"target": "es2016",
"baseUrl": ".",
"paths": {
"*": [
"*",
"common/*"
]
},
"checkJs": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}
JSON
其實,jsconfig 還有很多其他實用的設置,你不妨利用 VS Code 的自動補全和代碼提示,自己動手改一改,看看它們都是干嘛的。
更多建議: