OMD: javascript模块化开辟兼容CommonJS, AMD, CMD 以及 原生 JS

有更新,请到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,来看看详细的用法。

    原文作者:唐霜
    原文地址: https://segmentfault.com/a/1190000004371154
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞