解鎖 VS Code 更多可能性,輕松入門 WebView

作者:HelloGitHub-小夏
說起 VS Code 大家普遍印象應(yīng)該都差不多是這樣:不就是個(gè)編輯器嘛,最主要的還是 coding 的快感咯。
里面很多功能都應(yīng)該是圍繞如何提高 coding 效率、減少 coding 出錯(cuò)率、解放 coder 小哥哥小姐姐的勞動(dòng)力等等,至于代碼以外的東西比如預(yù)覽什么的,就交給瀏覽器咯。
所以可能很少有人會(huì)把 VS Code 和 WebView 聯(lián)想到一起。
一、隨處可見的 WebView
但是我相信,你一定在很多“有名”的 VS Code 插件中接觸過它(WebView)的身影。比如可以在 VS Code 中畫流程圖的 vscode-drawio:

上班摸魚的同時(shí)還要繼續(xù)提升自我來(lái)刷題的 vscode-leetcode:

還有上班摸魚的同時(shí)還要關(guān)心能否從一顆“小韭菜”實(shí)現(xiàn)財(cái)富自由的「韭菜盒子」 leek-fund:

所以你可以看到,有了 WebView 來(lái)拓展能力,插件市場(chǎng)才會(huì)變得“百花齊放”,能滿足各類人各類摸魚的需求。但是上面項(xiàng)目的成功,也不僅僅靠的是我們本文介紹的簡(jiǎn)單的 WebView 的能力,如果你對(duì)上面幾個(gè)項(xiàng)目有深挖的興趣,可以直接clone 代碼,一瞅到底,說不定下一個(gè)厲害的插件就是出自你手啦。
二、WebView 到底是什么
前面有提過 VS Code 允許我們?cè)谒o的規(guī)則之下可以自定義很多功能,但是視圖這一塊,其實(shí)我們自定義的范圍非常小,這就限制了程序員們天馬行空的創(chuàng)造力。但是自由的靈魂不會(huì)被眼前的困難打敗,同行之間的心心相惜所以有了 WebView 的誕生。
當(dāng)然這都是小編自己內(nèi)心 OS 的,不過可以確定的是 WebView API 的存在允許在 VS Code 中擴(kuò)展創(chuàng)建完全可自定義的視圖。例如:內(nèi)置的 Markdown 擴(kuò)展使用 webviews 來(lái)呈現(xiàn) Markdown 預(yù)覽。Webviews 還可用于構(gòu)建超出 VS Code 的本機(jī) API 支持的復(fù)雜用戶界面。
你也可以簡(jiǎn)單的把 WebView 理解為VS Code 內(nèi)部的 iframe。WebView 可以在這個(gè)框架中渲染幾乎所有的 HTML 內(nèi)容,還可以使用消息傳遞與擴(kuò)展進(jìn)行通信。這種自由使得 webviews 非常強(qiáng)大,而且也擁有了一個(gè)全新的擴(kuò)展范圍。
三、創(chuàng)建一個(gè)簡(jiǎn)單的 WebView
從第一點(diǎn)的例子你就應(yīng)該可以體會(huì)到 WebView 的功能拓展有多強(qiáng)大,它不僅可以作為自定義編輯器的視圖來(lái)擴(kuò)展提供自定義 UI 以編輯工作區(qū)中的任何文件。還允許在側(cè)邊欄或面板區(qū)域的 WebView 中繼續(xù)呈現(xiàn) WebView 視圖等等。
如果你感興趣,可以去官網(wǎng)繼續(xù)學(xué)習(xí)。今天我們下文談的主要還是最簡(jiǎn)單的一種方式:在編輯器中創(chuàng)建一個(gè)簡(jiǎn)單的 WebView 面板。
1、配置命令
第一步首先肯定是配置命令啦,我們?cè)俅未蜷_package.json文件,新配置一個(gè)command:


你可以看到這個(gè)標(biāo)題就是我們上面在package.json 上配置的“HelloGitHub webview”,或許有同學(xué)會(huì)對(duì)ViewColumn 這個(gè)配置疑惑。
那我們來(lái)看一下這里到底都有些什么值:

看不懂?沒關(guān)系,我們實(shí)操一下,修改上面在extension.js 里的配置如下:

效果如下:

這里多了一個(gè)js 的文件其實(shí)沒有什么意義,因?yàn)槿绻麤]有這個(gè)文件占編輯器的第一個(gè)ViewColumn 的話,其實(shí)效果和上面的配置是一樣的,有了這個(gè)文件之后,我們的 WebView 才會(huì)在第二欄打開。這些單詞是不是非常簡(jiǎn)單易懂?
2、初始化內(nèi)容
現(xiàn)在我們就要切入最重要的部分啦,如何豐富 WebView 的內(nèi)容呢?其實(shí)也很簡(jiǎn)單啦,把它看做一個(gè) iframe 就好啦,那無(wú)非就是 HTML 的那些東西唄?so easy!
首先我們要有一個(gè)包含整個(gè) HTML 內(nèi)容的獨(dú)立文件,為了好區(qū)分,我把它放在了這里:

配置了一個(gè)非常簡(jiǎn)單的網(wǎng)頁(yè)內(nèi)容,里面只有一個(gè)圖片:

看一下效果:

這里要提醒大家的是,你配置的應(yīng)該始終是一個(gè)完整的 HTML 文檔。HTML 片段或格式錯(cuò)誤的 HTML 可能會(huì)導(dǎo)致運(yùn)行不成功,所以在進(jìn)行復(fù)雜操作的時(shí)候一定要小心調(diào)試,多看控制欄哦。
3、更新內(nèi)容
是的,我們現(xiàn)在要從編輯器對(duì)這個(gè) WebView 做更新操作了!比如我們給這個(gè) WebView 加一行文字,然后在編輯器里面加一個(gè)定時(shí)器,動(dòng)態(tài)的去修改它。首先,修改我們的html 文件,它不在是一個(gè)靜態(tài)的文本了,他要?jiǎng)悠饋?lái)就得接收一個(gè)變量,所以改成函數(shù)咯:

看一下我們的效果,是不是就變成一個(gè)動(dòng)感十足的網(wǎng)頁(yè)啦:

但是效果是實(shí)現(xiàn)了,你有沒有發(fā)現(xiàn)我們實(shí)現(xiàn)的方法非常的“暴力”,是直接替換了整個(gè)html 的內(nèi)容,類似于重新加載 iframe。所以要是換到復(fù)雜的頁(yè)面,性能肯定是個(gè)非常嚴(yán)重的問題,就會(huì)導(dǎo)致非常多令人頭大的性能問題。而且當(dāng)用戶關(guān)閉 WebView 面板時(shí),WebView 本身是會(huì)被銷毀的。如果嘗試使用銷毀的 WebView 會(huì)引發(fā)異常,比如我們上面的setInterval 會(huì)繼續(xù)觸發(fā)并更新panel.webview.html。
所以我們要避免這種情況出現(xiàn):

4、消息傳遞
前面說過,你可以簡(jiǎn)單的把 WebView 理解成 iframe,那這也意味著它們都可以運(yùn)行腳本。不過默認(rèn)情況下 WebView 中禁用 JavaScript,你可以通過傳入enableScripts: true 來(lái)啟用。不過官網(wǎng)建議 WebView 應(yīng)始終使用內(nèi)容安全策略禁用內(nèi)聯(lián)腳本,所以我們這里就不做展開。但是這一點(diǎn)也不影響我們發(fā)揮 WebView 的巨大作用——消息傳遞。
WebView 調(diào)試
在消息傳遞內(nèi)容之前,我覺得有必要說一下這個(gè)調(diào)試工具命令Developer: Toggle Developer Tools。你可以通過comand+p(MacOS)喚起這個(gè)開發(fā)者調(diào)試命令,可以幫你在調(diào)試 WebView 的時(shí)候“如魚得水”,輕松捕獲異常并 fix

當(dāng)然你還可以在Elements 里面查看dom 的結(jié)構(gòu),簡(jiǎn)直就是太熟悉了~

WebView 接收消息
首先我們先來(lái)了解一下如何從我們的插件應(yīng)用向我們的 webview 傳遞消息。聰明的你一定猜到了對(duì)不對(duì)?沒錯(cuò)就是postMessage!
修改我們的注冊(cè)命令如下:
- 把createWebviewPanel 的變量存到一個(gè)新的變量上去
- 新增了一個(gè)用于消息傳遞的命令webview.doRefactor
- 同時(shí)因?yàn)樵?HTML 內(nèi)部需要監(jiān)聽message 的傳遞,所以我們必須確保開啟腳本,也就是上文說的enableScripts:true
- 為了確保我們不眼花繚亂,這里也去掉了之前的定時(shí)器setInterval

為了防止有人在跟著敲的時(shí)候漏掉這一步,我決定還是再提醒一下~要在package.json 里面加上新注冊(cè)的這個(gè)命令哦:

上面的夠簡(jiǎn)單吧,我們來(lái)看一下效果,記得打開開發(fā)者調(diào)試工具,首先是用webview.start 命令打開 WebView:

運(yùn)行webview.doRefactor 之后,我們就把我們的值傳到了 WebView 里去啦:

WebView 發(fā)送消息
WebView 還可以將消息傳遞回我們的擴(kuò)展程序。
這主要是通過使用 WebView 的postMessage 內(nèi)特殊的 VS Code API 對(duì)象上的函數(shù)來(lái)完成的。要訪問 VS Code API 對(duì)象,需要在 WebView 內(nèi)部調(diào)用acquireVsCodeApi 這個(gè)函數(shù)每個(gè)會(huì)話只能調(diào)用一次。
而且必須保留此方法返回的 VS Code API 實(shí)例,并將其分發(fā)給任何其他需要使用它的函數(shù)。
我們可以使用 VS Code API 的postMessage 方法在我們的插件中顯示來(lái)自 WebView 的消息:

同時(shí)也需要在我們的插件代碼里接收來(lái)自 WebView 的消息:

完整的代碼如下,在打開 WebView 的時(shí)候就要將事件綁定都搞定:

接下來(lái)我們先看一下點(diǎn)擊按鈕前的樣式:

來(lái)看一下我們點(diǎn)擊按鈕會(huì)發(fā)生什么“神奇”的事情呢?

四、總結(jié)
那快樂的時(shí)光總是短暫的,又到了文章結(jié)束的時(shí)候啦??偟膩?lái)說 WebView 就像是在 VS Code 里的 iframe,雖然可能在性能上有那么點(diǎn)弊端,但是卻能夠幫助我們實(shí)現(xiàn)很多豐富而又有趣的事情。
因此我們更要好好的利用這個(gè)功能,把它的力量發(fā)揮到極致。根據(jù)官網(wǎng)的描述,我們也要在使用的時(shí)候多注意以下幾點(diǎn):
- WebView 應(yīng)該具有它所需的最少功能集。例如:如果不需要運(yùn)行腳本,則不要設(shè)置enableScripts: true
- WebView 嚴(yán)格遵從 內(nèi)容安全策略,所以在 WebView 中可加載和執(zhí)行的內(nèi)容都有一定的限制。例如:內(nèi)容安全策略可以確保僅允許在 WebView 中運(yùn)行的腳本列表,甚至告訴 WebView 只能加載https 圖像。
- 出于安全考慮 WebView 默認(rèn)無(wú)法直接訪問本地資源,它在一個(gè)孤立的上下文中運(yùn)行,想要加載本地圖片、js、css 等必須通過特殊的vscode-resource: 協(xié)議,網(wǎng)頁(yè)里面所有的靜態(tài)資源都要轉(zhuǎn)換成這種格式,否則無(wú)法被正常加載。
- 就像普通網(wǎng)頁(yè)都要求的那樣,在為 WebView 構(gòu)建 HTML 時(shí),必須清理所有用戶輸入。未能正確清理輸入可能會(huì)導(dǎo)致內(nèi)容注入,這可能會(huì)使你的用戶面臨安全風(fēng)險(xiǎn)。比如:文件內(nèi)容、文件和文件夾路徑、用戶和工作區(qū)設(shè)置
- WebView 有自己的生命周期,如果在有極致體驗(yàn)的場(chǎng)景下發(fā)揮他的最大作用,建議去官網(wǎng)更加深入的學(xué)習(xí)一下
最后的最后,預(yù)告一下下一篇「VS Code」系列文章,也就是本入門系列最后一篇文章將會(huì)帶大家體驗(yàn)更綜合性的東西,給小編多一點(diǎn)點(diǎn)時(shí)間努力研究一下,期待我們下次再見咯!