使用 asar 提升 VSCode 插件的性能

最近在開發(fā)一款 VSCode 插件過程中遇到了一個(gè)性能瓶頸,插件的依賴項(xiàng)過于龐大導(dǎo)致插件體積很大,插件安裝和啟動(dòng)耗時(shí)也比較長(zhǎng)兵多,影響了用戶的使用體驗(yàn)劣像。為了解決這個(gè)問題乡话,本文提出了一種使用 asar 對(duì) node_modules 進(jìn)行打包的方法,經(jīng)測(cè)試插件體積減少了 46%耳奕,安裝時(shí)間減少了 75% 以上绑青,啟動(dòng)時(shí)間減少了 22%,插件的性能得到了有效提升屋群。

該方法具備一定的通用性闸婴,尤其是對(duì)插件依賴項(xiàng)過多時(shí)有較為明顯的提升。如果你是 VSCode 插件開發(fā)者并且面臨著類似的問題芍躏,歡迎嘗試下此方案邪乍。

問題分析

性能的主要瓶頸在于插件中的 node_modules 非常龐大,有 117,317 個(gè)文件对竣,體積達(dá)到了 185.5 MB庇楞,而且這些依賴需要在插件運(yùn)行時(shí)使用,vsce(VSCode 插件構(gòu)建工具)需要把 node_modules 也打包到產(chǎn)物內(nèi)否纬,導(dǎo)致插件產(chǎn)物(vsix 文件)的體積也很大吕晌,且文件零碎,用戶安裝插件時(shí)速度很慢临燃。

以下是構(gòu)建時(shí) vsce 給出的信息睛驳,可以看到 vsce 也發(fā)現(xiàn)了這個(gè)問題并且給出了一個(gè)解決方案:Bundling Extensions

優(yōu)化前的版本

VSCode 提出的優(yōu)化方案主要有兩個(gè)方面:

  1. 添加 .vscodeignore 構(gòu)建時(shí)忽略插件運(yùn)行時(shí)不需要的文件
  2. 對(duì)插件使用 rollup 等構(gòu)建工具在 vsce 前進(jìn)行打包,消滅 node_modules

很遺憾膜廊,這個(gè)方案無法有效優(yōu)化這個(gè)插件乏沸,因?yàn)橐环矫孢@些依賴需要在運(yùn)行時(shí)使用因此不能忽略,另一方面插件中的依賴大量使用了動(dòng)態(tài) require溃论,因此 rollup 等構(gòu)建工具無法完整的將所有依賴打包屎蜓。

如果你的插件依賴沒有遇到動(dòng)態(tài) require 的問題,推薦直接使用構(gòu)建工具的方案而不是 asar

因此钥勋,順著第二個(gè)思路炬转,我們需要找到一種方法將插件的依賴完整打包。

解決方案

在一次調(diào)試 VSCode 的過程中算灸,發(fā)現(xiàn)了 VSCode 的 Resources/app 目錄下有一個(gè) node_modules.asar 的文件扼劈,頓時(shí)受到了啟發(fā)。開發(fā)過 Electron 的朋友應(yīng)該都接觸過這個(gè)文件類型菲驴,而 VSCode 也是基于 Electron荐吵,VSCode 選擇將自身的依賴構(gòu)建成 asar 必然有性能方面的考量。那么我們是否也可以利用 asar 對(duì)插件的依賴進(jìn)行處理呢?

我們先看下 Electron 對(duì) asar 的介紹(https://github.com/electron/asar):

asar 是一種簡(jiǎn)單的擴(kuò)展存檔格式先煎,它和 tar 類似贼涩,無需壓縮即可將所有文件連接在一起,同時(shí)具備隨機(jī)訪問支持

asar 的優(yōu)點(diǎn):

  • 支持隨機(jī)訪問
  • 使用 JSON 存儲(chǔ)文件信息
  • 容易解析

由此來看薯蝎,asar 很匹配我們的需求遥倦。接下來就具體介紹如何在插件中使用。

構(gòu)建 asar 文件

asar 的構(gòu)建非常簡(jiǎn)單占锯,首先安裝:

$ npm install asar -D

然后在 package.json 添加一個(gè)運(yùn)行腳本:

{
  "build:asar": "asar pack ./node_modules ./dist/node_modules.asar"
}

運(yùn)行 npm run build:asar 即可得到 asar 文件

忽略 node_modules

得到 node_modules.asar 后袒哥,插件就不再需要 node_modules 目錄,因此需要在 .vscodeignore 文件中添加一下:

node_modules

讓 VSCode 支持加載 asar

由于 VSCode 是基于 Electron 的消略,因此天然能加載 asar 中的文件堡称。例如插件中有一個(gè)依賴項(xiàng) miniprogram-ci,我們通過將路徑改為以下方式即可在插件中加載 asar 文件中的依賴項(xiàng):

// 將 require('miniprogram-ci') 替換為:
require('./node_modules.asar/miniprogram-ci')

然而我們很難將 node_modules 中所有依賴項(xiàng)中的 require 也都改寫成這種形式艺演,這會(huì)導(dǎo)致依賴項(xiàng)的依賴無法加載却紧。為了解決這個(gè)問題,我研究了下 VSCode 的源代碼钞艇,發(fā)現(xiàn) VSCode 是采用改寫 Node 模塊加載的行為實(shí)現(xiàn)的啄寡。

讓 require 支持查找 asar

首先我們回顧下 Node 是如何加載依賴的。當(dāng) require('miniprogram-ci') 時(shí)哩照,Node 會(huì)先生成所有可能的查找路徑(paths)挺物,例如這個(gè)項(xiàng)目的插件入口位于 dist/extension/index.js,引用該依賴時(shí) Node 會(huì)形成以下查找路徑并按順序嘗試飘弧,直到成功

/Users/[用戶名]/.vscode/extensions/[插件名]/dist/extension/node_modules
/Users/[用戶名]/.vscode/extensions/[插件名]/dist/node_modules
/Users/[用戶名]/.vscode/extensions/[插件名]/node_modules(成功)
/Users/[用戶名]/.vscode/extensions/node_modules
…

因此默認(rèn)情況下 Node 是不會(huì)默認(rèn)查找 node_modules.asar 的识藤,我們可以通過修改 Node 的這一查找行為將 asar 的路徑也塞到 paths

參考 VSCode 源碼中的實(shí)現(xiàn)(bootstrap-node.js),通過改寫 Module._resolveLookupPaths 即可達(dá)到目的次伶。

在插件入口文件最上方增加以下代碼:

const Module = require('module');
const path = require('path');
const asarPath = path.join(__dirname, '..', 'node_modules.asar');
const nodeModulesPath = path.join(__dirname, '..', '..', 'node_modules');
const originalResolveLookupPaths = Module._resolveLookupPaths;

Module._resolveLookupPaths = function (moduleName, parent) {
  const paths = originalResolveLookupPaths(moduleName, parent);

  if (Array.isArray(paths)) {
    for (let i = 0, len = paths.length; i < len; i++) {
      if (paths[i] === nodeModulesPath) {
        paths.splice(i, 0, asarPath);
        break;
      }
    }
  }

  return paths;
};

即當(dāng) require 某個(gè)模塊時(shí)痴昧,搜索其查找路徑 paths 是否存在插件根目錄下的 node_modules 路徑,如有則將 asar 文件路徑插入到 paths 中冠王。搜索的目的是為了確認(rèn)這個(gè)模塊是否是 node_modules 下的赶撰,排除 Node 內(nèi)置模塊和其它情況的引用。

優(yōu)化效果

至此柱彻,插件就可以擺脫 node_modules豪娜,性能得到很大的提升。我們從三個(gè)方面對(duì)比下前后的性能變化:

插件體積

優(yōu)化前:67.7 MB哟楷,110,223 個(gè)文件瘤载,構(gòu)建用時(shí) 3m 5s

優(yōu)化后:36.4 MB,764 個(gè)文件卖擅,構(gòu)建用時(shí) 2m 56s

插件體積減少了 46%鸣奔,構(gòu)建用時(shí)持平墨技,并沒有因?yàn)樵黾恿?asar 構(gòu)建環(huán)節(jié)而顯著變慢

優(yōu)化后的版本

優(yōu)化前后的 vsix 產(chǎn)物可以在這里看到,其中版本 1.4.7 為優(yōu)化前挎狸,1.4.8 為優(yōu)化后:
https://github.com/crazyurus/miniprogram-vscode-extension/releases

構(gòu)建用時(shí)的變化可以在 GitHub Actions 中看到:https://github.com/crazyurus/miniprogram-vscode-extension/actions

插件安裝用時(shí)

安裝時(shí)間 VSCode 沒有直接給出扣汪,大致測(cè)量了下:

優(yōu)化前:1m 以上

優(yōu)化后:15s 左右

插件安裝用時(shí)也有大幅提升,且用時(shí)的減少可以有效減緩用戶網(wǎng)絡(luò)導(dǎo)致安裝失敗的影響伟叛,有效提升了安裝成功率

插件加載用時(shí)

VSCode 每次加載插件都會(huì)統(tǒng)計(jì)用時(shí)私痹,可以在插件列表或詳情頁看到:

插件加載用時(shí)

優(yōu)化前:95ms

優(yōu)化后:74ms

插件加載用時(shí)也有一定提升,主要避免了大量零碎文件的加載導(dǎo)致的 I/O 消耗

進(jìn)一步優(yōu)化

node_modules 中的所有依賴并不是插件運(yùn)行時(shí)都需要的统刮,我們可以將 devDependencies 排除在外不打包到 asar 中,進(jìn)一步減少體積账千。

  1. 可以通過 npm install --production 實(shí)現(xiàn)僅安裝 dependencies 中的依賴侥蒙,此時(shí) node_modules 中的依賴只與運(yùn)行時(shí)相關(guān)。得到 asar 產(chǎn)物后再安裝全部依賴完成插件的其它構(gòu)建步驟匀奏,感興趣的朋友可以嘗試下鞭衩。

  2. 也可以通過增加參數(shù) asar --unpack-dir 將部分體積大的依賴排除,例如:

$ asar pack ./node_modules ./dist/node_modules.asar --unpack-dir "{@types,ts-node,typescript}"

排除掉插件開發(fā)和構(gòu)建環(huán)節(jié)有關(guān) TypeScript 的依賴娃善,asar 會(huì)將這些依賴復(fù)制到 node_modules.asar.unpacked 文件夾下论衍,我們只需刪除或忽略這個(gè)文件夾即可。

最后

向大家推廣一下這款 VSCode 插件 微信小程序開發(fā)工具聚磺,支持 WXML 等小程序特有語法的高亮和代碼提示坯台,以及小程序的預(yù)覽、上傳瘫寝、體積分析等蜒蕾,歡迎開發(fā)小程序的朋友體驗(yàn)以及反饋意見。

另外這個(gè)插件是開源的焕阿,對(duì) VSCode 插件開發(fā)感興趣的朋友也可以了解下:https://github.com/crazyurus/miniprogram-vscode-extension

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末咪啡,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子暮屡,更是在濱河造成了極大的恐慌撤摸,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件褒纲,死亡現(xiàn)場(chǎng)離奇詭異准夷,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)外厂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門冕象,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人汁蝶,你說我怎么就攤上這事渐扮÷坫玻” “怎么了?”我有些...
    開封第一講書人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵墓律,是天一觀的道長(zhǎng)膀估。 經(jīng)常有香客問我,道長(zhǎng)耻讽,這世上最難降的妖魔是什么察纯? 我笑而不...
    開封第一講書人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮针肥,結(jié)果婚禮上饼记,老公的妹妹穿的比我還像新娘。我一直安慰自己慰枕,他們只是感情好具则,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著具帮,像睡著了一般博肋。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蜂厅,一...
    開封第一講書人閱讀 52,156評(píng)論 1 308
  • 那天匪凡,我揣著相機(jī)與錄音,去河邊找鬼掘猿。 笑死病游,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的术奖。 我是一名探鬼主播礁遵,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼采记!你這毒婦竟也來了佣耐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤唧龄,失蹤者是張志新(化名)和其女友劉穎兼砖,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體既棺,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡讽挟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丸冕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耽梅。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖胖烛,靈堂內(nèi)的尸體忽然破棺而出眼姐,到底是詐尸還是另有隱情诅迷,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布众旗,位于F島的核電站罢杉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏贡歧。R本人自食惡果不足惜滩租,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望利朵。 院中可真熱鬧律想,春花似錦、人聲如沸绍弟。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽晌柬。三九已至,卻和暖如春郭脂,著一層夾襖步出監(jiān)牢的瞬間年碘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來泰國(guó)打工展鸡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留屿衅,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓莹弊,卻偏偏與公主長(zhǎng)得像涤久,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子忍弛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容