JavaScript模块化编程探索

随着网站逐渐变成"互联网应用程序",嵌入网页的Javascript代码越来越庞大,越来越复杂。网页越来越像桌面程序,需要一个团队分工协作、进度管理、单元测试等等......开发者不得不使用软件工程的方法,管理网页的业务逻辑。
Javascript模块化编程,已经成为一个迫切的需求。

从CommonJS说起

CommonJS团队定义了module格式来解决JavaScript作用域问题,这样确保了每一个module都在自己的命名空间下执行。

根据CommonJS的规范,每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。

CommonJS规范规定,每个模块内部,module变量代表当前模块。这个变量是一个对象,它的exports属性(即module.exports)是对外的接口。加载某个模块,其实是加载该模块的module.exports属性。

CommonJS给出2个工具来实现模块之间的依赖:

  1. require() 用于在当前作用域引入已有的模块

  2. module object 用于从当前作用域导出一些东东

那就先搞一个Hello world的小栗子来试下吧!

编写简单的JavaScript模块

新建一个项目文件夹吧,虽然项目很小。。。起名commonjs,在里边新建2个JavaScript文件,分别命名为world.js和salute.js,代码如下:

// salute.js 打招呼
var MySalute = "Hello";
module.exports = MySalute;

/*注意上下是分别写在2个文件js文件里哦*/

// world.js
var MySalute = require("./salute");
var Result = MySalute + " world!";
console.log(Result);

然后无知的我有新建了一个demo.html,(想要在浏览器里打开看看是什么样子)内容如下:




    
    Document
    


    

结果在浏览器中打开,查看控制台大失所望,报了一个错误
world.js:2 Uncaught ReferenceError: require is not defined
发现浏览器不兼容CommonJS的根本原因,在于缺少四个Node.js环境的变量:

  • module

  • exports

  • require

  • global
    只要能够提供这四个变量,浏览器就能加载 CommonJS 模块,问题是可以解决的,但是好像并不怎么好玩,有兴趣的朋友可以去阮老师博客里逛逛啊传送门

现在我决定要去Node.js里边玩一下了

Node.js环境里玩一把

打开命令行工具cd到项目目录:

结果顺利打印出了Hello world!果然很有搞头啊,呵呵.

那么写到为止简单实现了模块之间的引用,到底这个CommonJS规范下还可以做些什么呢?到CommonJS官网看了一下,发现如下内容:

JavaScript是强调大面向对象语言,而且带有最快的解释器,而且之前的JavaScript定义的APIs仅仅用于构建浏览器端的应用,然而呢有了这个CommonJS就可以构建更宽范围的应用了,具体点就是可以用JavaScript来写:

  • 服务器端应用Server-side JavaScript applications

  • 命令行工具Command line tools

  • 基于GUI的桌面应用Desktop GUI-based applications

  • Hybrid applications (Titanium, Adobe AIR)(这个是什么?虽然我现在还不知道,但感觉它很牛逼)

AMD是用来干甚么的?

AMD (Asynchronous Module Definition)肤浅的理解异步模块定义。。。
一开始大家可能以为CommonJS的天性就是同步,它的模块系统并不适用于浏览器,而这个AMD就是指定了一个标准,证明给别人看模块化的JavaScript可以异步加载依赖,解决同步加载出现的问题。

定义

define函数是AMD定义模块的方法:
define(id?: String, dependencies?: String[], factory: Function|Object);
id:指定模块名字
dependencies:指明依赖
factory:是定义模块的,可以是function或object,如果是function那么函数的返回值就是module 导出的值。

examples

define('myModule', ['jquery'], function($) {
    // $ is the export of the jquery module.
    $('body').text('hello world');
});
// and use it
require(['myModule'], function(myModule) {});

RequireJS

随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作、模块复用、单元测试等等一系列复杂的需求。
RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一。最新版本的RequireJS压缩后只有14K,堪称非常轻量。它还同时可以和其他的框架协同工作,使用RequireJS必将使您的前端代码质量得以提升。此段出处

对比js模块的同步加载和异步加载

CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操作。AMD规范则是非同步加载模块,允许指定回调函数。由于Node.js主要用于服务器编程,模块文件一般都已经存在于本地硬盘,所以加载起来比较快,不用考虑非同步加载的方式,所以CommonJS规范比较适用。但是,如果是浏览器环境,要从服务器端加载模块,这时就必须采用非同步模式,因此浏览器端一般采用AMD规范。

浏览器同步加载js模块

新建a.js

(function(){
    function test(){
        alert("it works");
    }

    test();
})()

新建demo1.html




    
    Document
    


    body

在浏览器中运行demo1.html,alert执行的时候,html内容是一片空白的,即body并未被显示,当点击确定后,才出现,这就是JS阻塞浏览器渲染导致的结果。

RequireJS异步加载js模块

把a.js改写如下:

define(function(){
    function test(){
      alert("it works");
    }

    test();
})

到githug下载require.jsRequireJS download修改demo1.html如下:

!DOCTYPE html>


    
    Document
    
    


    body

浏览器提示了"it works",说明运行正确,但是有一点不一样,这次浏览器并不是一片空白,body已经出现在页面中,目前为止可以知道requirejs具有如下优点:

  1. 防止js加载阻塞页面渲染

  2. 管理模块之间的依赖性,便于代码的编写和维护,使得代码更加优雅。

CMD规范

CMD(Common Module Definition) 模块定义规范。该规范明确了模块的基本书写格式和基本交互规则。在 CMD 规范中,一个模块就是一个文件。代码的书写格式如下:

define(factory);

define 是一个全局函数,用来定义模块。define 接受 factory 参数,factory 可以是一个函数,也可以是一个对象或字符串。

factory 为对象、字符串时,表示模块的接口就是该对象、字符串。比如可以如下定义一个 JSON 数据模块:

define({ "foo": "bar" });

require 是一个方法,接受 模块标识 作为唯一参数,用来获取其他模块提供的接口。

define(function(require, exports) {

  // 获取模块 a 的接口
  var a = require('./a');

  // 调用模块 a 的方法
  a.doSomething();

});

require.async 方法用来在模块内部异步加载模块,并在加载完成后执行指定回调。callback 参数可选。

define(function(require, exports, module) {

  // 异步加载一个模块,在加载完成时,执行回调
  require.async('./b', function(b) {
    b.doSomething();
  });

  // 异步加载多个模块,在加载完成时,执行回调
  require.async(['./c', './d'], function(c, d) {
    c.doSomething();
    d.doSomething();
  });

});

require 是同步往下执行,require.async 则是异步回调执行。require.async 一般用来加载可延迟异步加载的模块。
更详细内容请查看CMD 模块定义规范

sea.js

RequireJS 和 Sea.js 都是模块加载器,倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
在 SeaJS 中,所有 JavaScript 文件都应该用模块的形式来书写,并且一个文件只包含一个模块。
使用全局函数 define 来定义模块:

define(id?, dependencies?, factory);

id
当前模块的唯一标识。该参数可选。如果没有指定,默认为模块所在文件的访问路径。如果指定的话, 必须是顶级或绝对标识(不能是相对标识)。
dependencies
当前模块所依赖的模块,是一个由模块标识组成的数组。该参数可选。如果没有指定,模块加载器会从 factory.toString() 中解析出该数组。
factory
模块的工厂函数。模块初始化时,会调用且仅调用一次该工厂函数。factory 可以是函数, 也可以是对象、字符串等任意值,这时 module.exports 会直接设置为 factory 值。
factory 函数在调用时,会始终传入三个参数: require、exports 和 module, 这三个参数在所有模块代码里可用。

define(function(require, exports, module) {

  // The module code goes here
  
});

more

你可能感兴趣的:(node.js,javascript,requirejs,amd模块加载)