前端模块化的简单综述(一)

刚学前端的时候,曾有一段时间很迷糊,不知道为啥突然从html文件、js文件和css文件三件套,变成需要打包在服务器才能用了。这种不明白感,随着使用vue,weex等框架逐步熟练之后,降低不少。但依然不知道,这一路究竟发生了什么。

  故在此,梳理整个前端模块的发展过程,为自己的疑惑提供一个完满的解答。(有不少内容来自对网络上一些大神的博客整理,本文会在文章最后做出引用和感谢)


目录

         0,模块的定义

1,前端最开始的处理

2,前端为什么需要模块化

3,common JS规范

4,AMD规范

5,CMD规范



 

0,模块的定义

  模块和模块化:

    模块:

在软件系统设计中,模块是一个拥有明确定义的输入输出和特性的程序实体。如果模块的所有输入都是实现功能必不可少的,所有输出都有动作产生,那么该模块即成为具有明确意义的模块。也就是说,如果少了一个输入,模块就不能实现全部功能,它没有不必要的输入,每个输入都用于产生输出,每个输出都是模块执行某一功能的结果,没有未经模块的转换就变成输出的输入。

总的来说,一般模块具有以下几种特征:

  1. 接口,模块的输入输出
  2. 功能,指模块实现的功能,有什么作用
  3. 逻辑,描述模块内部如何实现需求及所需数据
  4. 状态,指该模块的运行环境,模块间调用与被调用关系。

模块化:

    模块化就是将程序划分成若干个独立的模块,每个模块完成一个特定子功能,每个模块即相对独立,又相互联系,他们共同完成系统指定的各项功能。 模块化的目的是为了降低软件的复杂性。对软件进行适当的分解,不但可以降低复杂性,而且可以减少开发工作量,从而降低软件开发成本。


1, 前端最开始的处理:

        最自然的写法:

function foo(){
    //...
  }
  function bar(){
      //...
  }

而只将相关的代码放一起这种写法缺点很明显:

  1:大量的函数名会"污染"全局变量,无法保证不与其他模块发生变量名冲突,

  2:模块成员之间的关系不能清晰看出。

  3:没有私有变量和方法等。

 

针对问题1:和2:,可以用对象来缓解。

 var moduleA = {
    foo : function () {},
    bar: function () {}
  }

但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写

针对这个问题,可以采用立即执行函数来处理:

 var MODULE = (function () {
    var my = {}, privateVariable = 1;
    function privateMethod() {
      // method that use in module
    }
  
    my.moduleProperty = 1;
    my.moduleMethod = function () {
      // module API.
    };
  
    return my;
  }());

立即执行函数内部声明的变量和函数,为模块内部私有,return对象为供外部调用的接口。

如此方式的“模块”还可以扩展、继承、添加子模块等等。

详细参见:js模块的基本方法

如此便形成了前端模块的雏形。


2, 前端为什么需要模块化。

当下前端的快速发展,对网页的构建提出了更高要求:

  • Web sites are turning into Web Apps

  • Code complexity grows as the site gets bigger

  • Highly decoupled JS files/modules is wanted

  • Deployment wants optimized code in few HTTP calls

前端通过

代码的data-main属性不可省略,用于指定主代码所在的脚本文件

4.2 define 方法:定义模块

按照是否依赖其他模块,可以分成独立模块和非独立模块。

    独立模块的写法:

define({
    method1: function() {},
    method2: function() {},
});


// 当然也可以写为如下方式
define(function () {
  return {
    method1: function() {},
	method2: function() {},
 };
});

   

return 返回的是模块的接口。

    非独立模块

define(['module1', 'module2'], function(m1, m2) {
   ...
});



4.3 require方法:调用模块

require(['foo', 'bar'], function ( foo, bar ) {
        foo.doSomething();
});
// 当有过多依赖时,可以用commonJS 写法:
define(
    function (require) {
        var dep1 = require('dep1'),
            dep2 = require('dep2'),
            dep3 = require('dep3'),
            dep4 = require('dep4'),

            ...
    }

});

上面方法表示加载foo和bar两个模块,当这两个模块都加载成功后,执行一个回调函数。该回调函数就用来完成具体的任务。

require方法也可以用在define方法内部。比如上面的commonJS风格的写法使用。

 

5, CMD规范

CMD 即Common Module Definition通用模块定义,CMD规范是国内发展出来的,CMD有个浏览器的实现是玉伯的SeaJSSeaJS要解决的问题和requireJS一样,只不过在模块定义方式和模块加载(可以说运行、解析)时机上有所不同。

以下是对seajs使用的简单介绍:

 5.1 seajs页面的引入


seajs demo

首先是引入sea.js文件,然后是通过seajs.use加载main.js文件。main.js是一个“入口”文件。

5.2 模块的写法

比如定义一个changeText.js模块

define(function (require, exports, module) {
    var textContent = 'message from module changeText';
    module.exports = {
      text: textContent
    }
})

5.3 引入模块

define(function (require, exports, module) {
    var changeText = require('../static/changeText.js');
    var title = document.getElementById('title');
    title.innerHTML = changeText.text;
})

此时,大功告成,会在html页面上显示 “message from module changeText" 

5.4 别名 (seajs.config, alias)

 seajs.config({
   alias:{
     'changeText':'../static/changeText.js'
   }
 });

通过config中alias给'../static/changeText.js'设置了别名changeText,现在main中引用changeText模块就可以直接写成这样了var changeText = require('changeText')

5.5 seajs.use回调函数

seajs.use(['main','jquery'],function(main,$) {
    $('#title').after('');
    $('#show').on('click',function() {
         main.showText()
    })
});

上述代码我们加载了两个模块,并把它们输出的对象传参给main和$变量,通过点击事件调用main中的showText方法,而showText方法调用了changeText中的init方法。

 

参考链接:

《软件工程》叶俊民,清华大学出版社. p75-76

Javascript模块化编程(一):模块的写法

Javascript模块化编程(二):AMD规范

Seajs使用实例入门介绍

你可能感兴趣的:(js)