JavaScript 模块化编程(一)

Author : luckyGirl

工作后做的项目使用的是YUI库,遇到很蹩脚的一个问题就是外部JS的引用,由于引用的文件有时候会很多,并由于之间有依赖关系,希望它们能够依次加载,就形成下面的代码,相信很多人也遇到过:
<script src=”1.js”></script>
<script src=”2.js”></script>
<script src=”3.js”></script>
<script src=”4.js”></script>
<script src=”5.js”></script>
<script src=”6.js”></script>

这样的写法有很大的缺点,首先加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的1.js要在2.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。

后来发现了JavaScript模块化的概念,有了模块就能够更加方便地使用别人的代码,想要啥功能就加载什么模块,JavaScript模块规范主要分为3类:CommonJSAMDCMD

  • CommonJS:旨在建立一个服务器端、桌面端、命令行以及浏览器上面的生态环境,node.js就是参照CommonJS而实现的,将JavaScript语言用于服务器端编程,由于本次分享主要是针对浏览器编程,对CommonJS就不多做介绍了,只需知道它有一个全局性方法require()用于加载模块
  • AMD(Asynchronous Module Definition):异步模块定义,它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。AMD的主要有点在于实现js文件的异步加载,避免网页失去响应;管理模块之间的依赖性,便于代码的编写和维护.AMD也采用require()语句加载模块,它要求有2个参数require([module], callback),实现AMD的库主要有RequireJS,curl,Dojo,bdLoad,JSLocalnet,Nodules等
  • CMD(Common Module Definition):该规范确定了模块的基本书写格式和基本交互方式,与 AMD 规范相比,CMD 规范尽量保持简单,并与 CommonJS 的 Modules 规范保持了很大的兼容性。

接下来主要介绍一下比较流行的加载器之一RequireJS
1. 下载require.js的最新版本
下载后在页面引入即可(之下默认require.js同其他JS文件在同一目录js下)
<script src=”js/require.js”></script>
为避免网页失去响应,解决办法有两个,一个是把它放在网页底部加载,另一个是写成下面这样:
<script src=”js/require.js” defer async=”true”></script>
2. 加载我们自己的JS文件,假定自己的JS文件为main.js
<script src=”js/require.js” defer async=”true” data-main=”js/main”></script>
data-main属性的作用是,指定网页程序的主模块,由于require.js默认的文件后缀名是js,所以可以把 main.js简写成main
3. 主模块的写法
需要使用RequireJS通常是因为主模块依赖于其他模块,这就需要使用AMD规范定义的require()函数
main.js内容
require([‘module1’, ‘module2’, ‘module3’],function(module1, module2, module3){…})
require()异步加载moduleA,moduleB和moduleC,浏览器不会失去响应;它指定的回调函数,只有前面的模块都加载成功后,才会运行,解决了依赖性的问题, 假定主模块依赖jquery、canvas和sub这三个模块,main.js就可以这样写:
require([‘jquery’, ‘canvas’, ‘sub’], function ($, canvas, sub) { … });
以上是JS文件都是在同一目录下的写法,当目录不同时可以使用require.config()方法, require.config()就写在主模块(main.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径, 如
require.config({
paths: {
‘jquery’: ‘https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min’,
‘canvas’: ‘app/canvas’,
‘sub’: ‘app/sub’
}
});

4. AMD模块的写法
采用require.js加载的模块需要按照AMD的规定来写,即必须采用特定的define()函数来定义
模块不依赖其他模块
define(function(){
var add = function(){};
return {
add: add
}
});

模块还依赖于其他模块
define([‘math’],function(math){
var add = function(){
math.addMethod();
};
return {
add: add
}
});

5. 加载非规范的模块
理论上,require.js加载的模块,必须是按照AMD规范、用define()函数定义的模块。虽然已经有一部分流行的函数库(比如jQuery)符合AMD规范,更多的库并不符合,这种情况require.js依然是能够加载的,首先需要使用require.config()来定义它们的特征,shim是用来配置一些没有使用define()声明模块间的依赖性,如canvas模块依赖与jquery模块,这时可以使用shim,
require.config(
shim: {
‘canvas’: {
deps: [‘underscore’, ‘jquery’],
exports: ‘Canvas’
},
‘sub’: {
deps: [‘bar’],
exports: ‘Sub’
}}
);

6. require.js插件
一系列插件能够为require.js实现一些特定功能,如domready插件,可以让回调函数在页面DOM结构加载完成后再运行;text和image插件,则是允许require.js加载文本和图片文件;类似的插件还有json和mdown,用于加载json文件和markdown文件
当然,以上是使用了JS模块规范,下一篇将介绍一下普通JS编码时如何产生模块效果.

参考资料:http://blog.sina.com.cn/s/blog_63ff72a6010174r7.html

JavaScript 模块化编程(一)

JavaScript 模块化编程(二)

JavaScript 模块化编程(三)

standard

Have your say