前端模块设计规范

Author Avatar
GoDotDotDot 3月 08, 2017
  • 在其它设备中阅读本文章

概述

在经历了web 1.0时代之后,JavaScript这种脚本语言经过了应用开发血洗之后,如今已经步入web2.0时代,或者说是webapp时代。在web2.0流行的过程中,JavaScript已经不能称作脚本语言了,或者说脚本语言这个名词已经体现不出它的价值了,已经脱胎换骨,晋升为编程语言的行列,原有的编程模式已不能满足网页应用业务的复杂度。

在其他高级语言中,JavaScript不具有模块或者说是包引入机制,只是通过<script>标签来引入代码,而这种方式无疑使代码看起来杂乱无章,不能很好地组织业务逻辑。在过去,前端工程师一般都是通过命名空间等方式人为的让它向javaPHP等这些高级语言靠近。

什么是模块化

模块化是指在解决某一个复杂问题或者一系列的杂糅问题时,依照一种分类的思维把问题进行系统性的分解以之处理。模块化是一种处理复杂系统分解为代码结构更合理,可维护性更高的可管理的模块的方式。可以想象一个巨大的系统代码,被整合优化分割成逻辑性很强的模块时,对于软件是一种何等意义的存在。对于软件行业来说:解耦软件系统的复杂性,使得不管多么大的系统,也可以将管理,开发,维护变得“有理可循”。

还有一些对于模块化一些专业的定义为:模块化是软件系统的属性,这个系统被分解为一组高内聚,低耦合的模块。那么在理想状态下我们只需要完成自己部分的核心业务逻辑代码,其他方面的依赖可以通过直接加载被人已经写好模块进行使用即可。

主流模块规范

目前社区主要有以下几大规范:

  • CommonJS
  • AMD
  • CMD

CommonJS

CommonJS分为三个部分,分别为:

  • 模块引用
  • 模块定义
  • 模块标识

在node上,我们到处都可以见到CommonJS规范的身影,但是nodej又并非完全实现规范,而是做了一定的取舍和新增。

模块引用

为了能够引用其他模块,CommonJS提供了require()方法,该方法接受一个 模块标识 参数。对于该参数说明,请参见 模块标识,示例代码如下:

var math = require('math')

math.add(1,2) // 3

通过require方法就将需要的模块引入到当前作用域中,这里require方法其实返回的是想要引入模块上的exports对象,这样我们就可以调用该模块下暴露在exports对象上的所有成员了。

模块定义

在知道了如何引入一个模块之后,在感受到了模块化的强大之后,是否你也曾想自己亲手写一个模块供自己或其他人使用呢?下面就让我们来了解一下模块定义。

要想了解模块定义,我们首先需要知道一个module这么个东西。module是什么呢?

module这个对象代表的是模块自身,其中module.exports属性表示模块对外输出的值。我们的模块对外暴露出什么API,完全取决于exports这个属性,下面就用一个示例来演示如何向外暴露模块:

// math.js
// 定义个add方法,接受a,b两个参数,返回a+b的值
var add = (a,b)=> a+b
module.exports.add = add
// or
// module.exports = add // 注意当模块只有一个对外接口的时候才可以这样用,否则将会导致其他接口被切断引用,而无法被外部调用到!

此时add对象挂载在exports属性上,这个地方调用方法需要做下说明:

var math = require('./math.js')
// 对于采用module.exports.add = add
math.add(1,2) // 3
// 对于采用module.exports = add
math(1,2) // 3

原因很好理解,对于第一种add方法是附加在exports属性上,而第二种是直接将exports属性切断引用,重新赋值,将导致exports属性永远只有一个对象,那就是add

模块标识

模块标识就是给require方法传递的参数,该参数是一个字符串类型,可以以...开头的相对路径或者绝对路径。默认文件名后缀是js,如果你愿意多敲三个字符,那你就做吧。

AMD

AMD全名Asynchronous Module Definition,即异步模块定义,模块和模块的依赖可以被异步加载。AMD规范是CommonJS模块规范的一个延伸。

define(id?, dependencies?, factory);

这里只叙述CommonJS的异同,关于AMD详细说明以及使用,请点击这里

AMD需要用define来明确定义一个模块,其他大同小异。

CMD

CMD规范是由国内的玉伯提出,与AMD规范主要区别在与定义模块和依赖引入的部分。关于CMD的详细说明,请点击这里

对于AMD和CMD的不同,下面引用玉伯本人在知乎的回答,原文请点击这里

  • 对于依赖的模块,AMD 是提前执行,CMD 是延迟执行。不过 RequireJS 从 2.0 开始,也改成可以延迟执行(根据写法不同,处理方式不同)。CMD 推崇 as lazy as possible.
  • CMD 推崇依赖就近,AMD 推崇依赖前置。看代码:
// CMD
define(function(require, exports, module) {
   var a = require('./a')
   a.doSomething()
   // 此处略去 100 行
   var b = require('./b') // 依赖可以就近书写
   b.doSomething()
   // ... 
})
// AMD 默认推荐的是
define(['./a', './b'], function(a, b) {  // 依赖必须一开始就写好
    a.doSomething()
    // 此处略去 100 行
    b.doSomething()
    ...
})

虽然 AMD 也支持 CMD 的写法,同时还支持将 require 作为依赖项传递,但 RequireJS 的作者默认是最喜欢上面的写法,也是官方文档里默认的模块定义写法。

  • AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。比如 AMD 里,require 分全局 require 和局部 require,都叫 require。CMD 里,没有全局 require,而是根据模块系统的完备性,提供 seajs.use 来实现模块系统的加载启动。CMD 里,每个 API 都简单纯粹

有兴趣的朋友可以查看这篇知乎另一篇回答

基于MIT开源协议
本文链接:http://blog.godotdotdot.com/2017/03/08/前端模块设计规范/