Javascript模块化开发,使用模块化脚本加载工具RequireJS,提高你代码的速度和质量。
随着前端JavaScript代码越来越重,如何组织JavaScript代码变得非常重要,好的组织方式,可以让别人和自己很好的理解代码,也便于维护和测试。模块化是一种非常好的代码组织方式,本文试着对JavaScript模块化开发的一些基础知识和具体使用做一些阐释。
何为模块化开发?
“模块是为完成某一功能所需的一段程序或子程序。模块是任何robust(健壮、强壮)的应用架构不可缺少的一部分,是系统中职责单一且可替换的部分。”
简单理解:模块就是实现特定功能的一组方法,用用来实现代码的封装、增强代码可重用性,满足代码不断维护升级,分工合作的需要。如何开发新的模块,和复用已有模块来实现应用的功能,是我们需要考虑的事情,理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块。
关于模块,更多更详细部分请参考:深入理解JavaScript 模块模式
Javascript中的模块化开发
JavaScript 的当前版本,并没有为开发者们提供以一种简洁、有条理地的方式来引入模块的方法(ECMAScipt第六版表示会支持)。
作为代替,当前的开发者们只能被迫降级使用模块模式或是对象字面量模式的各种变体。通过很多这样的方法,各模块的脚本被串在一起注入到 DOM 中(作为 script 标签注入到 DOM 中)。
但好消息是在前端前辈们的不懈努力下,如今编写模块化的 JavaScript 目前已经变得极为简单,并摸索出一些普遍适用性的标准:AMD、CommonJS
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,提高你代码的速度和质量。的更多相关文章
- seajs实现JavaScript 的 模块开发及按模块加载
seajs实现了JavaScript 的 模块开发及按模块加载.用来解决繁琐的js命名冲突,文件依赖等问题,其主要目的是令JavaScript开发模块化并可以轻松愉悦进行加载. 官方文档:http:/ ...
- 浏览器环境下JavaScript脚本加载与执行探析之defer与async特性
defer和async特性相信是很多JavaScript开发者"熟悉而又不熟悉"的两个特性,从字面上来看,二者的功能很好理解,分别是"延迟脚本"和"异 ...
- 浏览器环境下Javascript脚本加载与执行探析之DOMContentLoaded
在”浏览器环境下Javascript脚本加载与执行探析“系列文章的前几篇,分别针对浏览器环境下JavaScript加载与执行相关的知识点或者属性进行了探究,感兴趣的同学可以先行阅读前几篇文章,了解相关 ...
- 浏览器环境下JavaScript脚本加载与执行探析之动态脚本与Ajax脚本注入
在<浏览器环境下JavaScript脚本加载与执行探析之defer与async特性>中,我们研究了延迟脚本(defer)和异步脚本(async)的执行时机.浏览器支持情况.浏览器bug以及 ...
- 【Hight Performance Javascript】——脚本加载和运行
脚本加载和运行 当浏览器遇到一个<script>标签时,无法预知javascript是否在<p>标签中添加内容.因此,浏览器停下来,运行javascript代码,然后继续解析. ...
- 从 RequireJs 源码剖析脚本加载原理
引言 俗话说的好,不喜欢研究原理的程序员不是好的程序员,不喜欢读源码的程序员不是好的 jser.这两天看到了有关前端模块化的问题,才发现 JavaScript 社区为了前端工程化真是煞费苦心.今天研究 ...
- 你不知道的JavaScript--Item26 异步的脚本加载
先来看这行代码: <script src = "allMyClientSideCode.js"></script> 这有点儿--不怎么样."这该放 ...
- 高性能JavaScript-JS脚本加载与执行对性能的影响
在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 脚本位置对性能的影响 优化页面加载性能的原则之一是将scri ...
- JS脚本加载与执行对性能的影响
高性能JavaScript-JS脚本加载与执行对性能的影响 在web产品优化准则中,很重要的一条是针对js脚本的加载和执行方式的优化.本篇文章简单描述一下其中的优化准则. 1. 脚本加载优化 1.1 ...
随机推荐
- 欢迎访问我的最新个人技术博客http://zhangxuefei.top
博客园已停止更新,欢迎访问我的最新个人技术博客http://zhangxuefei.top
- 依赖注入的威力,.NET Core的魅力:解决MVC视图中的中文被html编码的问题
有园友在博问中提了这样一个问题 —— .NET Core 中文等非英文文字html编码输出问题,到我们的 ASP.NET Core 项目中一看,也是同样的问题. 比如下面的Razor视图代码: @{ ...
- mongodb(mongoose-redis-cache)
在传统的项目中,我们经常会用到缓存来优化数据库的读取,比如java中,我们利用spring的AOP能力,在读写数据库前增加对缓存的操作. 在node与mongodb的项目中也仍然会存在类似问题,本文参 ...
- Java虚拟机6:内存溢出和内存泄露、并行和并发、Minor GC和Full GC、Client模式和Server模式的区别
前言 之前的文章尤其是讲解GC的时候提到了很多的概念,比如内存溢出和内存泄露.并行与并发.Client模式和Server模式.Minor GC和Full GC,本文详细讲解下这些概念的区别. 内存溢出 ...
- 浅谈Excel开发:五 Excel RTD函数
上文介绍了Excel中的UDF函数,本文介绍一下同样重要的RTD函数.从Excel 2002开始,Excel引入了一种新的查看和更新实时数据的机制,即real-time data简称RTD函数 ...
- ubuntu 安装 axel
Axel 通过打开多个 HTTP/FTP 连接来将一个文件进行分段下载,从而达到加速下载的目的.对于下载大文件,该工具将特别有用. 安装:sudo apt-get install axel 一般使用: ...
- 怎样记住Integer的最大值(有趣的思维和搞笑的回答)
前言 今天一个同事问我,数据库里面的某表如果用int做PK,那该表最多可以放多少记录,我说简单啊,就是2^31(正数),跟.NET的Int32.MaxValue一样,约等于20亿(正数)吧.同事说,那 ...
- 10 个 Redis 建议/技巧
转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/145.html?1455790611 Redis 在当前的技术社区里是非常 ...
- 缓存篇(Cache)~第二回 使用static静态成员实现服务器端缓存(导航面包屑)~续
返回目录 在写完首篇cache文章后,有些朋友给我来信,希望可以使用JS的方法来实现这个导航功能,所以,才有了这篇文章,JS与C#不同,它虽然有引用类型,但它不具备一些引用类型的特性,如它的方法参数为 ...
- display的理解
display可把框内显示的内容改变(自我理解) none 此元素不会被显示. block 此元素将显示为块级元素,此元素前后会带有换行符. inline 默认.此元素会被显示为内联元素,元素前后没有 ...