发布时间:2025-12-09 11:54:11 浏览次数:1
原创@前端司南
这个仓库[1]记录了一些关于javascript UMD模块规范的demo,对我学习UMD规范有了很大帮助,希望也能帮助到你。
之前也写了几篇关于javascript模块的博客,链接如下:
近几天准备总结一下javascript模块的知识点,所以建了这个Git仓库[5],如果能帮助到您,麻烦点个star哦,非常感谢!
这篇博客主要说下自己关于UMD的一点认知和思考,从实现一个简单的UMD模块,再到实现一个有依赖关系的UMD模块,整个过程加深了我对UMD模块的理解。
所谓UMD (Universal Module Definition),就是一种javascript通用模块定义规范,让你的模块能在javascript所有运行环境中发挥作用。
实现一个UMD模块,就要考虑现有的主流javascript模块规范了,如CommonJS, AMD, CMD等。那么如何才能同时满足这几种规范呢?
首先要想到,模块最终是要导出一个对象,函数,或者变量。
而不同的模块规范,关于模块导出这部分的定义是完全不一样的。
因此,我们需要一种过渡机制。
首先,我们需要一个factory,也就是工厂函数,它只负责返回你需要导出的内容(对象,函数,变量等)。
我们从导出一个简单的对象开始。
function factory() { return { name: '我是一个umd模块' }}假设不考虑CommonJS, AMD, CMD,仅仅将这个模块作为全局对象的一个属性应该怎么写呢?
(function(root, factory) { console.log('没有模块环境,直接挂载在全局对象上') root.umdModule = factory();}(this, function() { return { name: '我是一个umd模块' }}))我们把factory写成一个匿名函数,利用IIFE(立即执行函数)去执行工厂函数,返回的对象赋值给root.umdModule,这里的root就是指向全局对象this,其值可能是window或者global,视运行环境而定。
打开效果页面链接[6](要看源码的话,点开Git仓库[7]),观察Network的文件加载顺序,可以看到,原则就是依赖先行。
要兼容AMD也简单,判断一下环境,是否满足AMD规范。如果满足,则使用require.js提供的define函数定义模块。
(function(root, factory) { if (typeof define === 'function' && define.amd) { // 如果环境中有define函数,并且define函数具备amd属性,则可以判断当前环境满足AMD规范 console.log('是AMD模块规范,如require.js') define(factory) } else { console.log('没有模块环境,直接挂载在全局对象上') root.umdModule = factory(); }}(this, function() { return { name: '我是一个umd模块' }}))打开效果页面链接[8],可以看到,原则是调用者先加载,所依赖的模块后加载。
同理,接着判断当前环境是否满足CommonJS或CMD规范,分别使用相应的模块定义方法进行模块定义。
(function(root, factory) { if (typeof module === 'object' && typeof module.exports === 'object') { console.log('是commonjs模块规范,nodejs环境') module.exports = factory(); } else if (typeof define === 'function' && define.amd) { console.log('是AMD模块规范,如require.js') define(factory()) } else if (typeof define === 'function' && define.cmd) { console.log('是CMD模块规范,如sea.js') define(function(require, exports, module) { module.exports = factory() }) } else { console.log('没有模块环境,直接挂载在全局对象上') root.umdModule = factory(); }}(this, function() { return { name: '我是一个umd模块' }}))最终,使用require.js, sea.js, nodejs或全局对象挂载属性等方式都能完美地使用umd-module.js这个模块,实现了大一统。
给个sea.js调用UMD的效果页面链接,sea.js调用UMD模块[9]
而nodejs调用UMD模块需要执行node命令,
node umd-simple-used-by-nodejs效果如下:
当然,我们不能止步于此,模块会被调用,当然也会调用其他模块。因此我们还需要实现一个有依赖关系的UMD模块,来验证UMD规范的可行性。
这个简单,在html中你的模块前引入所依赖的模块即可。umd-module-depended和umd-module都是UMD模块,后者依赖前者。
<!DOCTYPE html><html> <head> <title>Test UMD</title> <!-- 依赖放前面 --> <script src="assets/js/umd-dep/umd-module-depended.js"></script> <script src="assets/js/umd-dep/umd-module.js"></script> <script src="assets/js/umd-dep/umd-global.js"></script> </head> <body> <h1>测试UMD模块</h1> <h2></h2> <p ></p> <p ></p> </body></html>点开效果页面链接[10],看得更清楚明白!
我们先在入口文件umd-main-requirejs.js中,定义好模块路径,方便调用。
require.config({ baseUrl: "./assets/js/umd-dep/", paths: { umd: "umd-module", depModule: "umd-module-depended" }});被依赖的模块umd-module-depended,只需要简单实现UMD规范即可。
而调用者umd-module,则需要做一些处理。按照require.js的规范来即可, define时,指定依赖的模块depModule,而匿名工厂函数需要在参数上接收依赖的模块depModule。
(function(root, factory) { if (typeof define === 'function' && define.amd) { console.log('是AMD模块规范,如require.js') define(['depModule'], factory) } else { console.log('没有模块环境,直接挂载在全局对象上') root.umdModule = factory(root.depModule); }}(this, function(depModule) { console.log('我调用了依赖模块', depModule) // ...省略了一些代码,去代码仓库看吧 return { name: '我自己是一个umd模块' }}))打开效果页面链接[11],看得更清楚明白!
同理,各种规范要求你怎么写模块依赖,你就怎么写就行。
(function(root, factory) { if (typeof module === 'object' && typeof module.exports === 'object') { console.log('是commonjs模块规范,nodejs环境') var depModule = require('./umd-module-depended') module.exports = factory(depModule); } else if (typeof define === 'function' && define.amd) { console.log('是AMD模块规范,如require.js') define(['depModule'], factory) } else if (typeof define === 'function' && define.cmd) { console.log('是CMD模块规范,如sea.js') define(function(require, exports, module) { var depModule = require('depModule') module.exports = factory(depModule) }) } else { console.log('没有模块环境,直接挂载在全局对象上') root.umdModule = factory(root.depModule); }}(this, function(depModule) { console.log('我调用了依赖模块', depModule) // ...省略了一些代码,去代码仓库看吧 return { name: '我自己是一个umd模块' }}))给个sea.js调用的示例链接[12]。
而nodejs调用也是通过命令行测试,
node umd-dep-used-by-nodejs效果如下:
最后厚着脸皮求个star,点亮我吧[13]
[1]
仓库: https://github.com/cumt-robin/umd-learning
[2]
回头再看JS模块化编程: http://hexo.wbjiang.cn/%E5%9B%9E%E5%A4%B4%E5%86%8D%E7%9C%8BJS%E6%A8%A1%E5%9D%97%E5%8C%96%E7%BC%96%E7%A8%8B.html
[3]
回头再看JS模块化编程之AMD: http://hexo.wbjiang.cn/%E5%9B%9E%E5%A4%B4%E5%86%8D%E7%9C%8BJS%E6%A8%A1%E5%9D%97%E5%8C%96%E7%BC%96%E7%A8%8B%E4%B9%8BAMD.html
[4]
sea.js的同步魔法: http://hexo.wbjiang.cn/sea.js%E7%9A%84%E5%90%8C%E6%AD%A5%E9%AD%94%E6%B3%95.html
[5]
Git仓库: https://github.com/cumt-robin/umd-learning
[6]
效果页面链接: https://cumt-robin.github.io/umd-learning/umd-simple-used-by-global.html
[7]
Git仓库: https://github.com/cumt-robin/umd-learning
[8]
效果页面链接: https://cumt-robin.github.io/umd-learning/umd-simple-used-by-requirejs.html
[9]
sea.js调用UMD模块: https://cumt-robin.github.io/umd-learning/umd-simple-used-by-seajs.html
[10]
效果页面链接: https://cumt-robin.github.io/umd-learning/umd-dep-used-by-global.html
[11]
效果页面链接: https://cumt-robin.github.io/umd-learning/umd-dep-used-by-requirejs.html
[12]
示例链接: https://cumt-robin.github.io/umd-learning/umd-dep-used-by-seajs.html
[13]
点亮我吧: https://github.com/cumt-robin/umd-learning
END