Skip to content

软件包封装:多种JS模块标准的软件包

一款成熟的类库都会提供多种模块的封装形式,如常用的Vue,就提供了cjs、esm、umd等多种封装模式,并且还会提供对应的压缩版本、方便在开发、生产环境下使用。

工程化场景下需要考虑支持哪些模块的规范

目前常用的模块规范有:

  • IFFE : 使用立即执行函数实现模块化。例: (function(){})()
  • CJS : 基于 CommonJs 标准的模块化。
  • AMD : 使用 Require 编写。
  • CMD : 使用 SeaJs 编写。
  • ESM : ES标准的模块化方案(ES6标准提出)。
  • UMD : 兼容 CJS、AMD、IFFEG 规范。

其中最常用的有三类:ESM、CJS、IFFE 。ESM标准目前是前端开发的标配,无论是选用webpack或是vite,都会采用这种模块规范。其次是CJS,不可否认,有大量的存量代码还在使用CJS规范,完全没有必要因为引入一个库去更改编译规则。最后就是IFFE这种类型,非常适合用于逻辑简单,无须搭建工程化环境的前端应用。

需要考虑代码的压缩和混淆问题

  • 代码压缩是指去除代码中的空格、制表符、换行符等内容,将代码压缩至几行甚至是一行,这样可以提高网站的加载速度。
  • 混淆是指将代码转换成一种功能上等价,但是难以阅读和理解的形式。混淆的主要目的是增加反向工程的难度,同时也可以相对减少代码的体积,比如将变量名缩短等。

需要考虑 SourceMap 配置

SourceMap 是一个信息文件,里面存储了代码打包转换后的位置信息,实质上是一个json描述文件,维护了打包前后的代码映射关系。通常输出的模块不会提供 SourceMap , 因为通过 Sourcemap 很容易还原原始代码。但是如果你想在浏览器中断点调试代码,或者希望在异常监控工具中定位出错位置, SourceMap 就非常有必要。所以需要正确掌握 SourceMap 的生成方式。

基于vite的打包方案

rollupOption 配置

vite.config.ts

ts
const rollupOptions = {
  external: ["vue"],
  output: {
    globals: {
      vue: "Vue",
    },
  },
}

由于 Vite 的构建是通过 rollup 完成的,所以 rollup 中的一些配置是通过这个属性传递给 rollup。 其中需要配置的两个解释如下:

  • external 作用是将某个模块保留在 bundle 之外, 比如在数组中添加了 vue , 就是为了不让 vue 打包到组件库中
  • output 这个配置用于 UMD/IFFE 包中,意思是全局中的某个模块在组件库中叫什么名字。比如:
js
import $ from 'jquery'

// 意味着jquery模块的id等同于 $ 变量
var MyBundle = (function($){
  // 代码
})(window.jquery);

打包配置

javascript
export default defineConfig({
 build: {
    rollupOptions,
    minify: 'terser', // boolean | 'terser' | 'esbuild'
    sourcemap: true, // 输出单独 source文件
    brotliSize: true,  // 生成压缩大小报告
    cssCodeSplit: true,
    lib: {
      entry: "./src/entry.ts",
      name: "SmartyUI",
      fileName: "smarty-ui",
      formats: ["esm", "umd", "iife"], // 导出模块类型
    },
  },
});
  • minify 是混淆的意思。这里有两个混淆工具 terseresbuild
  • sourcemap 是否生成 SourceMap 源文件
bash
pnpm i terser -D
  • lib.name 生成包的名字,在iife/umd 包,同一页上的其它脚本可以访问它
  • lib.fileName 文件名,其实只是一个输出文件名的前缀,默认情况下会和模块类型配合组成最终的文件名。
  • lib.formats ["esm", "umd", "iife"] 打包输出的模块类型文件