UMD模块

634 阅读4分钟

前言

最近在做一个构思代码编辑器,遇到了一个问题就是当内置组件过多编辑器整体代码体积过重。会存在如下两个问题(我考虑到的,也许还有其他方面):

  1. 如何让整个编辑器在构建和加载速度方面更加快速
  2. 如何增强组件可扩展性,例如随时上架和下架一下组件

针对上面两个问题,接下来来总结一下UMD远程组件的实现方式。

什么是UMD

首先我们应该先要了解一下什么UMD在其他章节开始前,我们必须先了解清楚他的定义。

UMD 模块规范: UMD模块本质就是一个JavaScript文件,它会试图去猜测该文件当前运行环境正在使用的模块系统。你可以通过script标签或者AMDCommonJS进行加载,其内部会自行判断当前执行该文件的模块系统是哪种。这样子做的好处在于,我们不需要针对浏览器、AMD、CommonJS针对性的构建build产物、以及它也支持使用CDN的方式加载

用简短的话来说: 它是一种兼容浏览器全局变量、AMD 规范、CommonJS 规范的规范.

构建UMD模块

接下来使用vite来打包一个工具包, vite会默认生成ES moduleUMD module代码文件

  1. 编写工具类 定义一个AKclown类暴露一个sayHello方法,引用getClassNamegetRandomInt方法并且进行打印 image.png

  2. 生成.d.ts文件 借助tsc工具生成类型文件

// 添加typescript
pnpm add typescript -D
// 默认生成tsconfig.json
npx tsc --init
// 编译
npx tsc

tsconfig.json文件配置,如下是我按照默认选择改动后的

{
  "compilerOptions": {
    "target": "es2016",
    "module": "commonjs",
    "declaration": true,
    "emitDeclarationOnly": true,
    "declarationDir": "./dist",
    "esModuleInterop": true,
    "allowImportingTsExtensions": true 
    "forceConsistentCasingInFileNames": true,
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["./src/*"],
  "exclude": []
}
  1. 配置vite构建符合UMDES文件
import { resolve } from 'path'
import { defineConfig } from 'vite'

export default defineConfig({
    build: {
        lib: {
            entry: resolve(__dirname, './src/main.ts'),
            name: 'clown', // 指定window挂在的名称。 window.clown
            fileName: 'UMD-TEST',
            formats: ['umd'],
        },
    },
})

通过如上配置最终生成打包后的umd代码如下: image.png

分析UMD代码

接下来分析一下构建产物UMD-TEST.umd文件。我们先删除掉不相干代码只保留UMD基础格式

(function (e, t) {
    typeof exports == "object" && typeof module < "u" ? module.exports = t() :
        typeof define == "function" && define.amd ? define(t)
            : (e = typeof globalThis < "u" ? globalThis :
                e || self, e.AKclown = t())
})(this,
    function () {
        ...
        return 入口类型
});
  1. 条件typeof exports == "object" && typeof module < "u"在Node环境打印如下: image.png typeof module < "u"是用来检查module是否被定义,typeof module获取到的值为object然后按照字典顺序比较objectu,字典上object顺序小于u所以表达式为true

2.条件 typeof define == "function" && define.amd。先构建AMD环境然后在将这个条件进行打印:

 <script data-main="./amd" src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"></script>

image.png 启动浏览器打印结果:
image.png

这里可以看到关于AMD的介绍 Asynchronous Module Definition定义。判断definedefine.amd是否存在,如果存在则判断为是AMD环境

  1. 条件e = typeof globalThis < "u" ? globalThis : e || self。 首先我们要了解一下globalThis关键字表示是什么

globalThis提供了一个标准的方式来获取不同环境下的全局 this 对象(也就是全局对象自身)

typeof globalThis < "u"返回的字符串是否比"u"小(例如undefined),通常表示不支持globalThis的环境。
image.png
web work中是采用self关键字获取全局变量的
image.png

总结:
首先判断是否为NODE环境,如果是那如果用module.exports = t()暴露工具类
紧接着判断是否为AMD环境,如果是采用define(t)定义模块。
都不符合将工具类挂载到全局变量上e.AKclown = t()

组件资产Assets的定义

在完成UMD模块构建时,我们需要定义assets.json(资产)来记录包名称版本号组件的UMD地址等,这里推荐参考阿里低代码引擎的资产包定义 image.png 在完成如上步骤之后,就可以通过请求assest.json文件就可以得到远程组件的基础信息。然后通过遍历该数组就实现了加载远程组件了。

总结

通过上述字段,我们学习了UMD模块的含义vite构建UMD解析UMD的基础结构assests资产包定义。在项目里面如何加载UMD如何解决UMD模块同名问题如何结合沙箱机制加载...将在下一节解决

参考文献

远程组件加载方案实践
微组件实践
What are UMD modules