require.js使用详细说明

JavaScript模块化

当前的网页已经开始逐渐变成"互联网应用程序", 在网页中插入的JavaScript代码越来越庞大, 越来越复杂. 网页越来越像桌面程序了, 需要一个团队分工协作, 进度管理, 单元测试等等......开发者不得不使用软件工程方法, 管理网页的业务逻辑.

JavaScript模块化编程, 已经成为一个迫切的需求. 理想情况下, 开发者只需要实现核心的业务逻辑, 其他都可以加载别人写好的模块.

但是JavaScript不是一种模块化的编程语言, 它不支持"类", 更不用说"模块"了.

JavaScript社区做了很多努力, 在现有的运行环境下, 实现"模块"的效果. 这里总结了当前JavaScript模块化编程的最佳实践, 说明如何投入使用.

原始写法

模块就是特定功能的一组方法.
只要把不同的函数简单的放在一起, 就算是一个模块.

function a1(){
    // code here
}
function a2(){
    // code here
}

上面的函数a1()和a2(), 组成一个模块, 使用的时候, 直接调用就行了.

但是这样的做法有一个很明显的缺点: "污染"了全局变量, 无法保证不与其他模块发生变量名冲突, 而且模块成员之间看不出直接关系.

对象写法

为解决上面的问题, 可以把模块写成一个对象, 所有的模块成员都放到这个对象里面.

var module = new Object({
    _count: 0,
    a1: function(){},
    a2: function(){},
});

上面的函数a1()和a2(), 都封装在module对象中, 使用的时候, 直接调用这个对象的属性.

module.a1();

但是, 这样的写法会暴露所有模块成员, 内部状态也可以被外部随意改写. 比如, 外部代码可以直接改变内部的_count变量的值, 也可以更改a1函数的定义.

module._count = 13;

立即执行函数写法

为了解决上面的问题, 可以使用立即执行函数的写法, 来解决不暴露私有成员的目的.

var module = (function(){
    var _count = 0;
    var a1 = function(){};
    var a2 = function(){};
    return {
        a1: a1,
        a2: a2
    }
})();

使用上面的写法, 外部代码无法读取到内部的_count变量.
这种写法就是JavaScript模块的基本写法. 但还需要再对这种写法进行一些加工才能算是真正的模块化编程.

放大模式

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

var module = (function(m)(){
    m.a3 = function(){};
    return m;
})(module);

上面的代码为module模块添加一个新方法a3, 然后返回新的module模块.

宽放大模式

在浏览器环境中, 模块的各个部分通常都从网络中获取的, 获取的这部分资源你可能无法确定何时以及哪个文件先加载到, 如果采用上面的写法, 第一个执行的部分有可能加载一个不存在的空对象, 这时就要采用"宽放大模式".

var module = (function(m)(){
    m.a3 = function(){};
    return m;
})(window.module || {});

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

输入全局变量

独立性是模块的重要特点, 模块内部最好不与程序的其他部分直接交互.
为了在模块内部调用全局变量, 必须显式地将其他变量输入模块.

var module = (function($, backbone){
    // code here
})(jQuery, Backbone);

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

模块规范

想一下, 为什么模块很重要?

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

但是这样的做得有一个前提条件, 就是大家都必须以同样的方式编写模块, 否则就会乱套.

目前, 通行的JavaScript模块规范共有两种: CommonJS和AMD(CMD与AMD很相似, 就不介绍, 有需要的, 可以去了解一下sea.js).

CommonJS

2009年, 美国程序员Ryan Dahl创造了Node.js项目, 将JavaScript语言用于服务器端编程.这标志"JavaScript模块化编程"正式诞生. 因为在浏览环境下, 没有模块也不是特别大的问题, 毕竟网页程序的复杂性有限; 但是在服务器端, 一定要有模块, 与操作系统和其他应用程序互动, 否则根本没法编程.

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

var math = require('math');

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

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

本文主要讲浏览器端的JavaScript编程, 不涉及Nodejs, 所以对CommonJS不做过多的介绍, 有兴趣的话可以去搜索下CommonJS规范.

在这里大家只要晓得, require()用于加载模块就行了.

浏览器环境

有了服务器模块后, 大家可能会想要让客户端也有模块. 而且最好两者能够兼容.

但是, 由于一个重大的局限, 使得CommonJS规范不适用于浏览器环境, 还是上一节的代码, 如果在浏览器中运行, 会是一个很大的问题.

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

第二行的math.sub(3, 2), 在第一行require('math')之后运行, 因此必须等到math.js加载完成, 也就是说, 如果加载时间过长, 整个应用可能都停在那里.

这对服务器来说, 不是一个问题, 因为所有的模块都存放在本地硬盘中, 可以同步加载完成, 等待时间也就是硬盘的读取时间. 但, 对于浏览器来说, 这却是一个很大的问题, 因为模块都放在服务器端, 等待时间完成取决于网速的快慢, 可能要等很长时间, 浏览器处于"假死"状态. 因此, 浏览器端肯定是不可以用"同步加载"的方式, 只能采用"异步加载". 这也就是AMD规范诞生的背景.

AMD

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

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

require([module], callback);

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

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

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

目前, 主要有两个JavaScript库实现了AMD规范: require.js和curl.js.

下面对require.js进行详细的介绍, 如果对curl.js有兴趣的话, 大家可以自行去问问度娘.

require.js加载模块

为什么要使用require.js?

最初, 所有的JavaScript代码都写在一个文件里面, 只要加载一个文件就够了. 可是后来, 代码越来越多, 一个文件显然不行, 必须拆分, 依次加载. 下面的网页代码, 你肯定不会陌生:









上面的代码依次加载多个js文件, 但这样的写法有很大的缺陷:

  • 加载js文件时, 浏览器会停止网页渲染, 加载文件越多, 网页失去响应的时间就会越长.
  • 由于js文件之间是有依赖关系的, 必须严格保证加载顺序. 依赖性最大的模块一定要最后加载, 当依赖关系很复杂的时候, 代码的编写和维护会变得很困难.

require.js就是为了解决这些问题而产生的.

  • 实现js文件的异步加载, 避免网页失去响应.
  • 管理模块之间的依赖性, 便于代码的编写和维护.

加载require.js

在官网下载最新版的require.js, 下载地址: http://www.requirejs.org.

下载后, 假定把它放在js/libs目录下, 用下面的代码加载:


在加载 这个文件本身也可能因为网络或服务器问题, 不能成功加载, 而造成网页失去响应. 解决的方法有两个, 一个是把它放在网页的底部加载, 另一个是写成下面这样:


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

自定义主模块

加载require.js后, 就要加载自己的代码了. 假设main.js是你的主"模块", 也放在js/libs目录下, 那么只需要写成下面这样的:


                    
                    

你可能感兴趣的:(require.js使用详细说明)