js模块开发的任务

首先需要了解

  1. Node Package Manager (NPM): 阅读 Getting Started guide
  2. Modern JavaScript with ES2015/16: 阅读 Babel 的 Learn ES2015 guide
  3. webpack-simple
  4. 这个在 Egghead.io上的 高级 Webpack 课程
  5. vue-loader

About-简介

前端开发在近一两年发展的非常快,JavaScript作为主流的开发语言得到了前所未有的热捧。大量的前端框架出现了,这些框架都在尝试着解决一些前端开发中的共性问题,但是实现又不尽相同。
在这个背景下,CommonJS社区诞生了,为了让前端框架发展的更加成熟,CommonJS鼓励开发人员 一起在社区里为一些完成特定功能的框架制定规范。 AMD(Asynchronous Module Definition) 就是其中的一个规范。

传统JavaScript代码的问题

让我们来看看一般情况下JavaScript代码是如何开发的:通过<script>标签来载入JavaScript文件,用全局变量 来区分不同的功能代码,全局变量之间的依赖关系需要显式的通过指定其加载顺序来解决,发布应用时要通过工具来压缩所有的JavaScript代码到一个文 件。当Web项目变得非常庞大,前端模块非常多的时候,手动管理这些全局变量间的依赖关系就变得很困难,这种做法显得非常的低效。

AMD (Asynchronous Module Definition) 的引入

从名称上看便知它是适合script tag的。也可以说AMD是专门为浏览器中JavaScript环境设计的规范。它吸取了CommonJS的一些优点,但又不照搬它的格式。

开始AMD作为CommonJS的 transport format 存在,因无法与CommonJS开发者达成一致而独立出来。它有自己的 wiki 和 讨论组 。

AMD提出了一种基于模块的异步加载JavaScript代码的机制,它推荐开发人员将JavaScript代码封装进一个个模块,对全局对象的依 赖变成了对其他模块的依赖,无须再声明一大堆的全局变量。通过延迟和按需加载来解决各个模块的依赖关系。模块化的JavaScript代码好处很明显,各 个功能组件的松耦合性可以极大的提升代码的复用性、可维护性。这种非阻塞式的并发式快速加载JavaScript代码,使Web页面上其他不依赖 JavaScript代码的UI元素,如图片、CSS以及其他DOM节点得以先加载完毕,Web页面加载速度更快,用户也得到更好的体验。

CommonJS的AMD规范中只定义了一个全局的方法:

1
define(id?, dependencies?, factory);

该方法用来定义一个JavaScript模块,开发人员可以用这个方法来将部分功能模块封装在这个define方法体内。

  • id表示该模块的标识,为可选参数。

  • dependencies是一个字符串Array,表示该模块依赖的其他所有模块标识,模块依赖必须在真正执行具体的factory方法前解决,这些依赖对象加载执行以后的返回值,可以以默认的顺序作为factory方法的参数。dependencies也是可选参数,当用户不提供该参数时,实现 AMD的框架应提供默认值为[“require”,”exports”,“module”]。

  • factory是一个用于执行改模块的方法,它可以使用前面dependencies里声明的其他依赖模块的返回值作为参数,若该方法有返回值,当该模块被其他模块依赖时,返回值就是该模块的输出。

CommonJS在规范中并没有详细规定其他的方法,一些主要的AMD框架如RequireJS、curl、bdload等都实现了define方法,同时各个框架都有自己的补充使得其API更实用。

AMD模块的写法

1
define(id?, dependencies?, factory);

其中:

  • id: 模块标识,可以省略。
  • dependencies: 所依赖的模块,可以省略。
  • factory: 模块的实现,或者一个JavaScript对象。

id遵循CommonJS Module Identifiers 。dependencies元素的顺序和factory参数一一对应。

以下是使用AMD模式开发的简单三层结构(基础库/UI层/应用层):

  1. 定义无依赖的模块 (base.js)

    1
    2
    3
    4
    5
    6
    define(function() {
    return {
    mix: function(source, target) {
    }
    };
    });
  2. 定义有依赖的模块 (ui.js,page.js)

ui.js

1
2
3
4
5
6
7
define(['base'], function(base) {
return {
show: function() {
// todo with module base
}
}
});

page.js

1
2
3
define(['data', 'ui'], function(data, ui) {
// init here
});

  1. 定义数据对象模块 (data.js)

    1
    2
    3
    4
    define({
    users: [],
    members: []
    });
  2. 具名模块

1
2
3
define('index', ['data','base'], function(data, base) {
// todo
});

具名模块多数时候是不推荐的,一般由打包工具合并多个模块到一个js文件中时使用。

  1. 包装模块
1
2
3
4
5
6
define(function(require, exports, module) {
var base = require('base');
exports.show = function() {
// todo with module base
}
});

不考虑多了一层函数外,格式和Node.js是一样的。使用require获取依赖模块,使用exports导出API。
除了define外,AMD还保留一个关键字require。 require 作为规范保留的全局标识符,可以实现为 module loader。也可以不实现。

目前,实现AMD的库有 RequireJS 、 curl 、 Dojo 、 bdLoad 、 JSLocalnet 、 Nodules 等。

也有很多库支持AMD规范,即将自己作为一个模块存在,如 MooTools 、jQuery 、 qwery 、 bonzo 甚至还有 firebug 。