<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從零開始實現(xiàn)VS Code基金插件

          共 14023字,需瀏覽 29分鐘

           ·

          2020-09-14 21:23

          寫在前面

          隨著7月一波牛市行情,越來越多的人投身A股行列,但是股市的風(fēng)險巨大,有人一夜暴富,也有人血本無歸,所以對于普通人來說基金定投是個不錯的選擇,本人也是基金定投的一枚小韭菜。

          基金定投

          上班的時候經(jīng)常心理癢癢,想看看今天的基金又賺(ge)了多少錢,拿出手機打開支付寶的步驟過于繁瑣,而且我也不太關(guān)心其他的指標(biāo),只是想知道今天的凈值與漲幅。VS Code 做為一個編碼工具,提供了強大的插件機制,我們可以好好利用這個能力,可以一邊編碼的時候一邊看看行情。

          示例

          實現(xiàn)插件

          初始化

          VSCode 官方提供了非常方便的插件模板,我們可以直接通過 Yeoman 來生成 VS Code 插件的模板。

          先全局安裝 yo 和 generator-code,運行命令 yo code

          #?全局安裝?yo?模塊
          npm?install?-g?yo?generator-code

          這里我們使用 TypeScript 來編寫插件。

          yo code
          yo code

          生成后的目錄結(jié)構(gòu)如下:

          目錄結(jié)構(gòu)

          VS Code 插件可以簡單理解為一個 Npm 包,也需要一個 package.json 文件,屬性與 Npm 包的基本一致。

          {
          ??//?名稱
          ??"name":?"fund-watch",
          ??//?版本
          ??"version":?"1.0.0",
          ??//?描述
          ??"description":?"實時查看基金行情",
          ??//?發(fā)布者
          ??"publisher":?"shenfq",
          ??//?版本要求
          ??"engines":?{
          ????"vscode":?"^1.45.0"
          ??},
          ??//?入口文件
          ??"main":?"./out/extension.js",
          ??"scripts":?{
          ????"compile":?"tsc?-p?./",
          ????"watch":?"tsc?-watch?-p?./",
          ??},
          ??"devDependencies":?{
          ????"@types/node":?"^10.14.17",
          ????"@types/vscode":?"^1.41.0",
          ????"typescript":?"^3.9.7"
          ??},
          ??//?插件配置
          ??"contributes":?{},
          ??//?激活事件
          ??"activationEvents":?[],
          }

          簡單介紹下其中比較重要的配置。

          • contributes:插件相關(guān)配置。
          • activationEvents:激活事件。
          • main:插件的入口文件,與 Npm 包表現(xiàn)一致。
          • namepublisher:name 是插件名,publisher 是發(fā)布者。${publisher}.${name} 構(gòu)成插件 ID。

          比較值得關(guān)注的就是 contributesactivationEvents 這兩個配置。

          創(chuàng)建視圖

          我們首先在我們的應(yīng)用中創(chuàng)建一個視圖容器,視圖容器簡單來說一個單獨的側(cè)邊欄,在 package.jsoncontributes.viewsContainers 中進行配置。

          {
          ??"contributes":?{
          ????"viewsContainers":?{
          ??????"activitybar":?[
          ????????{
          ??????????"id":?"fund-watch",
          ??????????"title":?"FUND?WATCH",
          ??????????"icon":?"images/fund.svg"
          ????????}
          ??????]
          ????}
          ??}
          }
          側(cè)邊欄

          然后我們還需要添加一個視圖,在 package.jsoncontributes.views 中進行配置,該字段為一個對象,它的 Key 就是我們視圖容器的 id,值為一個數(shù)組,表示一個視圖容器內(nèi)可添加多個視圖。

          {
          ??"contributes":?{
          ????"viewsContainers":?{
          ??????"activitybar":?[
          ????????{
          ??????????"id":?"fund-watch",
          ??????????"title":?"FUND?WATCH",
          ??????????"icon":?"images/fund.svg"
          ????????}
          ??????]
          ????},
          ????"views":?{
          ??????"fund-watch":?[
          ????????{
          ??????????"name":?"自選基金",
          ??????????"id":?"fund-list"
          ????????}
          ??????]
          ????}
          ??}
          }

          如果你不希望在自定義的視圖容器中添加,可以選擇 VS Code 自帶的視圖容器。

          • explorer: 顯示在資源管理器側(cè)邊欄
          • debug: 顯示在調(diào)試側(cè)邊欄
          • scm: 顯示在源代碼側(cè)邊欄
          {
          ??"contributes":?{
          ????"views":?{
          ??????"explorer":?[
          ????????{
          ??????????"name":?"自選基金",
          ??????????"id":?"fund-list"
          ????????}
          ??????]
          ????}
          ??}
          }
          顯示到資源管理器中

          運行插件

          使用 Yeoman 生成的模板自帶 VS Code 運行能力。

          vscode配置

          切換到調(diào)試面板,直接點擊運行,就能看到側(cè)邊欄多了個圖標(biāo)。

          調(diào)試面板
          運行結(jié)果

          添加配置

          我們需要獲取基金的列表,當(dāng)然需要一些基金代碼,而這些代碼我們可以放到 VS Code 的配置中。

          {
          ??"contributes":?{
          ????//?配置
          ????"configuration":?{
          ??????//?配置類型,對象
          ??????"type":?"object",
          ??????//?配置名稱
          ??????"title":?"fund",
          ??????//?配置的各個屬性
          ??????"properties":?{
          ????????//?自選基金列表
          ????????"fund.favorites":?{
          ??????????//?屬性類型
          ??????????"type":?"array",
          ??????????//?默認值
          ??????????"default":?[
          ????????????"163407",
          ????????????"161017"
          ??????????],
          ??????????//?描述
          ??????????"description":?"自選基金列表,值為基金代碼"
          ????????},
          ????????//?刷新時間的間隔
          ????????"fund.interval":?{
          ??????????"type":?"number",
          ??????????"default":?2,
          ??????????"description":?"刷新時間,單位為秒,默認?2?秒"
          ????????}
          ??????}
          ????}
          ??}
          }

          視圖數(shù)據(jù)

          我們回看之前注冊的視圖,VS Code 中稱為樹視圖。

          "views":?{
          ??"fund-watch":?[
          ????{
          ??????"name":?"自選基金",
          ??????"id":?"fund-list"
          ????}
          ??]
          }

          我們需要通過 vscode 提供的 registerTreeDataProvider 為視圖提供數(shù)據(jù)。打開生成的 src/extension.ts 文件,修改代碼如下:

          //?vscode?模塊為?VS?Code?內(nèi)置,不需要通過?npm?安裝
          import?{?ExtensionContext,?commands,?window,?workspace?}?from?'vscode';
          import?Provider?from?'./Provider';

          //?激活插件
          export?function?activate(context:?ExtensionContext)?{
          ??//?基金類
          ??const?provider?=?new?Provider();

          ??//?數(shù)據(jù)注冊
          ??window.registerTreeDataProvider('fund-list',?provider);
          }

          export?function?deactivate()?{}

          這里我們通過 VS Code 提供的 window.registerTreeDataProvider 來注冊數(shù)據(jù),傳入的第一個參數(shù)表示視圖 ID,第二個參數(shù)是 TreeDataProvider 的實現(xiàn)。

          TreeDataProvider 有兩個必須實現(xiàn)的方法:

          • getChildren:該方法接受一個 element,返回 element 的子元素,如果沒有element,則返回的是根節(jié)點的子元素,我們這里因為是單列表,所以不會接受 element 元素;
          • getTreeItem:該方法接受一個 element,返回視圖單行的 UI 數(shù)據(jù),需要對 TreeItem 進行實例化;

          我們通過 VS Code 的資源管理器來展示下這兩個方法:

          方法展示

          有了上面的知識,我們就可以輕松為樹視圖提供數(shù)據(jù)了。

          import?{?workspace,?TreeDataProvider,?TreeItem?}?from?'vscode';

          export?default?class?DataProvider?implements?TreeDataProvider<string>?{
          ??refresh()?{
          ????//?更新視圖
          ??}

          ??getTreeItem(element:?string):?TreeItem?{
          ????return?new?TreeItem(element);
          ??}

          ??getChildren():?string[]?{
          ????const?{?order?}?=?this;
          ????//?獲取配置的基金代碼
          ????const?favorites:?string[]?=?workspace
          ??????.getConfiguration()
          ??????.get('fund-watch.favorites',?[]);
          ????
          ????//?依據(jù)代碼排序
          ??return?favorites.sort((prev,?next)?=>?(prev?>=?next???1?:?-1)?*?order);
          ??}
          }


          現(xiàn)在運行之后,可能會發(fā)現(xiàn)視圖上沒有數(shù)據(jù),這是因為沒有配置激活事件。

          {
          ?"activationEvents":?[
          ????//?表示?fund-list?視圖展示時,激活該插件
          ??"onView:fund-list"
          ?]
          }
          基金代碼列表

          請求數(shù)據(jù)

          我們已經(jīng)成功將基金代碼展示在視圖上,接下來就需要請求基金數(shù)據(jù)了。網(wǎng)上有很多基金相關(guān) api,這里我們使用天天基金網(wǎng)的數(shù)據(jù)。

          天天基金網(wǎng)

          通過請求可以看到,天天基金網(wǎng)通過 JSONP 的方式獲取基金相關(guān)數(shù)據(jù),我們只需要構(gòu)造一個 url,并傳入當(dāng)前時間戳即可。

          const?url?=?`https://fundgz.1234567.com.cn/js/${code}.js?rt=${time}`

          VS Code 中請求數(shù)據(jù),需要使用內(nèi)部提供的 https 模塊,下面我們新建一個 api.ts

          import?*?as?https?from?'https';

          //?發(fā)起?GET?請求
          const?request?=?async?(url:?string):?Promise<string>?=>?{
          ??return?new?Promise((resolve,?reject)?=>?{
          ????https.get(url,?(res)?=>?{
          ??????let?chunks?=?'';
          ??????if?(!res?||?res.statusCode?!==?200)?{
          ????????reject(new?Error('網(wǎng)絡(luò)請求錯誤!'));
          ????????return;
          ??????}
          ??????res.on('data',?(chunk)?=>?chunks?+=?chunk.toString('utf8'));
          ??????res.on('end',?()?=>?resolve(chunks));
          ????});
          ??});
          };

          interface?FundInfo?{
          ??now:?string
          ??name:?string
          ??code:?string
          ??lastClose:?string
          ??changeRate:?string
          ??changeAmount:?string
          }

          //?根據(jù)基金代碼請求基金數(shù)據(jù)
          export?default?function?fundApi(codes:?string[]):?Promise<FundInfo[]>?{
          ??const?time?=?Date.now();
          ?//?請求列表
          ??const?promises:?Promise<string>[]?=?codes.map((code)?=>?{
          ????const?url?=?`https://fundgz.1234567.com.cn/js/${code}.js?rt=${time}`;
          ????return?request(url);
          ??});
          ??return?Promise.all(promises).then((results)?=>?{
          ????const?resultArr:?FundInfo[]?=?[];
          ????results.forEach((rsp:?string)?=>?{
          ??????const?match?=?rsp.match(/jsonpgz\((.+)\)/);
          ??????if?(!match?||?!match[1])?{
          ????????return;
          ??????}
          ??????const?str?=?match[1];
          ??????const?obj?=?JSON.parse(str);
          ??????const?info:?FundInfo?=?{
          ????????//?當(dāng)前凈值
          ????????now:?obj.gsz,
          ????????//?基金名稱
          ????????name:?obj.name,
          ????????//?基金代碼
          ????????code:?obj.fundcode,
          ????????//?昨日凈值
          ????????lastClose:?obj.dwjz,
          ????????//?漲跌幅
          ????????changeRate:?obj.gszzl,
          ????????//?漲跌額
          ????????changeAmount:?(obj.gsz?-?obj.dwjz).toFixed(4),
          ??????};
          ??????resultArr.push(info);
          ????});
          ????return?resultArr;
          ??});
          }

          接下來修改視圖數(shù)據(jù)。

          import?{?workspace,?TreeDataProvider,?TreeItem?}?from?'vscode';
          import?fundApi?from?'./api';

          export?default?class?DataProvider?implements?TreeDataProvider<FundInfo>?{
          ??//?省略了其他代碼
          ??getTreeItem(info:?FundInfo):?TreeItem?{
          ????//?展示名稱和漲跌幅
          ???const?{?name,?changeRate?}?=?info
          ????return?new?TreeItem(`${name}??${changeRate}`);
          ??}

          ??getChildren():?Promise?{
          ????const?{?order?}?=?this;
          ????//?獲取配置的基金代碼
          ????const?favorites:?string[]?=?workspace
          ??????.getConfiguration()
          ??????.get('fund-watch.favorites',?[]);
          ????
          ????//?獲取基金數(shù)據(jù)
          ??return?fundApi([...favorites]).then(
          ??????(results:?FundInfo[])?=>?results.sort(
          ???????(prev,?next)?=>?(prev.changeRate?>=?next.changeRate???1?:?-1)?*?order
          ?????)
          ????);
          ??}
          }

          視圖數(shù)據(jù)

          美化格式

          前面我們都是通過直接實例化 TreeItem 的方式來實現(xiàn) UI 的,現(xiàn)在我們需要重新構(gòu)造一個 TreeItem

          import?{?workspace,?TreeDataProvider,?TreeItem?}?from?'vscode';
          import?FundItem?from?'./TreeItem';
          import?fundApi?from?'./api';

          export?default?class?DataProvider?implements?TreeDataProvider<FundInfo>?{
          ??//?省略了其他代碼
          ??getTreeItem(info:?FundInfo):?FundItem?{
          ????return?new?FundItem(info);
          ??}
          }
          //?TreeItem
          import?{?TreeItem?}?from?'vscode';

          export?default?class?FundItem?extends?TreeItem?{
          ??info:?FundInfo;

          ??constructor(info:?FundInfo)?{
          ????const?icon?=?Number(info.changeRate)?>=?0???'?'?:?'?';

          ????//?加上?icon,更加直觀的知道是漲還是跌
          ????super(`${icon}${info.name}???${info.changeRate}%`);

          ????let?sliceName?=?info.name;
          ????if?(sliceName.length?>?8)?{
          ??????sliceName?=?`${sliceName.slice(0,?8)}...`;
          ????}
          ????const?tips?=?[
          ??????`代碼:?${info.code}`,
          ??????`名稱:?${sliceName}`,
          ??????`--------------------------`,
          ??????`單位凈值:????${info.now}`,
          ??????`漲跌幅:?????${info.changeRate}%`,
          ??????`漲跌額:?????${info.changeAmount}`,
          ??????`昨收:??????${info.lastClose}`,
          ????];

          ????this.info?=?info;
          ????//?tooltip?鼠標(biāo)懸停時,展示的內(nèi)容
          ????this.tooltip?=?tips.join('\r\n');
          ??}
          }
          美化后

          更新數(shù)據(jù)

          TreeDataProvider 需要提供一個 onDidChangeTreeData 屬性,該屬性是 EventEmitter 的一個實例,然后通過觸發(fā) EventEmitter 實例進行數(shù)據(jù)的更新,每次調(diào)用 refresh 方法相當(dāng)于重新調(diào)用了 getChildren 方法。

          import?{?workspace,?Event,?EventEmitter,?TreeDataProvider?}?from?'vscode';
          import?FundItem?from?'./TreeItem';
          import?fundApi?from?'./api';

          export?default?class?DataProvider?implements?TreeDataProvider<FundInfo>?{
          ??private?refreshEvent:?EventEmitternull>?=?new?EventEmitternull>();
          ??readonly?onDidChangeTreeData:?Eventnull>?=?this.refreshEvent.event;

          ??refresh()?{
          ????//?更新視圖
          ????setTimeout(()?=>?{
          ??????this.refreshEvent.fire(null);
          ????},?200);
          ??}
          }

          我們回到 extension.ts,添加一個定時器,讓數(shù)據(jù)定時更新。

          import?{?ExtensionContext,?commands,?window,?workspace?}?from?'vscode'
          import?Provider?from?'./data/Provider'

          //?激活插件
          export?function?activate(context:?ExtensionContext)?{
          ??//?獲取?interval?配置
          ??let?interval?=?workspace.getConfiguration().get('fund-watch.interval',?2)
          ??if?(interval?2)?{
          ????interval?=?2
          ??}

          ??//?基金類
          ??const?provider?=?new?Provider()

          ??//?數(shù)據(jù)注冊
          ??window.registerTreeDataProvider('fund-list',?provider)

          ??//?定時更新
          ??setInterval(()?=>?{
          ????provider.refresh()
          ??},?interval?*?1000)
          }

          export?function?deactivate()?{}

          除了定時更新,我們還需要提供手動更新的能力。修改 package.json,注冊命令。

          {
          ??"contributes":?{
          ??"commands":?[
          ???{
          ????"command":?"fund.refresh",
          ????"title":?"刷新",
          ????"icon":?{
          ?????"light":?"images/light/refresh.svg",
          ?????"dark":?"images/dark/refresh.svg"
          ????}
          ???}
          ??],
          ??"menus":?{
          ???"view/title":?[
          ????{
          ?????"when":?"view?==?fund-list",
          ?????"group":?"navigation",
          ?????"command":?"fund.refresh"
          ????}
          ???]
          ??}
          ?}
          }
          • commands:用于注冊命令,指定命令的名稱、圖標(biāo),以及 command 用于 extension 中綁定相應(yīng)事件;
          • menus:用于標(biāo)記命令展示的位置;
            • when:定義展示的視圖,具體語法可以查閱官方文檔;
            • group:定義菜單的分組;
            • command:定義命令調(diào)用的事件;
          view-actions

          配置好命令后,回到 extension.ts 中。

          import?{?ExtensionContext,?commands,?window,?workspace?}?from?'vscode';
          import?Provider?from?'./Provider';

          //?激活插件
          export?function?activate(context:?ExtensionContext)?{
          ??let?interval?=?workspace.getConfiguration().get('fund-watch.interval',?2);
          ??if?(interval?2)?{
          ????interval?=?2;
          ??}

          ??//?基金類
          ??const?provider?=?new?Provider();

          ??//?數(shù)據(jù)注冊
          ??window.registerTreeDataProvider('fund-list',?provider);

          ??//?定時任務(wù)
          ??setInterval(()?=>?{
          ????provider.refresh();
          ??},?interval?*?1000);

          ??//?事件
          ??context.subscriptions.push(
          ????commands.registerCommand('fund.refresh',?()?=>?{
          ??????provider.refresh();
          ????}),
          ??);
          }

          export?function?deactivate()?{}

          現(xiàn)在我們就可以手動刷新了。

          image-20200824113219392

          新增基金

          我們新增一個按鈕用了新增基金。

          {
          ??"contributes":?{
          ??"commands":?[
          ??????{
          ????????"command":?"fund.add",
          ????????"title":?"新增",
          ????????"icon":?{
          ??????????"light":?"images/light/add.svg",
          ??????????"dark":?"images/dark/add.svg"
          ????????}
          ??????},
          ???{
          ????"command":?"fund.refresh",
          ????"title":?"刷新",
          ????"icon":?{
          ?????"light":?"images/light/refresh.svg",
          ?????"dark":?"images/dark/refresh.svg"
          ????}
          ???}
          ??],
          ??"menus":?{
          ???"view/title":?[
          ????????{
          ??????????"command":?"fund.add",
          ??????????"when":?"view?==?fund-list",
          ??????????"group":?"navigation"
          ????????},
          ????{
          ?????"when":?"view?==?fund-list",
          ?????"group":?"navigation",
          ?????"command":?"fund.refresh"
          ????}
          ???]
          ??}
          ?}
          }

          extension.ts 中注冊事件。

          import?{?ExtensionContext,?commands,?window,?workspace?}?from?'vscode';
          import?Provider?from?'./Provider';

          //?激活插件
          export?function?activate(context:?ExtensionContext)?{
          ??//?省略部分代碼?...
          ??
          ??//?基金類
          ??const?provider?=?new?Provider();

          ??//?事件
          ??context.subscriptions.push(
          ????commands.registerCommand('fund.add',?()?=>?{
          ??????provider.addFund();
          ????}),
          ????commands.registerCommand('fund.refresh',?()?=>?{
          ??????provider.refresh();
          ????}),
          ??);
          }

          export?function?deactivate()?{}

          實現(xiàn)新增功能,修改 Provider.ts

          import?{?workspace,?Event,?EventEmitter,?TreeDataProvider?}?from?'vscode';
          import?FundItem?from?'./TreeItem';
          import?fundApi?from?'./api';

          export?default?class?DataProvider?implements?TreeDataProvider<FundInfo>?{
          ??//?省略部分代碼?...

          ??//?更新配置
          ??updateConfig(funds:?string[])?{
          ????const?config?=?workspace.getConfiguration();
          ????const?favorites?=?Array.from(
          ??????//?通過?Set?去重
          ??????new?Set([
          ????????...config.get('fund-watch.favorites',?[]),
          ????????...funds,
          ??????])
          ????);
          ????config.update('fund-watch.favorites',?favorites,?true);
          ??}

          ??async?addFund()?{
          ????//?彈出輸入框
          ????const?res?=?await?window.showInputBox({
          ??????value:?'',
          ??????valueSelection:?[5,?-1],
          ??????prompt:?'添加基金到自選',
          ??????placeHolder:?'Add?Fund?To?Favorite',
          ??????validateInput:?(inputCode:?string)?=>?{
          ????????const?codeArray?=?inputCode.split(/[\W]/);
          ????????const?hasError?=?codeArray.some((code)?=>?{
          ??????????return?code?!==?''?&&?!/^\d+$/.test(code);
          ????????});
          ????????return?hasError???'基金代碼輸入有誤'?:?null;
          ??????},
          ????});
          ????if?(!!res)?{
          ??????const?codeArray?=?res.split(/[\W]/)?||?[];
          ??????const?result?=?await?fundApi([...codeArray]);
          ??????if?(result?&&?result.length?>?0)?{
          ????????//?只更新能正常請求的代碼
          ????????const?codes?=?result.map(i?=>?i.code);
          ????????this.updateConfig(codes);
          ????????this.refresh();
          ??????}?else?{
          ????????window.showWarningMessage('stocks?not?found');
          ??????}
          ????}
          ??}
          }
          新增按鈕
          輸入框

          刪除基金

          最后新增一個按鈕,用來刪除基金。

          {
          ?"contributes":?{
          ??"commands":?[
          ???{
          ????"command":?"fund.item.remove",
          ????"title":?"刪除"
          ???}
          ??],
          ??"menus":?{
          ??????//?這個按鈕放到?context?中
          ??????"view/item/context":?[
          ????????{
          ??????????"command":?"fund.item.remove",
          ??????????"when":?"view?==?fund-list",
          ??????????"group":?"inline"
          ????????}
          ??????]
          ??}
          ??}
          }

          extension.ts 中注冊事件。

          import?{?ExtensionContext,?commands,?window,?workspace?}?from?'vscode';
          import?Provider?from?'./Provider';

          //?激活插件
          export?function?activate(context:?ExtensionContext)?{
          ??
          //?省略部分代碼?...
          ??

          ??
          //?基金類
          ??
          const?provider?=?new?Provider();

          ??
          //?事件
          ??context.subscriptions.push(

          ????commands.registerCommand(
          'fund.add',?()?=>?{
          ??????provider.addFund();

          ????}),

          ????commands.registerCommand(
          'fund.refresh',?()?=>?{
          ??????provider.refresh();

          ????}),

          ????commands.registerCommand(
          'fund.item.remove',?(fund)?=>?{
          ??????
          const?{ code }?=?fund;
          ??????provider.removeConfig(code);

          ??????provider.refresh();

          ????})

          ??);

          }


          export?function?deactivate()?{}

          實現(xiàn)新增功能,修改 Provider.ts

          import?{?window,?workspace,?Event,?EventEmitter,?TreeDataProvider?}?from?'vscode';
          import?FundItem?from?'./TreeItem';
          import?fundApi?from?'./api';

          export?default?class?DataProvider?implements?TreeDataProvider<FundInfo>?{
          ??//?省略部分代碼?...

          ??//?刪除配置
          ??removeConfig(code:?string)?{
          ????const?config?=?workspace.getConfiguration();
          ????const?favorites:?string[]?=?[...config.get('fund-watch.favorites',?[])];
          ????const?index?=?favorites.indexOf(code);
          ????if?(index?===?-1)?{
          ??????return;
          ????}
          ????favorites.splice(index,?1);
          ????config.update('fund-watch.favorites',?favorites,?true);
          ??}
          }
          刪除按鈕

          總結(jié)

          實現(xiàn)過程中也遇到了很多問題,遇到問題可以多翻閱 VSCode 插件中文文檔。該插件已經(jīng)發(fā)布的了 VS Code 插件市場,感興趣的可以直接下載該插件,或者在 github 上下載完整代碼。

          》》面試官都在用的題庫,快來看看《

          瀏覽 44
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  久草福利观看 | 久久成人大香蕉 | 偷拍视频网站 | 福利在线视频中文 | 亚洲AV高清无码 |