JS进阶篇--RequireJS模块化编程详解

1.模块的写法

模块化编程平常都有这么几个过渡历程,以下形貌。

原始要领

function m1(){
  //...
}
function m2(){
  //...
}

上面的函数m1()和m2(),构成一个模块。运用的时刻,直接挪用就好了。

这类做法的瑕玷很显著:”污染”了全局变量,没法保证不与其他模块发作变量名争执,而且模块成员之间看不出直接关联。

对象写法

为了处理上面的瑕玷,可以把模块写成一个对象,一切的模块成员都放到这个对象内里。

var module1 = new Object({
    _count : 0,
    m1 : function (){
      //...
    },
    m2 : function (){
      //...
    }
  });

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

module1.m1();

然则,如许的写法会暴露一切模块成员,内部状况可以被外部改写。比方,外部代码可以直接转变内部计数器的值。

module1._count = 5;

马上实行函数写法

运用”马上实行函数”(Immediately-Invoked Function Expression,IIFE),可以到达不暴露私有成员的目标。

var module1 = (function(){
    var _count = 0;
    var m1 = function(){
      //...
    };
    var m2 = function(){
      //...
    };
    return {
      m1 : m1,
      m2 : m2
    };
  })();

运用上面的写法,外部代码没法读取内部的_count变量。

console.info(module1._count); //undefined

module1就是Javascript模块的基础写法。下面,再对这类写法举行加工。

放大情势

假如一个模块很大,必需分红几个部份,或许一个模块须要继续另一个模块,这时候就有必要采纳”放大情势”(augmentation)。

var module1 = (function (mod){
    mod.m3 = function () {
      //...
    };
    return mod;
  })(module1);

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

宽放大情势(Loose augmentation)

在浏览器环境中,模块的各个部份一般都是从网上猎取的,偶然没法晓得哪一个部份会先加载。假如采纳上一节的写法,第一个实行的部份有可以加载一个不存在空对象,这时候就要采纳”宽放大情势”。

var module1 = ( function (mod){
    //...
    return mod;
  })(window.module1 || {});

与”放大情势”比拟,"宽放大情势"就是”马上实行函数”的参数可以是空对象。

输入全局变量

独立性是模块的重要特性,模块内部最好不与顺序的其他部份直接交互。

为了在模块内部挪用全局变量,必需显式地将其他变量输入模块。

var module1 = (function ($, YAHOO) {
    //...
  })(jQuery, YAHOO);

上面的module1模块须要运用jQuery库和YUI库,就把这两个库(实际上是两个模块)看成参数输入module1。如许做除了保证模块的独立性,还使得模块之间的依靠关联变得显著。

2.AMD范例

2009年,美国顺序员Ryan Dahl制造了node.js项目,将javascript言语用于服务器端编程。

这标志”Javascript模块化编程”正式降生。因为老实说,在浏览器环境下,没有模块也不是迥殊大的题目,毕竟网页顺序的复杂性有限;然则在服务器端,肯定要有模块,与操作体系和其他运用顺序互动,不然基础没法编程。

node.js的模块体系,就是参照CommonJS范例完成的。在CommonJS中,有一个全局性要领require(),用于加载模块。假定有一个数学模块math.js,就可以像下面如许加载。

var math = require('math');

然后,就可以挪用模块供应的要领:

var math = require('math');
  math.add(2,3); // 5

因为这个系列重要针对浏览器编程,不触及node.js,所以对CommonJS就不多做引见了。我们在这里只需晓得,require()用于加载模块就好了。

有了服务器端模块今后,很天然地,人人就想要客户端模块。而且最好二者可以兼容,一个模块不必修正,在服务器和浏览器都可以运转。

然则,因为一个严重的范围,使得CommonJS范例不适用于浏览器环境。照样上一节的代码,假如在浏览器中运转,会有一个很大的题目,你能看出来吗?

var math = require('math');
  math.add(2, 3);

第二行math.add(2, 3),在第一行require(‘math’)以后运转,因而必需等math.js加载完成。也就是说,假如加载时候很长,全部运用就会停在那边等。

这对服务器端不是一个题目,因为一切的模块都存放在当地硬盘,可以同步加载完成,等待时候就是硬盘的读取时候。然则,关于浏览器,这倒是一个大题目,因为模块都放在服务器端,等待时候取决于网速的快慢,可以要等很长时候,浏览器处于”假死”状况。

因而,浏览器端的模块,不能采纳”同步加载”(synchronous),只能采纳”异步加载”(asynchronous)。这就是AMD范例降生的背景。

AMD是”Asynchronous Module Definition”的缩写,意义就是”异步模块定义”。它采纳异步体式格局加载模块,模块的加载不影响它背面语句的运转。一切依靠这个模块的语句,都定义在一个回调函数中,比及加载完成以后,这个回调函数才会运转。

AMD也采纳require()语句加载模块,然则不同于CommonJS,它请求两个参数:

require([module], callback);

第一个参数[module],是一个数组,内里的成员就是要加载的模块;第二个参数callback,则是加载胜利以后的回调函数。假如将前面的代码改写成AMD情势,就是下面如许:

require(['math'], function (math) {
    math.add(2, 3);
  });

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

3.require.js的加载

<script src="js/require.js"></script>

有人可以会想到,加载这个文件,也可以形成网页落空相应。处理办法有两个,一个是把它放在网页底部加载,另一个是写成下面如许:

<script src="js/require.js" defer async="true" ></script>

async属性表明这个文件须要异步加载,防止网页落空相应。IE不支撑这个属性,只支撑defer,所以把defer也写上。

加载require.js今后,下一步就要加载我们本身的代码了。假定我们本身的代码文件是main.js,也放在js目次下面。那末,只须要写成下面如许就好了:

<script src="js/require.js" data-main="js/main"></script>

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

require.config()的设置

运用require.config()要领,我们可以对模块的加载行动举行自定义。require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载途径。

require.config({
    baseUrl: "js/lib",
    paths: {
      "jquery": "jquery.min",
      "underscore": "underscore.min",
      "backbone": "backbone.min"
    }
  });

AMD模块的写法

模块必需采纳特定的define()函数来定义。假如一个模块不依靠其他模块,那末可以直接定义在define()函数当中。

假定如今有一个math.js文件,它定义了一个math模块。那末,math.js就要如许写:

// math.js
  define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
  });

加载要领以下:

// main.js
  require(['math'], function (math){
    alert(math.add(1,1));
  });

假如这个模块还依靠其他模块,那末define()函数的第一个参数,必需是一个数组,指明该模块的依靠性。

define(['myLib'], function(myLib){
    function foo(){
      myLib.doSomething();
    }
    return {
      foo : foo
    };
  });

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

define()的完全定义:

define('sample3' ,['sample','sample1'],function (sample,sample1) {
    var sample4 = require('sample4');
    return function(){
        alert(sample.name+':'+sample.sayhell());
    }
});

关于define函数的name和require函数的依靠称号之间的关联
1)define(name,[] , callback); 这个name可以免却,默许是文件称号;固然也可以自定义,一旦我们定义了name,依据源代码我们可以发明define函数内部实在就是把这个name以及依靠模块、回调函数作为一个对象存储在全局的数组当中,也就是 defQueue.push([name,deps,callback]);那末这个name就是这个组件注册的的ID!

2)require([name , name2],callback); 体系起首会在全文检索path中是不是对应的途径,假如没有天然把他作为途径拼接在baseUrl上去异步加载这个js文件,加载时从源代码中可以看到 ,var data = getScriptData(evt);返回的 data.id 实在就是name,然后实行contex.completeLoad(node.id),其内部就很清晰了,把define中注册的name和这里获得的name举行比较假如相称就实行,所以原理就是:require 和 define 的 name 必需保证一致!

标签加载完成以后,猎取标签的唯一标识name

加载非范例的模块

举例来说,underscore和backbone这两个库,都没有采纳AMD范例编写。假如要加载它们的话,必需先定义它们的特性。

require.config({
    shim: {

      'underscore':{
        exports: '_'
      },
      'backbone': {
        deps: ['underscore', 'jquery'],
        exports: 'Backbone'
      }
    }
  });

require.config()接收一个设置对象,这个对象除了有前面说过的paths属性以外,另有一个shim属性,特地用来设置不兼容的模块。具体来说,每一个模块要定义(1)exports值(输出的变量名),表明这个模块外部挪用时的称号;(2)deps数组,表明该模块的依靠性。

比方,jQuery的插件可以如许定义:

shim: {
    'jquery.scroll': {
      deps: ['jquery'],
      exports: 'jQuery.fn.scroll'
    }
  }

require.js插件

require.js还供应一系列插件,完成一些特定的功用。

domready插件,可以让回调函数在页面DOM构造加载完成后再运转。

require(['domready!'], function (doc){
    // called once the DOM is ready
  });

text和image插件,则是许可require.js加载文本和图片文件。

define([
    'text!review.txt',
    'image!cat.jpg'
    ],

    function(review,cat){
      console.log(review);
      document.body.appendChild(cat);
    }
  );

相似的插件另有json和mdown,用于加载json文件和markdown文件。

参考地点:
Javascript模块化编程(一):模块的写法
Javascript模块化编程(二):AMD范例
Javascript模块化编程(三):require.js的用法
RequireJS define 细致引见

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