JavaScript 模块化介绍

一、什么是模块

        模块通常指的是一个独立的可重用软件组件,它包含了一组相关的函数、方法、类、变量等,能够实现特定的功能或提供特定的服务。在编程中,可以将程序的功能划分成不同的模块,每个模块负责不同的任务

JavaScript的模块化是一种将代码组织到独立的、可重用的、可维护的单元中的方式,使代码更易于管理和复用。模块化可以帮助开发者将代码分解成更小的部分,这样更容易理解和维护,同时也可以防止命名冲突和全局污染。在JavaScript中,有多种模块化的标准和实现,如CommonJS、AMD、ES6等。其中ES6的模块化已经成为JavaScript的官方标准,可以直接在浏览器和Node.js中使用。

二、模块化的演变过程

2.1、全局function模式

        将可复用的变量/函数直接赋值给window;

        存在的问题:污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系

window.fun1 = function(){
    ...
}

window.fun2 = function(){
    ...
}
2.2、namespace(命名空间)模式

        简单对象封装,减少了全局变量,解决命名冲突

        存在的问题:数据不安全(外部可以直接修改模块内部的数据)

let myModule= {
    data: '123',
    fun1(){
        console.log(this.data)
    }
}

myModule.data = "abc" //能直接修改模块内部的数据
myModule.fun1() // abc
2.3、IIFE(立即执行函数)模式

       简单来说就是匿名函数的自调用。在函数内容将数据私有化, 通过给window添加属性来向外暴露接口,外部只能通过暴露的方法获取/操作数据;

        存在的问题:如果当前模块需要依赖其他模块时,无法处理。

// index.html文件



// module.js文件
(function(window) {
    let data = '123'
  
    // 操作数据的函数
    function setData(value) {
      data = value
    }
  
    function getData() {
      otherFun() //内部调用
      return data
    }
  
    // 内部私有的函数
    function otherFun() {
      console.log('otherFun()')
    }
  
    // 暴露函数
    window.myModule = { setData, getData } //ES6写法
})(window)
2.4、IIFE模式增强

        解决了IIFE模式无法引入其他模块的问题,是现代模块实现的基石。

// index.html文件


    
    
    
    


    



// module.js文件
(function(window, $){
    let data = "123"

    // 操作数据的函数
    function setData(value) {
        data = value
    }

    function getData() {
        otherFun() //内部调用
        return data
    }

    // 内部私有的函数
    function otherFun() {
        console.log('otherFun()')
    }

    // 修改背景颜色
    function updateBG(){
        // 设置随机颜色
        var randomColor = Math.floor(Math.random()*16777215).toString(16);
        if((''+randomColor).length<6) randomColor=randomColor+'0';

        $('body').css('background', '#'+randomColor)
    }

    // 暴露函数
    window.myModule = { setData, getData, updateBG } //ES6写法
})(window, jQuery)
2.5、模块化的好处
  • 避免命名冲突(减少命名空间污染);

  • 更好的分离, 按需加载;

  • 更高复用性;

  • 高可维护性;

 2.6、引入多个
3.2.3、比较

        与CommonJS相比,可以得出AMD模块定义的方法非常清晰,不会污染全局环境,能够清楚地显示依赖关系。AMD模式可以用于浏览器环境,并且允许非同步加载模块,也可以根据需要动态加载模块。

 3.3、CMD

3.3.1、概念

        CMD 是Common Module Definition(通用模块定义)的缩写;

        CMD规范同样专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行。CMD规范整合了CommonJS和AMD规范的特点。

        Sea.js:一个遵循CMD规范的JavaScript模块加载框架。

  • 官网: http://seajs.org/

  • github : GitHub - seajs/seajs: A Module Loader for the Web

3.3.2、基本使用
// 定义模块
// 1.定义没有依赖的模块
define(function(require, exports, module){
  // 暴露方式跟CommonJS一致
  exports.xxx = value
  module.exports = value
})

// 2.定义有依赖的模块
define(function(require, exports, module){
  //引入依赖模块(同步)
  var module1 = require('./module1')
  //引入依赖模块(异步)
  require.async('./module2', function (module2) {})

  exports.xxx = value
})

// 引入使用的模块
define(function (require) {
  var m1 = require('./module1')
  var m4 = require('./module4')

  m1.fun()
  m4.fun()
})

实例:

// modeule1.js
define(function(require, exports, module){
    let data = "abc"
    
    function getData(){
        return data.toUpperCase()
    }

    exports.getData = getData
})

// modeule2.js
define(function(require, exports, module){
    module.exports = {
        name: 'zhangsan'
    }
})

// modeule3.js
define(function(require, exports, module){
    var m2 = require("./module2")

    function showMsg(){
        console.log("Hello " + m2.name)
    }

    module.exports = { showMsg }
})

// main.js
define(function(require){
    //引入依赖模块(同步)
    var m3 = require("./module3")
    m3.showMsg()

    //引入依赖模块(异步)
    require.async("./module1", function(m1){
        let data = m1.getData()
        console.log("Data:", data)
    })
})

// index.html

 3.3.3、CMD和AMD的区别
// CMD
define(function (requie, exports, module) {
    //依赖就近书写
    var module1 = require('Module1');
    var result1 = module1.fun();
    module.exports = {
      result1: result1,
    }
});

// AMD
define(['Module1'], function (module1) {
    var result1 = module1.fun();
    return {
      result1: result1,
    }
});

从上面的代码比较中我们可以得出AMD规范和CMD规范的区别

  1. 对依赖的处理:

  • AMD推崇依赖前置,即通过依赖数组的方式提前声明当前模块的依赖;

  • CMD推崇依赖就近,在编程需要用到的时候通过调用require方法动态引入;

     2.在本模块的对外输出:

  • AMD通过返回值的方式对外输出;

  • CMD通过给module.exports赋值的方式对外输出;

3.4、UMD

3.4.1、概念

          UMD 是Universal Module Definition(通用模块定义)的缩写;

          UMD  是一种javascript通用模块定义规范,让你的模块能在javascript所有运行环境中发挥作用。这就意味着要同时满足CommonJS, AMD, CMD的标准。

实现:

(function(root, factory) {
    if (typeof module === 'object' && typeof module.exports === 'object') {
        console.log('是commonjs模块规范,nodejs环境')
        module.exports = factory();
    } else if (typeof define === 'function' && define.amd) {
        console.log('是AMD模块规范,如require.js')
        define(factory)
    } else if (typeof define === 'function' && define.cmd) {
        console.log('是CMD模块规范,如sea.js')
        define(function(require, exports, module) {
            module.exports = factory()
        })
    } else {
        console.log('没有模块环境,直接挂载在全局对象上')
        root.umdModule = factory();
    }
}(this, function() {
    return {
        name: '我是一个umd模块'
    }
}))

 3.5、ES6模块化

3.5.1、概念

        ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。

3.5.2、基本使用
// 定义模块 math.js
var num= 0;
var add = function (a, b) {
    return a + b;
};
export { basicNum, add };

// 引用模块
import { num, add } from './math';
function test() {
    let res = add(99 + num)
    console.log(res)
}

        如上例所示,使用import命令的时候,用户需要知道所要加载的变量名或函数名,否则无法加载。为了给用户提供方便,让他们不用阅读文档就能加载模块,就要用到export default命令,为模块指定默认输出。 

// export-default.js
export default function () {
  console.log('foo');
}

// import-default.js
import customName from './export-default';
customName(); // 'foo'

         模块默认输出, 其他模块加载该模块时,import命令可以为该匿名函数指定任意名字。

3.5.3、ES6 模块与 CommonJS 模块的差异

  1. CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用;

  2. CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。因为 CommonJS 加载的是一个对象(即module.exports属性),该对象只有在脚本运行完才会生成。而 ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。

四、总结

  1. CommonJS规范主要用于服务端编程,加载模块是同步的,这并不适合在浏览器环境,因为同步意味着阻塞加载,浏览器资源是异步加载的,因此有了AMD CMD解决方案;

  2. AMD规范在浏览器环境中异步加载模块,而且可以并行加载多个模块。不过,AMD规范开发成本高,代码的阅读和书写比较困难,模块定义方式的语义不顺畅;

  3. CMD规范与AMD规范很相似,都用于浏览器编程,依赖就近,延迟执行,可以很容易在Node.js中运行;

  4. ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案;

  5. UMD为同时满足CommonJS, AMD, CMD标准的实现;

参考文章:

1.CommonJS详解 - 简书

2.​​​​​​JS模块规范大盘点:CommonJS, AMD, CMD, UMD 和 ES6 Modules 规范与源码实现 - 知乎 .

3.ES6 入门教程

你可能感兴趣的:(javascript,开发语言,ecmascript)