vite打包: 体积/速度优化/global/external/终极优化方案

后台系统的 js 体积优化
更新于: 2023-09-04 21:57:04

背景

因为我的大部分项目都是基于 react/react-dom/antd 来实现的,之前一直用 cra,现在是用 vite 来开发。

优化方向:

react/react-dom/antd 以全量方式引入项目,并走cdn,不走打包流程。

vite.confg.ts

  • 坑1: 网上推荐的方式 build:{ rollupOptions: …} 这种方式并不能使用,react 就报各种错误
  • 坑2: antd 需要 dayjs 的依赖

方式1:  external

推荐方式,与 webpack 达到类似的效果,完全不用对原有代码进行任何改造

项目: https://github.com/aric-tpls/vite-external-pkgs

实际项目中的改进:

  1. 可以将 external + globals 那部分进一步封装
  2. HTML 注入标签那部分,用其它插件完成配置。

index.html

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.production.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.7/dayjs.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/antd/5.3.1/antd.min.js"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/antd/5.3.1/reset.min.css" />
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

vite.config.ts

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  build: {
    rollupOptions: {
      external: ["react", "react-dom", "antd"],
      output: {
        format: "umd",
        globals: {
          react: "React",
          "react-dom": "ReactDOM",
          antd: "antd",
        },
      },
    },
  },
});

方式2: 需要代码上的改造,与正常写法不一致

参考 https://github.com/aric-tpls/vite-global-pkgs

  • src/vite-env.d.ts 添加配置(这一步不完美,现在没有找到更好的方式)
  • vite.config.ts: 重点关注这个配置 optimizeDeps
  • 代码层面做一些改造
  • 缺点:这种方式需要对 import ‘react’ 这种代码进行改造,所以不适合已经存在的项目

vite.env.d.ts

/// <reference types="vite/client" />
declare var nx: any;
declare var antd: any

vite.config.ts

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  optimizeDeps: {
    include: ["react", "react-dom","antd"],
  }
});

main.tsx

const { useState } = React;
const { render } = ReactDOM;
const { Button } = antd;

function App() {
  const [count, setCount] = useState(0);

  function handleClick() {
    console.log(nx, antd);
    setCount((c) => c + 1);
  }

  return (
    <div onClick={handleClick}>
      <p>You clicked {count} times</p>
      <Button type="primary">Btn.aric</Button>
    </div>
  );
}

render(<App />, document.getElementById("root"));

知识点

  • integrity:这个属性sha512-value,分成这2部分构成
  • value的算法是:base64(sha512(file_content_string))
<script
      src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js"
      integrity="sha512-8Q6Y9XnTbOE+JNvjBQwJ2H8S+UV4uA6hiRykhdtIyDYZ2TprdNmWOUaKdGzOhyr4dCyk287OejbPvwl7lrfqrQ=="
      crossorigin="anonymous"
      referrerpolicy="no-referrer"
    ></script>

如何计算 intergrity

import hashlib
import base64
import urllib.request

url = 'https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.production.min.js'

with urllib.request.urlopen(url) as response:
    sha512_hash = hashlib.sha512()
    while True:
        data = response.read(8192)
        if not data:
            break
        sha512_hash.update(data)
    sha512_digest = sha512_hash.digest()
    sha512_base64 = base64.b64encode(sha512_digest).decode('utf-8')
    print(f"SHA512 hash (Base64 encoded) of {url}: {sha512_base64}")

vite打包出错问题

rollup default.default error

https://www.cnblogs.com/mmykdbc/p/16319498.html
https://www.dianjilingqu.com/669177.html
https://stackoverflow.com/questions/61237208/rollup-error-default-is-not-exported-by-node-modules-react-index-js

最终方案

可以大幅度提是升开发时候的速度 + ci 里的打包速度(因为这些大库,已经不打包了)

推荐方案: https://github.com/aric-tpls/vite-external-pkgs

  • optimizeDeps: 优化开发速度,需要将 React → window.React 对已有项目不友好
  • buildOptions 配置,优化esbuild打包速度
  • 使用 vite-plugin-externals 插件,自动将 import 语法转化为 window.React 这种形式

安装包 vite-plugin-externals

yarn add --dev vite-plugin-externals

vite.config.ts 极简配置如下: 

import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { viteExternalsPlugin } from "vite-plugin-externals";

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [
    react(),
    viteExternalsPlugin({
      "react": "React",
      "react-dom": "ReactDOM",
      "antd": "antd",
    }),
  ],
  build: {
    rollupOptions: {
      external: ["react", "react-dom", "antd"],
      output: {
        format: "umd",
        globals: {
          "react": "React",
          "react-dom": "ReactDOM",
          "antd": "antd",
        },
      },
    },
  },
});

优化记录 ⚡️

优化过程中的一些参数/效果对比。

优化前后vite.vite 缓存情况
优化前
$ vite-external-pkgs/node_modules/.vite on 🌱 main [!] 
$ size
14M	deps
优化后
$ vite-external-pkgs/node_modules/.vite on 🌱 main [!] 
$ size
240K	deps

参考