有更新,请到github上看源码

什么是OMD

在node.js流行起来之前,javascript的开发方式都是函数式的顺序依赖关系,直到node火起来。CommonJS其实首先提供了一个丰富的可扩展的库,在此基础上提供可应用的API,在这些API的基础上,逐渐形成一套规范,使得javascript的开发在这些规范的基础上进行。它的重要特征就是运行开发者模块化的开发javascript,提供一个模块,这个模块对外提供接口,有一个入口去调动所有的模块协同完成不同的功能。node就是在此规范上建立起来的服务端框架。

node可以被认为是应用程序开发的一个案例,它是运行在服务器上的javascript。而是否可以在浏览器上面也遵循CommonJS规范呢?为了达到这个目的,requirejs火起来,与它对应的是AMD规范。实际上,AMD规范是CommonJS的一个兼容方案,requirejs希望建立一个既满足应用程序,又满足浏览器运行的规范。AMD也逐渐被开发者接受,国外很多javascript发布产品,基本上都遵循或支持amd规范。

seajs是专注于浏览器的模块化javascript开发框架,和前两者不同的是,它只专注于浏览器端,而且更优雅灵活,它所遵循的是CMD规范,其创始人为中国的前端大牛玉伯。自此,三家分立,各有裨益,javascript的模块化开发规范已经被创立完毕,以后就将其逐渐完善,并实现更多的应用。

在此基础上,有人试图实现兼容。也就是自己的代码既能在node上运行,也能在符合amd规范的代码环境中运行。于是产生了umd,其实umd并不算规范,而只能算一种兼容方案,它可以兼容CommonJS和AMD。但国内更多的其实是使用seajs,包括淘宝、腾讯的部分产品,都是使用seajs框架,而react等的跑火,有理由相信,未来的前端开发一定是模块化趋势的。

就目前而言,为了能兼容commonjs amd cmd以及原生的javascript,我结合了前人的经验,发布了omd,它也不是什么规范,只是一套兼容方案罢了。

omd的目的,就是希望开发javascript按照这个规则来完成代码,就能使它在模块化开发环境下能跑,在普通的javascript或jquery代码中也能跑。它就是一套兼容方案,目前兼容的有module.exports、amd、cmd以及原生支持。

OMD的源码

首先,我需要将源码放出来,所有的读者可以先简单的看下它的内容,大致了解它的运行规则。在github上follow这个项目,随时了解最新的一些变化。

OMD原理详解

在《javascript立即执行某个函数:插件中function(){}()再思考》一文中,我详细阐述了!function(fun){}(function($){});,因此这里就不详细阐述了。

兼容amd和cmd

if(typeof define == 'function' && (define.amd != undefined || define.cmd != undefined) {define(function() {return fun();});
}

上面这段代码,仅在amd或cmd规范下使用时才执行define。我们这里把范围缩小到requirejs和sea.js。对于requirejs而言,define和define.amd是可见的,因此执行define(function(){});,同样的道理,seajs也会执行。但是作为两种不同的规范,为何可以以相同的代码返回呢?

在requirejs中,一个模块可以如此去定义:

define({});
define(factory);
define(id,dependencies,factor);
define(dependencies,factor)

在seajs中也差不多,但是也有不同之处:

define({});
define(factory);
define(id,dependencies,factory);
define(id,factory);

前面三种都是一样的,但是细节上也有所不同,第一种以对象的方式定义并不能解决我们插件中接口传递的目的,因此不考虑。剩下的就只有两种相同的形式,但是就像前文说到的一样,OMD是为了实现兼容,也就是说它必须作为框架被应用,框架的基础代码具有稳定性,不能让用户改来改去,否则也就失去了规范的意义。而在剩下的两种中,define(id,dependencies,factory)的前两个变量,都需要自己去定义,因此,也被排除。最终也就只有define(factory)这种形式被我们采用。

define(function(require,exports,module){var a = require('a');
});

这种代码形式在require.js和sea.js中都可以使用,可是在sea.js中,可以使用exports.xxx = function(){}来提供接口,可是在require.js中,不得不采用return的形式,幸好,在sea.js中,return也是有效的。所以,最终,我们选择了return的形式向外提供接口:

define(function(){return {fun1 : function(a,b) {},fun2 : function(c,d) {}}; // return的结果为一个对象
});

在sea.js或require.js中:

define(function(){var $omd = require('omd');var fun1 = $omd.fun1(12,34);
});

这样,在omd.js中写的插件所提供的模块接口,就可以被使用了。

兼容comomjs

当define没有被定义的时候,说明跟sea.js和require.js没有任何关系了,这个时候,我们要检查是否支持node.js。

如果是在node.js环境下运行,那么module和exports是一个由核心库提供的全局变量。因此,只需要将插件提供的接口赋值给module.exports,就完成了当前文件(模块)所提供的接口了。

兼容原生的javascript

当以上情况都不满足的情况下,实际上,你所提供的接口,就是一个函数。你提供了一个fun1的接口,你就可以在其他javascript代码中执行fun1()函数。

全局变量$和局部变量$

把$作为变量名,最大的好处是兼容jquery和zepto。在第一个function(factory){}中,我两次使用到了factory($),在这里,$没有被事先声明过,因此用了一个if(typeof $ === 'undefined')做判断。但是如果当前环境下已经加载了jquery或zepto,$就是一个全局变量,实际上它是window.$。因此,在执行factory($)时,实际上是把$作为参数传递给了factory()。

而在前面那篇关于立即执行函数的文章中我已经讲过了,在这里的factory()实际上就是第二个我们要真正用来写插件代码的function($){},要理解这一点,你必须读懂上面那篇文章。

在factory($)中,$如果代表jquery或zepto,那么它实际上是一个全局变量。而到了function($),$实际上成为函数的参数,成为一个局部变量。在function($){}中,虽然你可以使用$('#div1')进行选择,但你要知道,这里的$并非全局变量window.$,而是传递而来的,是它的引用。下文还会提到,要使用 var jQuery = $; 这样诡异的代码来处理某些情况。

factory($)返回值问题

在第一次使用factory($)时,return factory($)。第二次时,ex = factory($)。从值的返回角度讲,factory($)必然存在一个return。具体是什么意义呢?

factory($)的返回值,其实就是插件提供的对外接口,而实际上,就是一个对象Object。我们可以在第一次return factory($);的时候,也先执行一次赋值操作:

define(function(){var ex = factory($);return ex;
});

前面已经讲过了,amd和cmd在define的function中return,实际上是模块对外提供接口的一种方式,而这种方式,必须保证以对象的形式返回。因此,在插件代码中,你可以看到,我首先定义的是一个ex = {},然后执行return ex;从而对外提供了接口。

而在node.js环境下,只需要将这个ex返回值赋予exports即可完成该模块的接口。在原生的JavaScript环境下,没有接口这种概念,对外提供的,则是函数或对象属性,将它赋予window对象,就相当于提供了一个全局函数或全局变量。

OMD开发规则

利用omd开发兼容各个规范的插件(模块)时,只需要在// 真正的插件代码都在这里这句注释后面撰写插件代码即可,无需像其他教程所示一样,写一个(function($){}(jQuery)),直接写插件内容即可。如果无需对外提供接口,则写完插件代码就可以完成开发。

关于var jQuery = $

在插件代码中,一些插件并没有使用$,而是使用jQuery。但是前文已经提到了,在这个函数内部,jQuery是未定义的,$是传递过来的变量,因此,将$赋值给jQuery,则让jQuery重新有效。

如果需要对外提供接口,则在// 接口开始后面使用对象属性的方式,将接口赋值给ex。注意一点,ex返回后,你不可以通过接口改变插件内部的变量,这个接口是对外接口。

利用OMD开发的步骤

1.在开始开发插件之前,将omd.js的代码框架拷贝到你的项目(插件)文件中;

2.在注释中对应的位置开始撰写你的脚步代码

3.在注释中指出接口输出的位置处,通过ex.fun = function(){}的方式返回接口

4.完成代码写作之后,利用JavaScript压缩工具,净化和压缩你的Javascript代码

OMD使用要点

和sea.js不同,require.js的所有模块都是异步加载,这意味着你不能按照以往的方式,通过先加载哪个模块,然后加载另外一个模块来确定它们的依赖关系。不幸的是,所有的jquery插件都要依赖jquery库。因此,在require中需要解决好这个依赖问题。依赖的用法只有在define的参数中,前面已经提到了define的几种用法,我们来看下具体的实现方法:

define(['jquery'],function($){require(['plugin.omd.js'],function($plugin){$('#test').plugin();$plugin.p('#test');});
});

上面的代码中,define(['jquery'])首先确定了这个模块依赖于jquery,并将其返回接口以$作为变量。在这个基础上,再去require(omd.js),则可以让omd.js中的对jQuery的依赖可以实现,omd.js中才可以正常使用jquery。

不过,从require.js的设计上,模块之间都是异步加载的,如果按照上述方法解决依赖问题,性能上比sea.js要差很多。

我已经将这个代码放到github上,里面有demo,你可以看下plugin.js和main.js,来看看具体的用法。

OMD: javascript模块化开发兼容CommonJS, AMD, CMD 以及 原生 JS相关推荐

  1. 关于JavaScript的模块(CommonJS, AMD, CMD, ES6模块)的理解

    Javascript模块化就是解决将代码进行分隔,作用域隔离,模块之间的依赖管理等多个方面问题. 这样的优点不言而喻:1.可维护性2.命名空间私有化,可以避免污染全局环境3.代码重用,通过模块可以方便 ...

  2. JavaScript模块化开发的演进历程

    写在前面的话 js模块化历程记录了js模块化思想的诞生与变迁 历史不是过去,历史正在上演,一切终究都会成为历史 拥抱变化,面向未来 延伸阅读 - JavaScript诞生(这也解释了JS为何一开始没有 ...

  3. commonjs是什么_JavaScript模块化标准CommonJS/AMD/CMD/UMD/ES6Module的区别

    JS-模块化进程 随着js技术的不断发展,途中会遇到各种问题,比如模块化. 那什么是模块化呢,他们的目的是什么? 定义:如何把一段代码封装成一个有用的单元,以及如何注册此模块的能力.输出的值依赖引用: ...

  4. 模块格式:CommonJS AMD CMD UMD ES6

    皆为前端工程化中定义模块(module)的规则,如:模块标识,模块依赖,模块实现,模块对外接口,模块加载: 实现时有:异步,同步,依赖前置,依赖就近,预执行,懒执行,软依赖,硬依赖,模块对象支持类型等 ...

  5. CommonJS,AMD,CMD,ES6,require 和 import 详解

    CommonJS,AMD,CMD,ES6 commonJS用同步的方式加载模块.在服务端,模块文件都存在本地磁盘,读取非常快,所以这样做不会有问题.但是在浏览器端,限于网络原因,更合理的方案是使用异步 ...

  6. JavaScript模块化开发整理

    在网上已经有很多关于模块化开发的文章了,这里还是按照自己的理解来整理一下. 随着项目文件的越来越大和需求的越来越贴近现实(我发现现在客户不如:一个领导说我要审批你们报上来的资料,系统发布以后用的还不错 ...

  7. JavaScript模块化开发(一)基础知识

    2019独角兽企业重金招聘Python工程师标准>>> 随着前段JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理 ...

  8. video视频转成canvas(兼容至IE8+,全原生JS)

    video视频转成canvas(兼容至IE8+,全原生JS) <!DOCTYPE html> <html lang="en"> <head>&l ...

  9. JS JavaScript模块化(ES Module/CommonJS/AMD/CMD)

    前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了, jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得 ...

  10. Javascript模块化编程系列二: 模块化的标准化(CommonJS AMD)

    前言 Javascript模块化编程系列一: 模块化的驱动 在前一篇介绍了为什么要进行Javascript模块化编程.至于如何实现模块化,不同的开发组织和个人具体的实现方式肯定是不一样.如何统一一个规 ...

最新文章

  1. 台湾国立大学郭彦甫Matlab教程笔记(12) advanced 2D plot 下
  2. Material Master_物料类型后台配置
  3. 【服务器】【个人网盘】宝塔安装NextCloud
  4. Laravel5.1/Homestead (0.2.7) 开发环境的部署和设置
  5. devc写Java_与dev c++类似的程序编写软件(适合初学者使用,支持c,c++,java,php,pascal等).pdf...
  6. 图像增强--视网膜皮层Retinex算法(二)
  7. 一个简单的可视化模型战士的 XML 编辑器QXmlEdit
  8. uCore lab1 操作系统实验
  9. 图形学人物简史:两位图灵奖与奥斯卡得主的图形学研究往事
  10. 半加器设计(结构描述法)
  11. Golang连接池应用实践
  12. 利用最小二乘法进行线性拟合
  13. 【2020-MOOC-浙江大学-陈越、何钦铭-数据结构】春期中考试(附每一个题目的详细解析)
  14. nginx服务器的文档根目录,nginx更改根目录
  15. 双数组Trie的一种实现
  16. HEVC/H.265理论知识(3)——帧内预测
  17. 2021河北师范对口计算机分数线,河北师范大学录取分数线2021是多少分(附历年录取分数线)...
  18. 办公室免费WiFi上网,悄悄地
  19. 如何下载m3u8文件
  20. 草根程序员的光荣之路--参加移动应用的海选

热门文章

  1. 【生活相关】实验室专题研讨PPT模板说明备忘
  2. leetcode刷题日记-喧闹和富有
  3. MFC字符串CString分割函数 简洁 C++
  4. 从零基础入门Tensorflow2.0 ----二、4.2 wide deep 模型(子类API)
  5. oracle导出1000万数据,1000万条数据,最好用什么工具做,是sql,还是oracle,抑或vfp?...
  6. php分城市的源码,userlist.php 源代码在线查看 - 城市分类信息,门户网站,我做为地方信息网 资源下载 虫虫电子下载站...
  7. 虚拟服务器ftp文件权限修改,虚拟主机用户ftp和apache用户文件互操作权限解决方法...
  8. 并发编程之美(1)并发编程基础二
  9. Flink 在米哈游的落地实践
  10. 我为什么不再推荐 RxJava