require.js

(一)JavaScript模块化

    什么是模块:模块就是用于实现特定功能一组方法

    I.只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块;

      function m1(){

          //......

      }

     function m2(){

        //......

     }

     上面的函数,m1,m2就组成了一个模块。使用的时候直接调用就行了。

     缺点:1.污染全局变量,无法保证不与其他模块命名冲突;2.模块成员之间看不出直接关系

    II.对象的写法:

     var module1=new Object({

       _count:0;

       m1:function(){

                //...

           };

       m2:function(){

             //...

          };

     });    

     上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。

     module1.m1();

     缺点: 这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。

     module1._count=5;

    III.使用匿名函数(立即执行函数写法)[这就是用的闭包的思想]

    可以达到不暴露私有成员的目的。

         var module1=(function(){

           var _count=0;

           var m1=function(){

                        //......

                      };

           var m2=function(){

                       //......

                     } ;

          return {m1:m1;m2:m2}

          })()      

        module1.m1()

        

      VI.放大模式

       如果一个模块很大,必须分成几个部分,或者一个模块需要继承另一个模块,这时就有必要采用"放大模式"(augmentation)

        var module1=(function(mod){

                               mod.m3=function(){ 

                                                              //......

                                                          }

                             return mod;

                           })(module1) 

        上面的代码为module1模块添加了一个新方法m3(),然后返回新的module1模块。       

      V.宽放大模式

       在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式"。

  var module1 = ( function (mod){

    //...

    return mod;

  })(window.module1 || {});

       与"放大模式"相比,"宽放大模式"就是"立即执行函数"的参数可以是空对象。   

     六、输入全局变量

     独立性模块的重要特点模块内部最好不与程序的其他部分直接交互

     为了在模块内部调用全局变量,必须显式地将其他变量模块。

     var module1=(function($,YAHOO){

                           //...

                        })(Jquery,YAHOO)

       上面的module1模块需要使用jQuery库和YUI库,就把这两个库(其实是两个模块)当作参数输入module1。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。

       以上是我们一般需要写的模块!

       在一个JavaScript文件中可以写很多个闭包!

       但是模块,指的就是单独放在一个文件中用一个匿名立即执行函数包起来的。例如:

      

      模块的特点:1.独立性:一个模块单独在一个js文件中;

                        2.封闭性:用匿名立即执行函数包围;

                        3.规范性:模块分为规范性模块和非规范性模块。规范性模块就是AMD规范模块;

   (二)AMD规范异步模块定义

      上面介绍了JavaScript模块的基本写法,这里介绍如何规范地定义和使用模块;模块分为:1.AMD规范模块

                                                                                                                              2.非规范模块

      其中,AMD规范模块就是符合AMD模式的模块,即用define定义的模块。      

      要想理解模块并正确的使用模块,那么我们需要首先学习一下require.js

     I.什么是require.js?

      require.js就是用js写的一个模块加载器。这个模块加载器,对外提供了三个变量或者称之为方法:define,require,require.js;

      1.define:创建AMD规范的模块。该函数用户创建模块。每个模块拥有一个唯一的模块ID,它被用于RequireJS的运行时函数,define函数是一个全局函数,不需要使用requirejs命名空间. 

      例如:

          (1).创建一个math模块

          

       (2).在main.js中使用这个模块   

          

         分析: 这里先创建一个math.js文件,然后在这个文件里面在define(callback)方法中传入要创建模块(注:这里的回调函数只要是一个匿名函数。)

                    然后,在需要调用这个math模块的地方,用require方法加载即require(['math'],function(math){

                                                                                                                                                math.add()....

                                                                                                                                            })

        一般define()有三个参数:

                                     第一个参数是name:即定义模块外部引用是的名字或者符号;如果没有这个参数,如上例,则使用文件的名字(math);

                                       

                                     

                                    如果有name,则需要在使用这个模块之前调用require的config方法去配置路径。即:配置jq这个模块的路径;

                                    第二个参数就是这个模块所依赖的模块。是一个数组;

                                    第三个参数是匿名回调函数,即这个模块的代码; 

           【define一般我们自己不怎么用,都是写库(模块)的人使用define去写AMD规范模块】       

        2.require()– 该函数用于读取依赖。同样它是一个全局函数,不需要使用requirejs命名空间.【这个是引用模块。一般我们要使用库,都可以使用这个来引入库】

          主要用于加载模块:

           

            有两个参数:第一个参数是一个数组,这个数组存储着所依赖的模块,第二个参数是一个回调函数。

            例如:我们写的js需要引用jQuery等模块,则可以这么写

             require(['jqeury'],function($){

                   //这里是我们自己写的代码

                 })

        3. require.config()– 该函数用于配置RequireJS【写在主模块的头部,配置好了才能去加载模块】

            config是require函数对象的一个方法,主要用来配置模块

            config方法有一个参数,就是一个对象。这个对象有几个属性:baseurl,paths,shim

                                                                                               paths:引入个模块的路径,例如:‘jq’:'js/jquery-3.6.0';jq就是外部引用模块的一个代表或者是模块的名字,后面的属性值就是这个模块的路径。

                                                                                                shim:对于非AMD规范的模块,我们就需要用shim属性规定这些模块。

                                                                                                            shim对象属性{

                                                                                                                                  "模块一":{

                                                                                                                                                  deps:['模块'];//该模块所依赖的模块

                                                                                                                                                  export:‘_’;//这个属性的值是和widow对象中,该模块所对应的属性。

                                                                                                                                                  }

                                                                                                                                }

              例如:我们没有用define定义一个模块:

                       

                       在使用这个模块的时候,需要这么使用:

                        

                       分析总结:1.没有通过define定义的模块math,其实就是将一个由方法组成的对象赋值给变量math,math变量是widow对象的一个属性:

                       

                        可能有人就疑惑了,既然math是widow对象的一个属性,那么直接用不就行了还需要一些乱七八糟的过程干嘛?一会config,一会又require的?(或许只有我会这么想)

                   math不是摆平无故的成为widow对象的属性,是执行了math.js之后,才称为widow的属性。故而,需要将这个math.js加载。那么加载一方面可以通过<script>标签,另一方面就是需要使用require.js实现加载!为了能够正确地被加载,就需要在require.config()中进行配置。config是为了正确地让require去加载模块。

      模块加载了,我们就可以在require的回调函数中使用这些模块,即调用这些模块的方法来实现我们的开发 。其实,无论什么框架(模块),如jQuery等都是通过JavaScript实现的一些方法库即模块。 这些库是为什么能够在其他模块中使用呢:

                                                                                                             主要方面:模块都是javascript写的一组方法。这些方法被匿名函数包含着,最后返回给引用匿名函数的一个对象。而这个对象是window的一个属性,而window属性在任何一个JavaScript中都可以被调用,故而就可以实现在一个模块中可以引用另一个模块。                                                                                                                                                      

      附加先想一想,为什么模块很重要?

      因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。

      模块的使用方法:

                              1.直接在页面的script标签中加载各种模块。例如:

                              

                              这里6.js的依赖性最强,必须等前面5个都加载完了之后再加载这个模块。比如,这里我们假设6.js就是我们自己写的a.js文件,1.js是jQuery.js;2.bootscapt.js;3.YOOTOO.js;4...;5...;这里,这些模块都是用JavaScript写的一些方法(故而称为模块:能够完成特定功能的一组方法)。故而,在6.js中,就是去调用这些方法从而实现页面的功能【这些方法都是编写者封装好的,一般只给我们提供api】虽然这些模块都是通过JavaScript写的方法,但是其模块的用法各方面还是不一样的,模块的名称也不一样。

                           2.使用require方法加载各模块(注:这里,require本身也是一个模块) 

                            require的用法:只需要在页面中加载一个require模块就可以了。

                            

      使用模块这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套!考虑到Javascript模块现在还没有官方规范,这一点就更重要了。

      目前,通行的Javascript模块规范共有两种:CommonJSAMD。我主要介绍AMD,但是要先从CommonJS讲起。

      I.CommonJS

       node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

       var math=require('math');

       然后就可以调用模块提供的方法:

       var math=require('math');

       math. add(2,3)  //5

       我们在这里只要知道,require()用于加载模块就行了。

       II.AMD

       AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行

        AMD也采用require()语句加载模块,但是不同于CommonJS,它要求两个参数:

        

       第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。如果将前面的代码改写成AMD形式,就是下面这样:

       

      math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。

      目前,主要有两个Javascript库实现了AMD规范:require.jscurl.js。本系列的第三部分,将通过介绍require.js,进一步讲解AMD的用法,以及如何将模块化编程投入实战。

     

    (三)require.js的用法  参考网址:http://www.ruanyifeng.com/blog/2012/11/require_js.html

       最早的时候,所有Javascript代码都写在一个文件里面,只要加载这一个文件就够了【注:所有的script标签引入的js文件,都可以写在一个JavaScript文件中,比如下面的6个模块,可以写在一个文件中,比如1.js写在最上面,接着2.js,...最后是6.js.代码在执行的时候从上到下依次加载执行,比如当用户点击一个元素的时候,就调用上面1——6之间对应的方法。】。后来,代码越来越多,一个文件不够了,必须分成多个文件,依次加载。下面的网页代码,相信很多人都见过。

      

      这段代码依次加载多个js文件。(这和写在一个js文件里面从上到下加载一样的)

      这样的写法有很大的缺点:首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;

                                          其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。(比如,jQuery的引入必须在JavaScript的前面引入)

      require.js的诞生,就是为了解决这两个问题

                                                                                 (1)实现js文件的异步加载,避免网页失去响应【用require引入的各模块是异步加载的,不需要按照顺序等一个加载完了,再去加载另一个模块。切记:不是因为在页面中只有一个require.js,其余的js文件都在主模块中引入!!!因为我们加载完require.js之后,马上就去加载data-main中的js文件,这个文件中使用require方法加载各模块,这不就跟页面中有script标签因为很多的模块时一样的了?主要就是因为用require方法加载的模块可以异步加载,故而节约了时间,从而提高了效率】;

                                                                                 (2)管理模块之间的依赖性,便于代码的编写和维护。【比如主模块中代码一般写在require的回调函数中,即依赖模块加载好了之后再去执行回调函数】

  (1)require.js的加载

      使用require.js的第一步,是先去官方网站下载最新版本。

      下载后,假定把它放在js子目录下面,就可以加载了。

      

       async属性表明这个文件需要异步加载,避免网页失去响应。IE不支持这个属性,只支持defer,所以把defer也写上。

       加载require.js以后,下一步就要加载我们自己的代码了。假定我们自己的代码文件是main.js也放在js目录下面。那么,只需要写成下面这样就行了:

      

       data-main属性的作用是,指定网页程序的主模块。在上例中,就是js目录下面的main.js,这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

  (2)主模块的写法

       上一节的main.js,我把它称为"主模块",意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。

       下面就来看,怎么写main.js。

       如果我们的代码不依赖任何其他模块,那么可以直接写入javascript代码。

    

     但这样的话,就没必要使用require.js了。真正常见的情况是,主模块依赖于其他模块,这时就要使用AMD规范定义的的require()函数。

       

      require()函数接受两个参数。第一个参数是一个数组,表示所依赖的模块,上例就是['moduleA', 'moduleB', 'moduleC'],即主模块依赖这三个模块;第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块会以参数形式传入该函数从而回调函数内部就可以使用这些模块。

       require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题。

        下面,我们看一个实际的例子。

         假定主模块依赖jquery、underscore和backbone这三个模块,main.js就可以这样写:

     

       require.js会先加载jQuery、underscore和backbone,然后再运行回调函数。主模块的代码就写在回调函数中

      例如,我们通过require加载jQuery这个插件模块:

      1.首先,这里,jQuery.js文件夹和main.js还有require.js在同一目录中:require加载模块和直接在script标签中加载模块还是有相似之处:都需要URL地址;不同之处:script直接就根据url地址请求加载;而require需要一方面是请求加载,另一方面需要将对应的模块作为参数传递,并且在回调函数中使用这些作为参数传进来的模块 

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <script src="js/requirejs.js"   data-main="js/main"></script>
</head>
<body>
<p>
    这里测试require.js的用法
</p>
</body>
</html>

    main.js中: 

require.config({
   // jQuery:'/js/jquery-3.6.0'
});
require(['jquery'],function ($) {
    $(function () {
       $("p").click(function () {
          $(this).slideToggle();
       });
    });
});

    可以正确使用jQuery模块了。 

   上例中,jQuery文件和main.js在同一目录中,那么,如果不在同一目录中,require去哪里加载jQuery.js文件呢?这就涉及到路径的问题

  (3)模块的加载

       上一节最后的示例中,主模块的依赖模块是['jquery', 'underscore', 'backbone']。默认情况下,require.js假定这三个模块与main.js在同一个目录,文件名分别为jquery.js,underscore.js和backbone.js,然后自动加载。

      使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(main.js)的头部参数就是一个对象{},这个对象中包括几个属性:

     1.paths:[这里的paths中的属性值也不能带有.js或者.min]

     这个对象的paths属性指定各个模块的加载路径:

     

     上面的代码给出了三个模块的文件名,路径默认与main.js在同一个目录(js子目录)。如果这些模块在其他目录,比如js/lib目录,则有两种写法。一种是逐一指定路径。

        

      2.baseUrl

      

       如果某个模块在另一台主机上,也可以直接指定它的网址,比如:

       

        require.js要求,每个模块是一个单独的js文件。这样的话,如果加载多个模块,就会发出多次HTTP请求,会影响网页的加载速度。因此,require.js提供了一个优化工具,当模块部署完毕以后,可以用这个工具将多个模块合并在一个文件中,减少HTTP请求数。     

       3.shim

        require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。

        

     五、AMD模块的写法

         require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。

         具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

         假定现在有一个math.js文件,它定义了一个math模块。那么,math.js就要这样写:

         

         

        如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

        

        当require()函数加载上面这个模块的时候,就会先加载myLib.js文件。

     六、加载非规范的模块

        理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。但是实际上,虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合。那么,require.js是否能够加载非规范的模块呢?

        回答是可以的。

        这样的模块在用require()加载之前,要先用require.config()方法,定义它们的一些特征。

        举例来说,underscore和backbone这两个库,都没有采用AMD规范编写。如果要加载它们的话,必须先定义它们的特征。

       

        require.config()接受一个配置对象,这个对象除了有前面说过的paths属性之外,还有一个shim属性,专门用来配置不兼容的模块。具体来说,每个模块要定义(1)exports值(输出的变量名),表明这个模块外部调用时的名称;(2)deps数组,表明该模块的依赖性。

        

         

原文地址:https://www.cnblogs.com/yyn120804/p/10103621.html