随着前端JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理解代码,也便于维护和测试。模块化是一种非常好的代码组织方式,本文试着对JavaScript模块化开发的一些基础知识和具体使用做一些阐释。

何为模块化开发?

“模块是为完成某一功能所需的一段程序或子程序。模块是任何robust(健壮、强壮)的应用架构不可缺少的一部分,是系统中职责单一且可替换的部分。”

简单理解:模块就是实现特定功能的一组方法,用用来实现代码的封装、增强代码可重用性,满足代码不断维护升级,分工合作的需要。如何开发新的模块,和复用已有模块来实现应用的功能,是我们需要考虑的事情,理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。

关于模块,更多更详细部分请参考:深入理解JavaScript 模块模式

Javascript中的模块化开发

JavaScript 的当前版本,并没有为开发者们提供以一种简洁、有条理地的方式来引入模块的方法(ECMAScipt第六版表示会支持)。

作为代替,当前的开发者们只能被迫降级使用模块模式或是对象字面量模式的各种变体。通过很多这样的方法,各模块的脚本被串在一起注入到 DOM 中(作为 script 标签注入到 DOM 中)。

但好消息是在前端前辈们的不懈努力下,如今编写模块化的 JavaScript 目前已经变得极为简单,并摸索出一些普遍适用性的标准:AMDCommonJS

Javascript模块化开发标准

CommonJS

wiki地址 http://wiki.commonjs.org/

JavaScript是一个强大、流行的语言,它有很多快速高效的解释器。官方JavaScript标准定义的API是为了构建基于浏览器的应用程序。然而,并没有定于一个用于更广泛的应用程序的标准库。

commonjs 是一个志愿性质的工作组,它致力于设计、规划并标准化 JavaScript API。从而填补原生JavaScript标准库过少的缺点。它的终极目标是提供一个类似Python,Ruby和Java标准库。它试图覆盖更宽泛的方面比如 IO、文件系统、promise 模式等等。这样的话,开发者可以使用CommonJS API编写应用程序,然后这些应用可以运行在不同的JavaScript解释器和不同的主机环境中。现在非常火爆的nodejs实际上就是commonjs的一个实现。 ——CommonJS是一种规范,NodeJS是这种规范的实现。

在CommonJS中,使用全局性方法require()加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

// 加载模块
var math = require('math');
// 调用模块提供的方法
  math.add(2,3); //

注意,math.add(2, 3),在第一行require('math')之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。

这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态。

因此,浏览器端的模块,在网站性能优化正在逐步成为产业的今天,不能采用"同步加载"(synchronous),只能采用"异步加载"(asynchronous)。这就是AMD规范诞生的背景。

AMD RequireJS介绍

AMD是“Asynchronous Module Definition”的缩写,意思就是“异步模块定义”。从名称上就可以看出,它是通过异步方式加载模块的,模块的加载不影响后续语句的执行,所有依赖加载中的模块的语句,都会放在一个回调函数中,等到该模块加载完成后,这个回调函数才运行。

它是适合script tag的,是专门为浏览器中JavaScript环境设计的规范,它有很多独特的优势,包括天生的异步及高度灵活等特性,这些特性能够解除常见的代码与模块标识间的那种紧密耦合。

RequireJS 是 AMD 规范最好的实现者之一,是一个非常小巧的 JavaScript 模块载入框架。 从架构层抽象出“模块化”开发方案,并已标准化了模块化开发,同时和其他的开发框架保持兼容。

主要用于浏览器端,但也适用于Rhino / Node 等环境,是当今最常用的JavaScript库之一,它兼容所有主流浏览器。

IE 6+ .......... compatible ✔
Firefox 2+ ..... compatible ✔
Safari 3.2+ .... compatible ✔
Chrome 3+ ...... compatible ✔
Opera 10+ ...... compatible ✔

使用RequireJS,我们能够更容易地实现更复杂,更强大的JS的富客户端程序。

使用它我们可以解决两个问题:

(1)实现js文件的异步加载,避免网页失去响应;

(2)管理模块之间的依赖性,便于代码的编写和维护。

它同时还起到了隐形命名空间的作用

注:无论当前 JavaScript 代码是内嵌还是在外链文件中,页面的下载和渲染都必须停下来等待脚本执行完成。JavaScript 执行过程耗时越久,浏览器等待响应用户输入的时间就越长,虽然可以使用async和defer关键字使得加载异步,但可能因此在加载过程中丢失加载的顺序。

require.js的使用

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

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

<!DOCTYPE html>
<html>
<head>
<title>My Sample Project</title>
<!-- data-main属性指定在require.js加载完成后,加载js/main.js文件. -->
<script data-main="js/main" src="js/require.js"></script>
</head>
<body>
<h1>My Sample Project</h1>
</body>
</html>

注意data-main属性,由于require.js默认的文件后缀名是js,所以可以把main.js简写成main。

在mian中我们就可以很愉快地进行开发了,按照这种方式,整个页面我们只需要引入这一个js文件就可以了。

我们以使用jquery 为例:

// 配置jquery 模块的文件路径
require.config({
paths: {
jquery: 'jquery-1.8.3',
underscore: 'underscore.min' // 可以同时配置多个文件
}
}); // 使用模块
require(['jquery', 'underscore'], function($, _) {
console.log($().jquery, _.VERSION);
});

如果这些模块在其他目录,比如js/lib目录,可以改变基目录(baseUrl)。

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

paths参数配置 模块名和路径, 路径可以是远程路径:http:http://ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js  后缀名可省略。

如果jquery就在main.js文件同目录下,则可以省略配置的步骤,直接使用即可。

// 使用模块
require(['jquery'], function($) {
console.log($().jquery);
});

创建AMD模块

AMD规范的API非常简单:

define(id?, dependencies?, factory);

define函数接受三个参数:

  • 第一个参数: 一个string字符串,它表示模块的标识(名称),其他模块可以用这个名称来引用本模块曝露出来的对象;可以省略该参数,缺省以文件名来命名该模块;
  • 第二个参数: 一个数组,定义了本模块需要依赖的其他模块的列表,例如jquery或者其他用户自定义模块,没有依赖的话可以为 [] ;
  • 第三个参数: 一个回调函数,在依赖的模块加载成功后,会执行这个回调函数,它的参数是所有依赖模块的引用,该函数的返回值即为本模块曝露的对象;

例如: jQuery从1.7后开始支持AMD规范,即如果jQuery作为一个AMD模块运行时,它的模块名是“jquery”。注意“jquery” 是小写的。

jQuery中的支持AMD代码如下:

if ( typeof define === "function" && define.amd && define.amd.jQuery ) {
define( "jquery", [], function () { return jQuery; } );
}

一个完整的模块定义包含模块名称,模块的依赖和回调函数,比如下面的代码:

define("adder", ["math"], function (math) {
return {
addTen : function (x) {
return math.add(x, 10);
}
};
});

如果这个模块并没有依赖,那么回调参数默认是 require,exports(一个空的输出对象,回调函数没有返回值的时候,默认返回 ), module( 模块自身 ),这时模块可以改写为:

define("adder", function (require, exports) {
exports.addTen = function (x) {
return x + 10;
};
});

如果省略第一个参数,则会定义一个匿名模块,见代码:

define( function (require, exports) {
exports.addTen = function (x) {
return x + 10;
};
});

在实际中,使用的更多的是匿名模块定义方式,因为这样更加的灵活,模块的标识和它的源代码不再相关,开发人员可以把这个模块放在任意的位置而不需要修改代码。一般只有在要使用工具打包模块到一个文件中时,才会声明第一个参数,所以应该尽量避免给模块命名。

在写模块的时候,也有可能没有依赖或者稍后才需要加载依赖

define(function (require, exports, module) {

    // ……

    var a = require('a'),
b = require('b'); exports.action = function () {
// ……
};
});

上述回调函数里的require的使用将被自动进行动态加载。

Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。的更多相关文章

  1. seajs实现JavaScript 的 模块开发及按模块加载

    seajs实现了JavaScript 的 模块开发及按模块加载.用来解决繁琐的js命名冲突,文件依赖等问题,其主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载. 官方文档:http:/ ...

  2. 浏览器环境下JavaScript脚本加载与执行探析之defer与async特性

    defer和async特性相信是很多JavaScript开发者"熟悉而又不熟悉"的两个特性,从字面上来看,二者的功能很好理解,分别是"延迟脚本"和"异 ...

  3. 浏览器环境下Javascript脚本加载与执行探析之DOMContentLoaded

    在”浏览器环境下Javascript脚本加载与执行探析“系列文章的前几篇,分别针对浏览器环境下JavaScript加载与执行相关的知识点或者属性进行了探究,感兴趣的同学可以先行阅读前几篇文章,了解相关 ...

  4. 浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入

    在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及 ...

  5. 【Hight Performance Javascript】——脚本加载和运行

    脚本加载和运行 当浏览器遇到一个<script>标签时,无法预知javascript是否在<p>标签中添加内容.因此,浏览器停下来,运行javascript代码,然后继续解析. ...

  6. 从 RequireJs 源码剖析脚本加载原理

    引言 俗话说的好,不喜欢研究原理的程序员不是好的程序员,不喜欢读源码的程序员不是好的 jser.这两天看到了有关前端模块化的问题,才发现 JavaScript 社区为了前端工程化真是煞费苦心.今天研究 ...

  7. 你不知道的JavaScript--Item26 异步的脚本加载

    先来看这行代码: <script src = "allMyClientSideCode.js"></script> 这有点儿--不怎么样."这该放 ...

  8. 高性能JavaScript-JS脚本加载与执行对性能的影响

    在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 脚本位置对性能的影响 优化页面加载性能的原则之一是将scri ...

  9. JS脚本加载与执行对性能的影响

    高性能JavaScript-JS脚本加载与执行对性能的影响 在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 ...

随机推荐

  1. GCD笔记

    GCD笔记http://www.cocoachina.com/applenews/devnews/2013/1210/7506_2.html1. 全称Grand Central Dispatch2. ...

  2. Webstorm 2016内置web服务器配置

    运行three.js的官方的例子.本来想用IIS来运行,运行不了.所以用webstorm,用鼠标右键的方式,来运行,如下图 但是有一天,我把IIS配置好了,可以在IIS中运行了(只是把build文件夹 ...

  3. ng2-timesheet, 一个timesheet.js的angular2复制版

    一个 timesheet.js (JavaScript library for HTML5 & CSS3 time sheets) 的 Angular 2 复制版 用法: npm instal ...

  4. android user build serial console

    在 init.rc 里有一段 on property:ro.debuggable=1 start console 当user debug时 ro.debuggable=0,console 不会被启动 ...

  5. hadoop 笔记(hbase)

    hbase 基础: hbase是基于列的数据,其数据模式如下: 1.安装 1.1)hbase安装分为单机.伪分布式.分布式,单机下安装不依赖于hadoop:因为不需要分布式文件系统支持: 1.2)安装 ...

  6. var与this,{}与function 小记

    JavaScript var是用来定义一个变量,this常用来定义或调用一个属性或方法.但是在全局范围内,this和var定义变量是等价的. window console.log('window:', ...

  7. Unity3D热更新全书FAQ

    只要有程序员朋友们问过两次的问题 就会收录在此FAQ中 1.C#Light对比LUA有什么好处 C#Light是静态类型脚本语言,语法同C#,Lua是动态类型脚本语言,这两种都有人喜欢. 我更喜欢静态 ...

  8. [ACM_几何] Fishnet

      http://acm.hust.edu.cn/vjudge/contest/view.action?cid=28417#problem/C 本题大意:有一个1X1的矩形,每边按照从小到大的顺序给n ...

  9. 使用MYSQL命令直接导入导出SQL文件

    很多时候,我们的数据开发都会用到很多开发利器,比如powerdesigner, navicat等这些软件,虽然好用,但是要收费,在公司里面是禁止使用盗版软件的,怕罚款各方面的,所以我们也不敢直接在公司 ...

  10. SignalR + KnockoutJS + ASP.NET MVC4 实现井字游戏

    1.1.1 摘要 今天,我们将使用SignalR + KnockoutJS + ASP.NET MVC实现一个实时HTML5的井字棋游戏. 首先,网络游戏平台一定要让用户登陆进来,所以需要一个登陆模块 ...