模块化发展

阶段1-文件划分

将每个功能单独放在一个文件中,约定每一个文件是一个模块,当引用这个模块时,通过script标签引入到html页面中,直接使用模块中的方法或成员变量。

// moduleA.js
var name = 'moduleA';
function say(){
    console.log('say i am'+name)
}
function write(){
    console.log('write i am'+name)
}


// moduleB.js
var name = 'moduleB';
function say(){
    console.log('say i am'+name)
}
function write(){
    console.log('write i am'+name)
}

// test.html
<script src="moduleA.js"></script>
<script src="moduleB.js"></script>
<script>

// 冲突
say();

// 变量可被修改
name = 'test';
</script>

缺点:
所有的变量全部暴露在全局中,都可以被随时访问和修改,随着文件的增多,很难通过约定避免命名冲突

阶段2-命名空间

// moduleC
var moduleC = {
    name: 'moduleC',
    say: function(){
        console.log('say i am'+name)
    }
}

var moduleD = {
    name: 'moduleD',
    say: function(){
        console.log('say i am'+name)
    }
}

// test.html
<script src="moduleA.js"></script>
<script src="moduleB.js"></script>
<script>
moduleC.say();      // 正常调用

moduleC.name = 'test';      // 模块中的成员变量可被随意修改

</script>

缺点:命名空间的方法极大程度的减小了命名冲突的可能,但是也存在成员变量可能被随意修改的风险

阶段3-立即执行函数(IIFE)

// moduleE
var moduleE = (function(){
    var name = 'moduleE';
    function say(){
        console.log('say i am '+name)
    }

    return {
        say: say
    }
})()


// moduleF
var moduleF = (function(){
    var name = 'moduleF';
    function say(){
        console.log('say i am '+name)
    }

    return {
        say: say
    }
})()

<script src="moduleE.js"></script>
<script src="moduleF.js"></script>
<script>

moduleE.say();  // 正常调用
moduleF.say();  // 正常调用
console.log(moduleE.name)       //undefined
    
</script> 

实现了成员私有化,模块内的变量外界不能随意访问修改,通过立即执行函数也可以传递模块所需要的依赖,使得模块对外部的依赖更加清晰

模块化规范

CommonJS

CommonJS规定每个文件都是一个模块,有独立的作用域。每个模块内部都有一个module对象代表当前模块,通过它来导出当前模块
Commonjs的规范有以下特点:

  • 文件即模块,文件内所有代码都运行在独立的作用域,不会污染全局空间
  • 模块可以被多次引用。在第一次被加载时就会被缓存,之后都从缓存中读取
  • 加载某个模块就是引入该模块的module.exports属性
  • module.exports属性输出的是值的拷贝,一旦值被输出,模块内的变化不会影响到输出的值
  • 模块的加载顺序按照代码的引入顺序
// m1.js
module.exports = {
    name: 'm1',
    say: function(){
        console.log(this.name)
    }
}

//m2.js
exports.test = function(){
    console.log('m2')
}

var m1 = require('./m1');
var m2 = require('./m2');

m1.say();   // m1
m2.test();  // m2

exports可以理解为module.exports的引用,可以给exports对象添加方法,但不能直接赋值。因为这样会切断exports与module.exports的联系。

AMD

由于Commonjs规范加载模块是同步的,不适合浏览器环境,所以出现了异步加载的模块化标准AMD,

特点:同时并发加载所有依赖模块之后再执行当前模块的回调函数
代表库:require.js
require( [ ] ,function( ){ } )是require.js的核心之一,它接受两个参数。第一个参数是一个数组,表示所依赖的模块,第二个参数是一个回调函数,当前面指定的模块加载成功后,它将被调用。加载的模块会以参数形式传入该函数,从而在回调函数内部就可以使用这些模块

require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
   ...
});

// 定义一个模块
define(function(){
    var name = 'moduleJ';
    var sayHi = function(){
        console.log('i am require.js')
    }
    return {
        name: name,
        sayHi: sayHi
    };
})

// 引用模块
require(['moduleJ'], function(){
        moduleJ.sayHi();
    })

CMD

CMD 规范整合了 CommonJS 和 AMD 规范的特点
代表库: sea.js

CMD 最大的特点就是懒加载,不需要在定义模块的时候声明依赖,可以在模块执行时动态加载依赖。当然还有一点不同,那就是 CMD 同时支持同步加载模块和异步加载模块。

define(function(require, exports, module) {
  var add = require('math').add;
  exports.increment = function(val) {
    return add(val, 1);
  };
  module.id = "increment";
});

AMD 和 CMD 的两个主要区别

  • AMD需要异步加载模块,而CMD可以用同步的方式require依赖,也可以用require.async这种异步的方式
  • CMD遵循依赖就近原则,AMD遵循依赖前置原则。也就是说,在 AMD 中,我们需要把模块所需要的依赖都提前在依赖数组中声明。而在 CMD 中,我们只需要在具体代码逻辑内,使用依赖前,把依赖的模块 require 进来

ES6模块化

CommonJS和AMD都是在运行时确定依赖关系,即运行时加载,CommonJS加载的是拷贝,而ES6 module则是在编译时就确定依赖关系,所有加载的其实都是引用


// 导出
var first = 'test';
var second = 'test';
function func() {
    return true;
}
export {first, second, func};

//引用
import {first} from './a'
  • export default会导出默认输出,即用户不需要知道模块中输出的名字,在导入的时候为其指定任意名字。导入默认模块时不需要大括号,导出默认的变量或方法可以有名字,但是对外无效。export default只能使用一次。
原文地址:https://www.cnblogs.com/colagao/p/14490823.html