返回

Vite 项目 Node 包压缩优化:提升扩展程序性能

vue.js

优化 Vite 项目中 Node 包的压缩,提升扩展程序性能

最近在开发一个钱包扩展程序,打包特定 Node 包时,扩展程序变得异常缓慢。为了使用交易构建器之类的功能,又必须打包这个包。 这个问题有点棘手,接下来我们好好分析一下。

问题根源:为什么会慢?

导致速度变慢的罪魁祸首,很可能就是 "@bitgo/utxo-lib" 这个包。即使已经有了 dist 文件夹,其中可能包含未经优化的 CommonJS 模块。 包含大量的代码、未使用的模块以及可能存在的性能瓶颈,都可能显著增加扩展程序的体积和加载时间。

解决方法

既然找到了问题所在, 咱们看看有哪些办法可以解决这个问题。下面是几种不同的方法,根据情况可以一起用。

1. 优化 @bitgo/utxo-lib

最直接的方法就是优化这个包。可以从下面几个地方着手:

a. Tree Shaking (摇树优化)

看看 @bitgo/utxo-lib 包是否支持 Tree Shaking。如果支持,Vite 可以自动移除未使用的代码,大大减小打包体积。检查 package.json 中的 "sideEffects" 字段,看看它是否设置为 false,或者是一个文件路径数组,这表示该软件包支持 tree-shaking。

  • 如果"sideEffects"为true,或者不存在,你可以试试修改一下node_modules/@bitgo/utxo-lib/package.json. 但要注意的是, 直接改动 node_modules 的内容不是一个长期可行的做法.

b. 手动优化(如果可以的话)

如果你对 @bitgo/utxo-lib 的代码很熟悉, 尝试找出哪些部分真正被用到,手动创建一个只包含必要代码的更小版本。

c. 找找有没有替代方案

  • 更轻量的库: 如果实在优化不了,试试搜索有没有更轻量级的替代库,实现类似的功能。

2. 调整 Vite 配置

vite.config.js 进行微调,也有可能解决一部分问题。

a. build.minify 选项

确认你的vite.config.js文件中 build.minify 设置为了 true。Vite 默认使用 esbuild 进行压缩,速度很快,效果也不错。

// vite.config.js
export default defineConfig({
  // ...
  build: {
    // ...
    minify: true, // 确保这个是 true
    // ...
  },
});

b. 使用不同的压缩器 (进阶)

Vite 默认的 esbuild 通常效果就很好. 如果你愿意折腾,也可以尝试用 terser。 Terser 压缩效果通常更好, 但速度会慢一点.

// vite.config.js
export default defineConfig({
  // ...
  build: {
    // ...
    minify: 'terser', // 改成 terser
    terserOptions: {
      // Terser 压缩选项 (可以进一步微调)
       compress: {
        drop_console: true, // 移除 console.log 语句 (生产环境推荐)
        drop_debugger: true // 移除 debugger 语句 (生产环境推荐)
      }
    },
  },
});

记得先安装 Terser:

npm install terser --save-dev
# 或者
yarn add terser -D

c. 代码分割 (Code Splitting)

Vite 的代码分割功能是默认启用的. 它会试着把你的代码分割成更小的 chunk, 这可以提高加载速度, 尤其是初始加载。 在你的 rollupOptions配置中:

  • preserveModules: true: 此选项会将每个模块打包到其自己的文件中。
  • preserveModulesRoot: 'src' : 用于确定放置非入口模块的相对根目录。

你当前的配置看起来已经很不错了, 但是有时候,Vite 的自动分割可能不是最优的。可以手动配置试试。 比如说, 你确定 @bitgo/utxo-lib 主要用在 background 脚本里, 就可以尝试手动指定:

// vite.config.js
export default defineConfig({
  // ...
  build: {
    // ...
    rollupOptions: {
       // ...
      output: {
         // ...
        manualChunks(id) {
          if (id.includes('@bitgo/utxo-lib')) {
            return 'bitgo-utxo-lib'; // 专门给它一个 chunk
          }
        }
      },
    },
  },
});

这个 manualChunks 函数能让你手动控制哪些模块放在哪个 chunk 里。

d. 审查resolve.alias

你用resolve.alias重定向了一些模块,比如'stream', 'crypto' 和 'buffer'到浏览器的兼容版本。如果 @bitgo/utxo-lib 没有直接使用 Node.js 的这些内置模块,只是它的某些依赖用了,就可以考虑移除这些别名,或更精确地配置它们。

3. 其他优化建议

a. 异步加载

如果 @bitgo/utxo-lib 的某些功能不是立即需要的,可以考虑用动态导入 (import()) 来异步加载。 这样能显著提升初始加载速度。

// 某个需要用到 utxo-lib 功能的模块里
async function doSomethingWithUtxoLib() {
  const { someFunction } = await import('@bitgo/utxo-lib');
  // 使用 someFunction ...
}

b. 使用 Web Workers

如果 @bitgo/utxo-lib 执行的是计算密集型任务,而且不直接操作 DOM, 考虑把它放到 Web Worker 里。Web Workers 在单独的线程运行, 不会阻塞主线程,可以提升用户界面的响应速度。

// main.js (或者 content script)
const worker = new Worker('worker.js');
worker.postMessage({ /* ... 数据 ... */ });
worker.onmessage = (event) => {
  // 处理 worker 返回的结果
};

// worker.js
import { someFunction } from '@bitgo/utxo-lib'; // 在 worker 里导入
self.onmessage = (event) => {
  const result = someFunction(event.data);
  self.postMessage(result);
};

###4. 使用 pre-bundled 依赖项(针对本项目情况)

由于@bitgo/utxo-lib有一个 dist 文件夹,它 可能 已经包含了为浏览器构建的预构建版本。 可以检查 dist 文件夹,寻找看起来像已经打包好的文件(例如, utxo-lib.min.js, index.umd.js 或类似的文件)。

如果是这样,可以直接引用这个预构建版本:

  1. 找到预构建文件: 查看 @bitgo/utxo-libdist 文件夹。

  2. 修改导入方式: 如果你的代码里是这样导入的:

    import { /* ... */ } from '@bitgo/utxo-lib';
    

    尝试改成这样 (假设预构建文件叫 utxo-lib.min.js):

    import { /* ... */ } from '@bitgo/utxo-lib/dist/utxo-lib.min.js';
    
    

或者在vite.config中增加 alias配置项

'@bitgo/utxo-lib': path.resolve(__dirname, 'node_modules/@bitgo/utxo-lib/dist/utxo-lib.min.js')
  1. 处理 CommonJS: 如果预构建的文件仍然是 CommonJS 格式,Vite 应该会自动处理它, 把 CommonJS 转成 ES Modules.
    但为了保险, 可以在构建时,用 rollup 插件将@bitgo/utxo-lib预构建文件从CommonJS转换到ESM。
npm install @rollup/plugin-commonjs --save-dev

然后在 vite.config.js:

 import { defineConfig } from 'vite';
 import { nodePolyfills } from 'vite-plugin-node-polyfills';
//增加这一行
 import commonjs from '@rollup/plugin-commonjs';
 // ... 其他导入
  export default defineConfig({
      plugins: [
         // ...其他插件
          commonjs({
              include: ['node_modules/@bitgo/utxo-lib/dist/**']
         })
      ]
   })

5. 处理bitcoin-ops/evals.json(根据项目情况)

项目中还用到了bitcoin-ops/evals.json的别名. 确保路径是正确的, 如果还是不行可以内联文件。

src/utils/bitcoin-ops-evals.json 文件的内容直接复制到一个 JavaScript 文件中, 然后再使用它, 就避免文件系统查找:
例如:创建一个 src/utils/bitcoinOps.js

// src/utils/bitcoinOps.js
export const bitcoinOpsEvals = {
  // ... 这里粘贴 evals.json 的内容 ...
};

在代码里,这样导入:

import { bitcoinOpsEvals } from '@/utils/bitcoinOps';

把这几种方法结合起来,应该就能大大减少扩展程序的体积, 解决卡顿的问题了! 加油!