vscode 支持JavaScript

2022-08-11 14:18 更新

VS Code 團(tuán)隊(duì)本身就是 JavaScript 的使用者,同時(shí) VS Code 還是 TypeScript 項(xiàng)目非常早期的用戶,可以說(shuō)是和 TypeScript 一起成長(zhǎng)起來(lái)的。無(wú)論是 VS Code 還是 TypeScript 團(tuán)隊(duì),都極其重視 VS Code 上的 JavaScript 使用體驗(yàn),因?yàn)檫@是他們工作的很大一部分。

基礎(chǔ)語(yǔ)言支持

接下來(lái)我們?cè)谶@個(gè)文件夾下創(chuàng)建一個(gè) JavaScript 文件index.js,內(nèi)容如下:

function foo() {
    bar("Hello World");
}

function bar(str) {
    console.log(str);
}

JavaScript

這段 JavaScript 代碼中定義兩個(gè)函數(shù) foo 和 bar,其中 foo 函數(shù)內(nèi)部調(diào)用了 bar 這個(gè)函數(shù)。根據(jù)我們之前學(xué)習(xí)的知識(shí),我們可以使用下面這些命令:

1、轉(zhuǎn)到定義(F12)

2、格式化文件(Format Document)

3、符號(hào)跳轉(zhuǎn)(Cmd + Shift + O)

4、函數(shù)建議列表和參數(shù)建議

如果你是跟著專欄一章一章學(xué)習(xí)過(guò)來(lái)的話,相信你已經(jīng)非常熟悉這些操作了。

類型提示

我們知道 JavaScript 是一門(mén)動(dòng)態(tài)類型語(yǔ)言,一個(gè)對(duì)象的類型在最終運(yùn)行的時(shí)候才會(huì)決定和進(jìn)行檢測(cè)。而 TypeScript 這門(mén)語(yǔ)言的出現(xiàn),允許我們?cè)跁?shū)寫(xiě)和編譯代碼時(shí),就對(duì)代碼中對(duì)象的類型和使用進(jìn)行規(guī)范和約束,以降低因類型錯(cuò)誤而導(dǎo)致的 bug。尤其是當(dāng)項(xiàng)目逐漸龐大后,擁有一個(gè)比較完善的類型體系,無(wú)論是對(duì)于代碼質(zhì)量,還是代碼的維護(hù)難度,都有很多好處。

到這里你可能會(huì)問(wèn),“那是不是說(shuō),如果要使用上類型系統(tǒng),我就一定得把項(xiàng)目全部用 TypeScript 重寫(xiě)呢?”答案當(dāng)然是:不用。這里我教你兩招。

第一招是使用工具JSDoc,第二招是typings/d.ts。下面我們來(lái)分別闡述其妙處。

JSDoc

JSDoc 跟 Javadoc 或者 phpDocumentor 很類似,它是一個(gè)文檔規(guī)范工具,我們通過(guò)在代碼里寫(xiě)上注釋,記錄好各個(gè)類、函數(shù)、對(duì)象的作用,然后就可以生成相應(yīng)的 API 文檔了。同時(shí),我們還能夠在注釋里標(biāo)記對(duì)象的 JavaScript 類型,這樣我們?cè)陂喿x和使用這段代碼時(shí)就很方便了。

下面,讓我們來(lái)給 index.js 里的 bar 函數(shù)添加上 JSDoc,新的代碼如下:

function foo() {
    bar("Hello World");
}

/**
 * bar
 * @param {string} str 
 */
function bar(str) {
    console.log(str);
}

JavaScript

上面的代碼注釋的意思是,bar 這個(gè)函數(shù),它需要傳入一個(gè)參數(shù) str,同時(shí)這個(gè)參數(shù)的類型是 string。

此時(shí)當(dāng)我們?cè)谡{(diào)用 bar 函數(shù)時(shí),參數(shù)建議就會(huì)告訴我們需要傳入一個(gè) string 類型的參數(shù)。

VS Code 里的 JavaScript 語(yǔ)言服務(wù),會(huì)讀取 JavaScript 文件里的 JSDoc 注釋,然后根據(jù)注釋里提供的類型信息,來(lái)對(duì)類型進(jìn)行檢查和建議。所以,如果你希望給你的 JavaScript 項(xiàng)目增加類型,并且有比較好的開(kāi)發(fā)體驗(yàn),JSDoc 就是一個(gè)不錯(cuò)的選擇。關(guān)于更多 JSDoc 的知識(shí),請(qǐng)參考文檔。

typings/d.ts

看到這里,你的下一個(gè)問(wèn)題可能是:如果我使用的函數(shù)是來(lái)自某個(gè) npm 模塊,也就是說(shuō)這是別人寫(xiě)的代碼,VS Code 還能知道這個(gè)函數(shù)的參數(shù)類型嗎?

這里就必須要再提一次 TypeScript 的類型系統(tǒng)了。TypeScript 的一大特點(diǎn)就是靜態(tài)類型,一般一個(gè) TypeScript 項(xiàng)目,發(fā)布的時(shí)候會(huì)編譯成 JavaScript,同時(shí)也會(huì)發(fā)布一個(gè)d.ts文件,這個(gè)文件記錄了發(fā)布的這個(gè) JavaScript 文件里的對(duì)象類型。

接著 VS Code 則會(huì)通過(guò)讀取這個(gè) d.ts 文件,來(lái)為這個(gè)模塊里的函數(shù)和對(duì)象提供類型建議。VS Code 是使用下面幾種方式去尋找 d.ts 文件:

  • 首先,就是看看這個(gè) npm 包本身有沒(méi)有自帶d.ts

文件,如果有的話就直接使用。使用 TypeScript 書(shū)寫(xiě)的項(xiàng)目一般都會(huì)有

d.ts

文件,而很多知名的 JavaScript 框架,雖然并不是使用 TyepScript 來(lái)維護(hù)的,也提供了

d.ts

文件。

  • 其次,VS Code 還會(huì)看當(dāng)前項(xiàng)目文件夾下是否有 d.ts 文件。如果你使用的某個(gè) npm 包自己沒(méi)有 d.ts 文件的話,你可以自行書(shū)寫(xiě)。關(guān)于如何書(shū)寫(xiě) d.ts 文件,可以參考TypeScript 的文檔。
  • 最后,還有很多 JavaScript 項(xiàng)目,它們自己沒(méi)有 d.ts 文件,但是社區(qū)為它們書(shū)寫(xiě)了d.ts文件,并且發(fā)布到 npm @types 下供大家使用。而VS Code 會(huì)自動(dòng)到 npm @types 里進(jìn)行搜索,看看是不是有合適的類型 d.ts 可以使用。

在我的日常開(kāi)發(fā)工作中,上面的第三種方式是最常發(fā)生的,而且值得稱道的是,第三種方式并不需要用戶做任何的事情。VS Code 會(huì)自動(dòng)搜索,找到合適的 d.ts 文件,然后下載下來(lái),接著就能夠提供智能提示了。這個(gè)功能又被叫做自動(dòng)類型采集(Auto Type Acquisition)。

回到我們的示例代碼中,假如說(shuō)我們想在 index.js 里使用 lodash 這個(gè) npm 包,我們會(huì)在代碼的最上方,添加如下的模塊引用:

const _ = require('lodash');

JavaScript

添加完這段引用后,當(dāng)我們輸入 _. 時(shí),VS Code 就會(huì)立刻給我們建議 lodash 里提供的各種函數(shù)了。

在上面的動(dòng)圖中,你可以看到,除了建議列表,在輸入?yún)?shù)時(shí),我們還可以看到參數(shù)類型建議和相關(guān)的文檔信息。

而如果我們?cè)?lodash 的這個(gè)函數(shù)上運(yùn)行“跳轉(zhuǎn)定義”命令,會(huì)發(fā)現(xiàn) VS Code 跳轉(zhuǎn)到了 lodash 對(duì)應(yīng)的 d.ts 文件(object.d.ts)中。

通過(guò)上面的兩個(gè)例子,相信你已經(jīng)明白了,雖然我們沒(méi)有書(shū)寫(xiě) TypeScript,但是 VS Code 會(huì)通過(guò)查找模塊相關(guān)的 d.ts 文件,來(lái)努力給我們提供類型相關(guān)的建議。而我們也可以通過(guò)書(shū)寫(xiě) JSDoc 格式的注釋,主動(dòng)地給 VS Code 提供類型信息。

ts-check

很多同學(xué)很喜歡TypeScript 的類型系統(tǒng)和代碼檢查,但是另一方面,又覺(jué)得要想把整個(gè)項(xiàng)目遷移到 TypeScript 工作量太大了。TypeScript 團(tuán)隊(duì)也考慮到了這一點(diǎn),為了能夠讓 JavaScript 社區(qū)漸進(jìn)地使用 TypeScript 的工具鏈,他們?yōu)?JavaScript 提供了一個(gè)新的功能,叫做ts-check,也就是在 JavaScript 代碼中,手動(dòng)地申明開(kāi)啟更強(qiáng)的代碼審核。

我們可以在上面的示例代碼的第一行,添加下面這段注釋:

// @ts-check

JavaScript

此時(shí)整段代碼顯示為如下:

// @ts-check

const _ = require('lodash');

function foo() {
    bar("Hello World");
}

/**
 * bar
 * @param {string} str 
 */
function bar(str) {
    console.log(str);
}

JavaScript

在這段代碼中,我們使用了 JSDoc 來(lái)申明 bar 這個(gè)函數(shù),必須傳入一個(gè)參數(shù),而且是 string。如果我們?cè)谑褂?bar 函數(shù)時(shí)沒(méi)有遵守的話,TypeScript 就會(huì)提供錯(cuò)誤信息。

比如,我們輸入 bar() ,就能看到錯(cuò)誤信息 :[ts] 應(yīng)有 1 個(gè)參數(shù),但獲得 0 個(gè)。而如果我們輸入 bar(1),則能看到: [ts] 類型“1”的參數(shù)不能賦給類型“string”的參數(shù)。

有了 ts-check后,我們就可以一個(gè)文件一個(gè)文件地給代碼添加類型信息,然后為這個(gè)文件單獨(dú)開(kāi)啟錯(cuò)誤檢查了。這樣既不用擔(dān)心全部遷移 TypeScript 帶來(lái)的工作量和各種阻力,也能享受到類型系統(tǒng)所帶來(lái)的好處了。

模塊引用

上面我們介紹的代碼示例,是一個(gè)單文件,我們能夠在其中引用第三方 npm 模塊。而如果我們的文件夾中有多個(gè) JavaScript 代碼,并且彼此之間互相引用的話,VS Code 還能夠理解嗎?我們不妨試試。

相對(duì)地址引用

我們先在項(xiàng)目下新創(chuàng)建一個(gè) app.js 文件,內(nèi)容如下:

export function app() {
    console.log("Hello World");
}

JavaScript


而在 index.js 中,我們替換掉 lodash 的引用,而引用 app 這個(gè)模塊。

const { app } = require('./app');

JavaScript

引用后,當(dāng)我們?cè)?index.js 書(shū)寫(xiě)代碼時(shí),就能夠得到 app 這個(gè)函數(shù)的提示了。

對(duì)于 app.js 這個(gè)模塊,VS Code 的處理思路跟處理 lodash 那個(gè) npm 包的引用是一樣的。VS Code 先是通過(guò) ./app 這個(gè)相對(duì)路徑找到模塊,然后看這個(gè)模塊里 export 哪些函數(shù)和對(duì)象,接著在 index.js 提供提示。

但是,JavaScript 程序員都知道,JavaScript 世界里的模塊系統(tǒng),從來(lái)都沒(méi)有一個(gè)廣泛接受的標(biāo)準(zhǔn),即使有標(biāo)準(zhǔn),社區(qū)也會(huì)使用一些前沿的、還在測(cè)試探討階段的新語(yǔ)法,常見(jiàn)的模塊有: AMD、CommonJS、ECMAScript 6 Module,等等。而社區(qū)廣泛使用的打包工具 Webpack,更是允許你各種自定義模塊的引用方式。這時(shí)VS Code 的 JavaScript 語(yǔ)言服務(wù),要想同時(shí)支持所有的模塊系統(tǒng),可就頭疼了。

舉個(gè)最簡(jiǎn)單的例子——模塊絕對(duì)路徑引用。在上面的例子里,我們引用 app 這個(gè)模塊時(shí),使用的是相對(duì)地址:

const { app } = require('./app');

JavaScript

這個(gè)很好處理,我們到同一個(gè)目錄下去找這個(gè) app 模塊就行了。而如果使用了絕對(duì)路徑,比如:

const { app } = require('app');

JavaScript

這個(gè)就比較麻煩了。JavaScript 的語(yǔ)言就不知道,究竟是把這個(gè)當(dāng)作 npm 包,然后到 node_modules 文件夾下去尋找呢,還是從當(dāng)前項(xiàng)目的根目錄下去尋找這個(gè)模塊呢?如果項(xiàng)目中使用了 babel 或者 webpack 的話,又該怎么去理解這些工具對(duì)模塊的處理呢?

此時(shí),在 VS Code 的編輯器里,我們已經(jīng)能看到錯(cuò)誤提示了。


jsconfig

為了解決這個(gè)問(wèn)題,我們需要引入一個(gè)特殊的配置文件 jsconfig.json。這個(gè)配置文件用于提示 JavaScript 語(yǔ)言服務(wù),當(dāng)前項(xiàng)目的 JavaScript 代碼,哪些文件是屬于這個(gè)項(xiàng)目的?JS 語(yǔ)法是哪個(gè)版本?而又該去哪里尋找模塊?

下面,讓我們?cè)谖募A下創(chuàng)建如下內(nèi)容的 jsconfig.json 文件:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2016",
        "baseUrl": "."
    },
    "exclude": [
        "node_modules",
        "**/node_modules/*"
    ]
}

JSON

這個(gè)文件告訴 VS Code ,當(dāng)前項(xiàng)目里的 JavaScript 語(yǔ)法是使用 es2016 的,引用模塊時(shí),如果模塊的名稱是絕對(duì)路徑,那么請(qǐng)從根目錄(也就是 .)開(kāi)始尋找。

有了這個(gè)文件之后,VS Code 的 JavaScript 語(yǔ)言服務(wù)立刻就找到 app 這個(gè)模塊了。


jsconfig 甚至還允許指定多個(gè)文件夾依次進(jìn)行模塊的查找。下面我們創(chuàng)建一個(gè)文件夾 common,然后把 app.js 挪入到 common 中。


然后在 jsconfig 中,進(jìn)行如下的修改:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2016",
        "baseUrl": ".",
        "paths": {
            "*": [
                "*",
                "common/*"
            ]
        }
    },
    "exclude": [
        "node_modules",
        "**/node_modules/*"
    ]
}

JSON

我們?cè)谏厦娴?jsconfig 里添加一個(gè) paths 屬性,這里面就是對(duì)模塊路徑的映射。VS Code 會(huì)閱讀這些映射,依次去尋找模塊。此時(shí)當(dāng)我們?cè)俅蜷_(kāi) index.js ,將鼠標(biāo)移動(dòng)到 require(‘a(chǎn)pp’) 代碼上時(shí),我們能夠看到,此時(shí) VS Code 正確地將 app 這個(gè)模塊定位到了 common/app.js 這個(gè)文件。


在前面的專欄里,我們?cè)诮榻B VS Code 的智能語(yǔ)言服務(wù)功能時(shí),有讀者提了一個(gè)非常好的問(wèn)題,那就是如果項(xiàng)目中使用了 Webpack 而且 Webpack 中設(shè)置了 resolve,那么 VS Code 里的代碼跳轉(zhuǎn)就不工作了。相信現(xiàn)在你應(yīng)該已經(jīng)明白是為什么了,如果你的項(xiàng)目里為 Webpack 做了特殊的模塊地址映射,那么你也需要在 jsconfig 里做同樣的映射,這樣 VS Code 就知道如何找到模塊了。

既然說(shuō)到模塊,接下來(lái)就介紹兩個(gè)非常有用的模塊相關(guān)的功能。

自動(dòng)模塊引用

首先我們把 index.js 最上面的模塊引用刪除。然后在 index.js 的末尾,輸入 app() 這段代碼。

你可以看到,VS Code 自動(dòng)為我們添加了模塊的引用。這是因?yàn)?VS Code 已經(jīng)知道當(dāng)前項(xiàng)目里有哪些模塊,每個(gè)模塊提供了哪些方法,當(dāng)你使用它們時(shí),就會(huì)幫你將引用填入文件開(kāi)頭。

這個(gè)功能又叫自動(dòng)模塊引用(Auto Imports)。

自動(dòng)模塊更新

JavaScript 的模塊地址很多都是跟文件地址相關(guān)的,閱讀性很好,但是當(dāng)我們移動(dòng)文件地址的時(shí)候,可就麻煩了。因?yàn)檫@意味著我們需要把所有跟這個(gè)文件相關(guān)的模塊引用全部修改一遍。不過(guò)好在VS Code提供了模塊地址自動(dòng)更新的功能。

下面我們把 app.js 從 common 這個(gè)文件夾下,移動(dòng)到根目錄下。

從上面的動(dòng)圖中,你能夠看到,VS Code 會(huì)問(wèn)你是否要自動(dòng)修改模塊的引用,在選擇“是”之后,VS Code 就會(huì)把 app 這個(gè)模塊的引用地址,從 “common/app” 改成了 “app”。是不是非常實(shí)用呢?

代碼審查 tsconfig/checkJs

上面我們提到了,可以通過(guò)在 JavaScript 文件里寫(xiě)入 @ts-check 注釋,讓 VS Code 對(duì)單個(gè)文件進(jìn)行代碼審查。不過(guò)如果項(xiàng)目不算大,而你打算對(duì)所有的 JavaScript 文件進(jìn)行代碼檢查的話,那么也可以在 jsconfig.json 文件的 compilerOptions 里添加 checkJs 屬性,打開(kāi)對(duì) JavaScript 的檢查。修改后的 jsconfig.json 如下:

{
    "compilerOptions": {
        "module": "commonjs",
        "target": "es2016",
        "baseUrl": ".",
        "paths": {
            "*": [
                "*",
                "common/*"
            ]
        },
        "checkJs": true
    },
    "exclude": [
        "node_modules",
        "**/node_modules/*"
    ]
}

JSON

其實(shí),jsconfig 還有很多其他實(shí)用的設(shè)置,你不妨利用 VS Code 的自動(dòng)補(bǔ)全和代碼提示,自己動(dòng)手改一改,看看它們都是干嘛的。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)