VSCODE 版 CNode 已上線

2022-01-08 JS 酷
1. 前言

本篇是 VS Code 插件開發實戰系列第三篇,前面兩篇是

《一起來寫 VS Code 插件:為你的團隊提供常用代碼片段》

《一起來寫 VS Code 插件:實現一個翻譯插件》

「CNode」 社區為國內最專業的 Node.js 開源技術社區,致力於 Node.js 的技術研究。本篇將通過實現 VS Code 版 CNode, 來帶領大家一起熟悉 VSCode Webview 強大的功能。在開始之前,我們先參考 官網關於 webview 的介紹。Webview API 允許擴展在 visualstudio 代碼中創建完全可定製的視圖,可以將 webview 看作是 VS Code 中的 iframe。

我們可以通過網頁將事件消息傳遞給我們的服務端(包括 NodeJS), 服務端處理完後可以把消息數據傳遞給網頁。因此我們能在 extensions 中開發出跟網頁一樣的內容,但實現遠比網頁更強大的功能。

2. 效果

首先來看下實現的效果

主要分為 2 部分,左側是主題列表,右側是主題詳情。

3. 初始化項目

首先通過腳手架初始化一個 typescript + webpack 的工程

4. 配置左側導航圖標

"icon": "icon.png",
"activationEvents": [
"onView:vs-sidebar-view"
],
"contributes": {
"viewsContainers": {
"activitybar": [
{
"id": "vs-sidebar-view",
"title": "CNODE 社區",
"icon": "media/cnode_icon_64.png"
}
]
},
"views": {
"vs-sidebar-view": [
{
"type": "webview",
"id": "vs-sidebar-view",
"name": "Topic 列表",
"icon": "media/cnode_icon_64.png",
"contextualTitle": "Topic 列表"
}
]
}
},
...

views 是配置視圖列表,activitybar 是定義下顯示在側邊導航上的視圖。

4.1. 註冊一個側邊欄

在 extension.ts 中註冊一個 與 package.json 對應的 vs-sidebar-view側邊欄ID

import * as vscode from "vscode";
import { SidebarProvider } from "./SidebarProvider";

export function activate(context: vscode.ExtensionContext) {
const sidebarPanel = new SidebarProvider(context.extensionUri);
context.subscriptions.push(
vscode.window.registerWebviewViewProvider("vs-sidebar-view", sidebarPanel)
);
}

4.2. 實現側邊欄

import * as vscode from "vscode";
import { getNonce } from "./getNonce";

export class SidebarProvider implements vscode.WebviewViewProvider {
_view?: vscode.WebviewView;
_doc?: vscode.TextDocument;
constructor(private readonly _extensionUri: vscode.Uri) {}

public resolveWebviewView(webviewView: vscode.WebviewView) {
this._view = webviewView;

webviewView.webview.options = {
// 在 webview 允許腳本
enableScripts: true,
localResourceRoots: [this._extensionUri],
};

webviewView.webview.html = this._getHtmlForWebview(webviewView.webview);
}

public revive(panel: vscode.WebviewView) {
this._view = panel;
}

private _getHtmlForWebview(webview: vscode.Webview) {
const scriptUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "build", "static/js/main.js")
);
const styleMainUri = webview.asWebviewUri(
vscode.Uri.joinPath(this._extensionUri, "build", "main.css")
);

// Use a nonce to 只允許特定腳本運行.
const nonce = getNonce();

return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="Content-Security-Policy" content="img-src https: data:; style-src 'unsafe-inline' ${webview.cspSource}; script-src 'nonce-${nonce}';">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="${styleMainUri}" rel="stylesheet">
<script nonce="${nonce}">
const tsvscode = acquireVsCodeApi(); //內置函數,可以訪問 VS Code API 對象
const apiBaseUrl = 'https://cnodejs.org/'
</script>
</head>
<body>
<section id="root"></section>
<script nonce="${nonce}" src="${scriptUri}"></script>
</body>
</html>`;
}
}

上述代碼採用面向對象的方式實現一個 SidebarProvider類,根據 vscode.WebviewViewProvider,
其實實現所有的 WebviewViewProvider 都是是這段代碼,其他代碼都是相同的,因為關於 webview 中的 HTML 我們都可以使用js來生成,這不正是我們的單頁面應用開發嗎?

上述代碼中, 「Nonce」是一個在加密通信只能使用一次的數字。在認證協議中,它往往是一個隨機或偽隨機數,以避免重放攻擊。Nonce也用於流密碼以確保安全。如果需要使用相同的密鑰加密一個以上的消息,就需要Nonce來確保不同的消息與該密鑰加密的密鑰流不同。 所以我們直接拷貝官方demo 中的代碼。

// 生成特定隨機數
export function getNonce() {
let text = "";
const possible =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 32; i++) {
text += possible.charAt(Math.floor(Math.random() * possible.length));
}
return text;
}

5. 實現側邊視圖

CNode 提供了允許跨域的 API,我們可以在 js 中直接調用,如果你也想開發類似的功能請在HTTP headers 中加入

Access-Control-Allow-Origin: *

5.1. 配置 webpack config

在原先 webpack.config.js 中加入打包 React 的配置,webpack5 支持多份 config 配置。

const viewConfig = {
entry: "./view/index.tsx",
output: {
path: path.resolve(__dirname, "build"),
filename: "static/js/[name].js",
},
mode: "production",
plugins: [
new miniCssExtractPlugin(),
],
module: {
rules: [
{
test: /\.(ts|tsx)$/i,
loader: "ts-loader",
exclude: ["/node_modules/"],
},
{
test: /\.css$/i,
use: [miniCssExtractPlugin.loader, "css-loader", "postcss-loader"],
},
],
},
resolve: {
extensions: [".ts", ".tsx"],
},
}

module.exports = [extensionConfig, viewConfig];

然後啟動調試的時候,webpack就會自動打包了;

「注意」 這裡的 mode 必須設置為 production,webpack development 模式會使用 eval 來執行代碼,而 eval 在 VS Code webview 不允許執行。

5.2. 配置 tailwindcss

為了方便,我這邊使用了tailwindcss,因為我可以使用 tailwindcss-typography   這個插件,幫我生成漂亮的文章類型排版。

yarn add tailwindcss @tailwindcss/typography autoprefixer

使用命令初始化 tailwindcss config

npx tailwindcss init

module.exports = {
mode: "jit",
purge: ["./view/**/*.tsx"],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [require("@tailwindcss/typography")],
};

mode jit 是及時編譯模式 tailwindcss 2.1 版本加的,忽略掉我們不需要的css 代碼。

生成文章頁面的樣式

.markdown-preview {
@apply prose prose-lg max-w-full bg-white p-20;
}

5.3. 使用 React 來實現主題列表

使用 react 實現一個列表的代碼我這邊就不敘述了,跟我們平常寫業務沒什麼區別,最主要的是 「數據通信」,當我們點擊主題列表,右邊要打開一個新的 webview 頁面

const handleClick = (item: Topic) => {
setCurrent(item.id);

tsvscode.postMessage({ type: "detail", value: item });
};

根據官方例子

scripts-webview_to_extension.gif

Webviews 還可以將消息傳遞迴它們的擴展。這是通過在 webview 中的特殊 VS Code API 對象上使用 postMessage 函數來實現的。要訪問 VS Code API 對象,就要在 webview 中調用 acquireVsCodeApi函數。

定義TS 全局對象

import * as _vscode from "vscode";

declare global {
const tsvscode: {
postMessage: ({ type: string, value: any }) => void;
getState: () => any;
setState: (state: any) => void;
};
const apiBaseUrl: string;
}

接著就可以在 vs-sidebar-view 接收數據了。

webviewView.webview.onDidReceiveMessage(async (data) => {
switch (data.type) {
case "detail":
createPreviewPanel(this._extensionUri, data.value);
break;

default:
break;
}
});

收到數據後可以就可以打開一個預覽頁面。

6. 預覽頁面實現

function createPreviewPanel(topic: Topic){
// 創建一個新的 panel.
const panel = vscode.window.createWebviewPanel(
"cnode-preview",
"CNODE 技術社區",
column || vscode.ViewColumn.One,
{
// 在 webview 允許腳本
enableScripts: true,

// 限制 從 media 文件夾加載資源
localResourceRoots: [vscode.Uri.joinPath(extensionUri, "build")],
}
);

panel.webview.html = _getHtmlForWebview(panel.webview, topic);
}

可以使用 內置方法 vscode.window.createWebviewPanel 創建一個新的面板,並且接收主題數據。_getHtmlForWebview 與 SidebarProvider 中的 _getHtmlForWebview 一致,返回 HTML 即可。

7. 避免重複創建預覽頁

當然也可以通過 postMessage 傳遞屬性

「extension 端」

panel.webview.postMessage({text: 'hello'});

「webview 端」

window.addEventListener('message', event => {
const message = event.data;
console.log('Webview接收到的消息:', message);
}

8. 主題適配

VS Code 將主題分為三類,並在 body 元素中添加一個 class 來指示當前主題:

body.vscode-light {
color: black;
}

body.vscode-dark {
color: white;
}

body.vscode-high-contrast {
color: red;
}

Webviews 還可以使用 CSS 變量訪問 VS Code 主題顏色。這些變量名以 vscode 作為前綴,並用-替換.。例如 editor.foreground 變為 var (--vscode-editor-foreground)。

查看可用主題變量的主題顏色參考。還有一個擴展可以為變量提供智能建議。

9. 調試

要調試Webview不能直接把 VSCode 的開發者工具打開,直接打開你只能看到一個<webview></webview>標籤,看不到代碼,要看代碼需要按下Ctrl+Shift+P然後執行打開Webview開發工具,英文版應該是Open Webview Developer Tools:

從上圖也可以看的 在html標籤上注入了當前皮膚的 css 變量。

10. 狀態保持

與瀏覽器標籤不一樣的是,當 webview 移動到後臺又再次顯示時,webview 中的任何狀態都將丟失。因為 webview 是基於 iframe 實現的。

解決此問題的最佳方法是使你的 webview 無狀態,通過消息傳遞來保存webview的狀態。

10.1.  state

在 webview 的 js 中我們可以使用vscode.getState()和vscode.setState()方法來保存和恢復 JSON 可序列化狀態對象。當 webview 被隱藏時,即使 webview 內容本身被破壞,這些狀態仍然會保存。當然了,當 webview 被銷毀時,狀態將被銷毀。

10.2. 序列化

通過註冊WebviewPanelSerializer可以實現在VScode重啟後自動恢復你的webview,當然,序列化其實也是建立在getState和setState之上的。

註冊方法:vscode.window.registerWebviewPanelSerializer

10.3.  retainContextWhenHidden

對於具有非常複雜的UI或狀態且無法快速保存和恢復的webview,我們可以直接使用retainContextWhenHidden選項。設置retainContextWhenHidden: true後即使webview被隱藏到後臺其狀態也不會丟失。

儘管retainContextWhenHidden很有吸引力,但它需要很高的內存開銷,一般建議在實在沒辦法的時候才啟用。

getState和setState是持久化的首選方式,因為它們的性能開銷要比retainContextWhenHidden低得多。

11. 發布

關於發布可以看我的上一篇 一起來寫 VS Code 插件:為你的團隊提供常用代碼片段

12. 小結

本篇通過實現 VS Code 版 CNode 來幫我們熟悉 webview 的 api,當然還可以增加評論系統,創建主題,基於用戶系統可以實現點讚收藏等。

開發更複雜的功能,只缺你的想像力。例如:

韭菜盒子,做最好用的股票和基金插件

create-app 可視化CLI工具

「最後」

附上本插件的下載地址和源碼

同時 vscode extensions 開發門檻不高,歡迎大家嘗試,或者將有意思的 extensions 推薦在評論區。

希望這篇文章對大家有所幫助,也可以參考我往期的文章或者在評論區交流你的想法和心得,歡迎一起探索前端。

相關焦點

  • 微軟發布 vscode.dev,把 VSCode 帶入瀏覽器!
    翻譯:韓老師 + Google Translate早在 2019 年,當.dev 頂級域名開放時,我們趕緊註冊了 vscode.dev。github.dev 是深度集成到 GitHub 中的 Web 版 VS Code 定製實例。登錄是自動的,URL 格式如下 github.com 的 /organization/repo 模式,使你可以簡單地改變.com 到.dev 以編輯代碼。除了 GitHub 上的存儲庫,VS Code for the Web 還支持 Azure Repos(Azure DevOps 的一部分)。
  • 10 個 VSCode 摸魚神器,確定不試一下?
    小霸王開源地址:https://github.com/gamedilong/anes-repository趁著老闆不注意,我就可以愉快的玩耍了,能讓你在緊張的開發之餘在vscode裡發鬆身心。通過勞逸結合,提升開發效率。商店連結:https://marketplace.visualstudio.com/items?
  • 吹爆程序猿的VSCode彩虹屁插件,釘宮理惠,英雄聯盟版現已生成,你...
    「啾,我的可愛已成功導入」VSCode幾乎支持所有主流語言,是一款程序猿常用的代碼編譯器,同時,它也支持各類擴展程序,Rainbow Fart就是其中之一。為了有更好的編譯體驗,這款插件還可以支持暗黑模式。不過,我們最關心的是它如何召喚蘿莉音?當你打開編輯面板,輸入「import」,它就已經出現了!」啾,我的可愛已成功導入!
  • 這些都是前端工程師用的比較多的vscode插件
    在眾多前端代碼編輯工具中,我最喜歡的就是微軟的vscode。首先它十分輕便,不吃硬體,運行非常順暢;其次是其各種各樣的插件使得編程效率蹭蹭地往上提,爸爸媽媽再也不用擔心我要加班了(才怪!!!不加班是不可能的)下面推薦幾個我常用的vscode插件~一 Auto Close TagAuto Close Tag——標籤自動閉合插件。安裝之後再也不用手動輸入閉合標籤,減少錯誤的同時大大提高了編程效率。二 Auto Rename TagAuto Rename Tag——自動重命名插件。
  • 【Web逆向】【vscode插件】quokka pro 激活碼
    作者論壇帳號:濤之雨前言似乎是本地算法。。。(我也挺莫名其妙。。。)首先是看一下註冊成功後的效果:上圖左邊正常使用pro,右邊顯示正常「註冊」,有時候會彈出過期(如下圖)或者試用的窗口(如上圖右下角)分析算法又是根據node地常識和相關說明文檔[官方&中譯],找到相關的package.json配置,並且根據關鍵詞:licenseDetails在文件夾裡搜索,即可在dist文件夾中找到相關的函數:(因為相關代碼過長,不方便直接查看,因此這裡我放上截圖可能會好點(【本截圖生成的網站】,感覺效果還不錯)
  • Github開源工具之VsCode音樂插件Cloudmusic
    介紹Cloudmusic是網易雲音樂在vscode中的擴展插件,讓你使用vscode既可以寫代碼又可以聽音樂
  • webpack loader 與plugin 開發實戰 —— 點擊 vue 頁面元素跳轉到對應的 vscode 代碼
    參考 apihttps://webpack.docschina.org/api/plugins/整體思路要做到點擊元素能夠跳轉 vscode,首先需要某種手段打開 vscode,藉助一個 plugin 實現如下功能:打開 vscode:藉助 react 封裝的 launchEditor[5] 方法,可以識別各種編輯器並喚醒,原理是通過
  • VSCode 設置:彩虹花括號
    收錄於話題 #vscode
  • VScode開源音樂插件-VSC Netease Music
    使用網易音樂的API,基於Webview 實現,通過 HTMLAudioElement 播放音樂,不依賴命令行播放器,在 vscode 上划水聽音樂。
  • 剪映Mac 版已上線,Win版預計明年初上線
    目前,抖音旗下的視頻創作工具剪映已正式上線剪映Mac專業版,如果你是Mac用戶,可在剪映官網或蘋果應用商店免費下載安裝。同時,Windows版也安排上日程,計劃於明年2月上線。剪映MAC版界面在手機移動端,剪映以超低的使用門檻、強大的視頻剪輯功能,同時作為抖音官方推薦的視頻創作工具,吸引了無數的用戶使用,可以說在移動端,剪映所向披靡,是一家獨大。
  • 如何用本地的VSCode連接極鏈AI的GPU伺服器
    但是注意:不建議使用vscode的terminal,因為一旦關了就真的停了,還是用網頁開notebook比較保險,不會掉線。擴展閱讀:ImageJ實用技巧——圖像自動配準最近流行的目標檢測數據集介紹A survey on Image Data Augmentation 數據增強文獻綜述整理 - 室內定位數據集深度學習在醫學圖像分析領域的綜述深度學習福利 免費GPU算力資源領取!
  • 主題之美,排名靠前的 10 個 VSCode 主題
    收錄於話題 #vscode
  • 網頁版VS Code來了,特斯拉Xbox手機都能用,隨時隨地寫bug
    對,如果你想運行GitHub項目的話,只需在項目地址前加上vscode.dev/,就像這樣:https://github.com/microsoft/vscode => vscode.dev/https://github.com/Microsoft/vscode
  • 騰訊START雲遊戲TV版已上線
    據悉,目前騰訊START雲遊戲TV版已正式上線,遊戲內容包含了《古劍奇譚三》、《拳皇14》、《只只大冒險》、《侍魂:曉》、《不思議的皇冠》、《彩虹墜入》、《軒轅劍外傳:穹之扉》、《NBA2K Online 2》等等多款大型主機力作,全面滿足了遊戲玩家們的多樣化需求。
  • 芒果TV國際版APP正式上線:已支持Google Play下載
    日前,國內視頻平臺芒果TV在香港灣仔會展中心舉行芒果TV國際版APP上線啟動儀式,正式推出芒果TV國際版APP。據悉,該App目前已經可以在蘋果App Store和Google Play下載。芒果TV國際版  據介紹,芒果TV國際版針對海外用戶觀看和使用習慣,採用了簡潔明了的設計風格,縮減了頻道入口
  • 本市工程建設項目審批管理系統(V2.0版)已上線試運
    根據《上海市優化營商環境條例》和工程建設項目審批制度改革的要求,市住建委、市發改委、市規劃資源局、市交通委、市大數據中心等相關單位,對上海市工程建設項目審批管理系統(以下簡稱「審批管理系統」)(V1.0版)進行了整合,升級為審批管理系統(V2.0版),已於近期上線試運行。
  • 騰訊視頻(體驗版)已於11月4日16:00上線 Nintendo e 商店
    原標題:騰訊視頻(體驗版)已於11月4日16:00上線 Nintendo e 商店     騰訊 NintendoSwitch 官方微博今天下午宣布,騰訊視頻(體驗版)已於 11 月 4 日 16:00 上線 Nintendo e 商店,大家可前往 Nintendo e 商店免費下載體驗.
  • 《殺戮尖塔》手遊版美服已上線,國服上線時間未知恐難複製輝煌
    又一款steam口碑遊戲的移植手遊並上線,《殺戮尖塔》6月13日美服ios上線,售價9.99美元,並陸續登陸日服,韓服。據騰訊極光計劃消息,《殺戮尖塔》的港澳版本也將於7月份上線,至於國服的上線日期還未確定,估計今年國內雙端上線還是非常困難的。
  • 《Apex英雄》Steam版已上線 鎖國區|apex英雄|遊戲|大逃殺|射擊...
    EA旗下免費FPS《Apex英雄》Steam版已正式上線,目前遊戲在Steam上總評為「特別好評」,遊戲支持中文,但卻鎖國區。
  • 愛影視TV版APP已上線!海量劇集隨心看
    愛影視TV版APP已上線!👉海量資源免費看!👉電視追劇更輕鬆!