wayne0 2019-09-05
这个仓库记录了一些关于javascript UMD
模块规范的demo
,对我学习UMD
规范有了很大帮助,希望也能帮助到你。
之前也写了几篇关于javascript
模块的博客,链接如下:
近几天准备总结一下javascript
模块的知识点,所以建了这个Git仓库,如果能帮助到您,麻烦点个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
,视运行环境而定。
打开效果页面链接(要看源码的话,点开Git仓库),观察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模块' } }))
打开效果页面链接,可以看到,原则是调用者先加载,所依赖的模块后加载。
同理,接着判断当前环境是否满足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模块
而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 id="content"></p> <p id="content2"></p> </body> </html>
点开效果页面链接,看得更清楚明白!
我们先在入口文件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模块' } }))
打开效果页面链接,看得更清楚明白!
同理,各种规范要求你怎么写模块依赖,你就怎么写就行。
(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
调用的示例链接。
而nodejs
调用也是通过命令行测试,
node umd-dep-used-by-nodejs
效果如下:
以上实现了简单的UMD
模块,也验证了UMD
模块间存在依赖关系时的可行性。虽然本文是以简单对象导出为例,但足以作为我们深入UMD
规范的起点,加油!
最后厚着脸皮求个star
,点亮我吧
扫一扫下方小程序二维码或搜索Tusi博客
,即刻阅读最新文章!