封装一个打包cesium的vite插件

571 阅读2分钟

背景

项目有多个入口文件,没用到cesium的入口文件不需要打包cesium,打包出来的cesium资源需要配合 vite-plugin-compression 插件压缩,使用 vite-plugin-cesium 插件满足不了需求,因此参考 vite-plugin-cesium 改了一个插件出来。

功能

  • 在多页面应用中可以指定入口
  • 资源可以被压缩
  • 支持使用线上的资源

基本思路

  • 开发环境:全局设置 CESIUM_BASE_URL。在 configureServer 添加中间件,项目运行中能够通过请求拿到本地的cesium资源。
  • 生产环境:全局设置 CESIUM_BASE_URL。将cesium外部化,不参与构建。如果需要输出文件,就将cesium的必须文件输出到指定目录下面。入口文件添加对 Cesium.js 文件的访问。

代码

插件Api

  • config:一些基础设置
  • configureServer:开发环境设置cesium的中间件
  • transformIndexHtml:生产环境生成 Cesium.js 的script标签
  • writeBundle:使用这个钩子就可以在压缩cesium之前输出静态资源

参数说明

interface VitePluginCesiumPlusOptions {
  devMinifyCesium?: boolean;
  /**
   * cesium源文件的根路径
   * @default "node_modules/cesium/Build"
   */
  cesiumBuildRootPath?: string;
  /**
   * 输出路径
   * @default "cesium/"
   */
  cesiumOutputPath?: string;
  /**
   * 指定入口的路径。默认所有入口都打包
   */
  inputIndexHtml?: string | string[];
  /**
   * 线上的Cesium.js链接,如果指定了这个链接,build时就不会输出cesium的源文件了
   */
  cesiumJsUrl?: string;
}

代码

import path, { posix } from "path";
import serveStatic from "serve-static";
import externalGlobals from "rollup-plugin-external-globals";
import { copy } from "fs-extra";

const { resolve } = path;
const { join } = posix;

function vitePluginCesiumPlus(options?: VitePluginCesiumPlusOptions) {
  let {
    devMinifyCesium = false,
    cesiumBuildRootPath = "node_modules/cesium/Build",
    cesiumOutputPath = "cesium/",
    inputIndexHtml,
    cesiumJsUrl,
  } = options || {};
  const cesiumBuildPath = join(cesiumBuildRootPath, "Cesium");
  let isBuild = false;
  let isOut = false;
  let outDir = "";
  let CESIUM_BASE_URL = "";

  return {
    name: "vite-plugin-cesium-plus",
    config(config, { command }) {
      isBuild = command === "build";
      CESIUM_BASE_URL = join(config.base || "/", cesiumOutputPath);
      outDir = config.build?.outDir || "dist";

      return {
        define: {
          CESIUM_BASE_URL: JSON.stringify(CESIUM_BASE_URL),
        },
        build: {
          rollupOptions: {
            // 外部化
            external: ["cesium"],
            plugins: [externalGlobals({ cesium: "Cesium" })],
          },
        },
      };
    },
    configureServer({ middlewares }) {
      const cesiumPath = join(
        cesiumBuildRootPath,
        // 开发环境默认使用未压缩版代码
        devMinifyCesium ? "Cesium" : "CesiumUnminified"
      );
      // 中间件增加对资源请求的访问
      middlewares.use(join("/", CESIUM_BASE_URL), serveStatic(cesiumPath));
    },
    transformIndexHtml(html, { filename }) {
      const tags = [];
      if (
        isBuild &&
        // 对入口文件的处理
        (typeof inputIndexHtml === "undefined" ||
          (typeof inputIndexHtml === "string" &&
            resolve(filename) === resolve(inputIndexHtml)) ||
          (Array.isArray(inputIndexHtml) &&
            inputIndexHtml.map((e) => resolve(e)).includes(resolve(filename))))
      ) {
        isOut = !cesiumJsUrl;
        tags.push({
          tag: "script",
          attrs: {
            src: cesiumJsUrl || join(cesiumOutputPath, "Cesium.js"),
          },
        });
      }
      return tags;
    },
    async writeBundle() {
      if (isBuild && isOut) {
        try {
          // 输出静态资源
          await Promise.all(
            ["Assets", "ThirdParty", "Workers", "Widgets", "Cesium.js"].map(
              (name) =>
                copy(
                  join(cesiumBuildPath, name),
                  join(outDir, cesiumOutputPath, name)
                )
            )
          );
        } catch (err) {
          console.error("copy failed", err);
        }
      }
    },
  };
}
export default vitePluginCesiumPlus;

已经将插件发布 vite-plugin-cesium-plus

使用

npm i vite-plugin-cesium-plus -D

基本用法

import { defineConfig } from 'vite'
import cesium from 'vite-plugin-cesium-plus'

export default defineConfig({
  plugins: [
    cesium()
  ],
  // ...
})

指定入口

import { defineConfig } from 'vite'
import cesium from 'vite-plugin-cesium-plus'

export default defineConfig({
  // ...
  plugins: [
    cesium({
      inputIndexHtml: path.resolve(__dirname, 'demo/index.html')
    })
  ],
  rollupOptions: {
    build: {
      input: {
        demo: path.resolve(__dirname, 'demo/index.html')
      },
      // ...
   }
  }
})

指定线上文件

import { defineConfig } from 'vite'
import cesium from 'vite-plugin-cesium-plus'

export default defineConfig({
  // ...
  plugins: [
    cesium({
      cesiumJsUrl: 'https://xxx/Cesium.js'
    }),
  ],
})

注意

插件没有提供对css的引入方式(这样可以按需引入),需要单独引用一下:

import 'cesium/Build/Cesium/Widgets/widgets.css';