随着前端的发展,无模块化存在的问题日益显露。但在这个过程中,模块化也是有一定的发展。

 stage1-文件划分方式

根据业务或功能封装某一类变量或者函数;

约定每一个文件都是一个模块;

当使用到这个模块的时候,通过script标签引入到html页面中,直接使用模块中的成员(变量|函数);

但当代码体积达到一定量的时候,这种方法的缺点就很明显了。

//module-a.js
// module a 相关状态数据和功能函数

var name = 'module-a'

function method1 () {
  console.log(name + '#method1')
}

function method2 () {
  console.log(name + '#method2')
}
//module-b.js
// module b 相关状态数据和功能函数

var name = 'module-b'

function method1 () {
  console.log(name + '#method1')
}

function method2 () {
  console.log(name + '#method2')
}
//index.html
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    // 命名冲突
    method1()
    // 模块成员可以被修改
    name = 'foo'
  </script>

缺点:

  1. 全部变量都暴露在全局作用域中,没有私有空间,所有成员都可被外部访问或者修改
  2. 当模块达到一定数量,通过约定的方式很难避免变量冲突
  3. 无法管理模块与模块之间的依赖关系

stage2-命名空间的方式

每个模块只暴露一个全局对象,所有的成员都挂载到这个全局对象上

//module-a.js
var moduleA = {
  name: 'module-a',

  method1: function () {
    console.log(this.name + '#method1')
  },

  method2: function () {
    console.log(this.name + '#method2')
  }
}
//module-b.js
var moduleB = {
  name: 'module-b',

  method1: function () {
    console.log(this.name + '#method1')
  },

  method2: function () {
    console.log(this.name + '#method2')
  }
}
//index.html
  <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
    // 模块成员可以被修改
    moduleA.name = 'foo'
  </script>

优点:

     通过“命名空间”减小了命名冲突的可能

 缺点:

     模块内部的成员依然在外部可以被访问和修改

     模块之间的依赖关系依然不明确

stage3-通过立即执行函数(IIFE)为模块提供私有空间

具体做法就是将模块内的代码放在一个立即执行函数中

若想把某个成员暴露出去,就把该成员挂载到window中

//module-a.js
;(function () {
  var name = 'module-a'

  function method1 () {
    console.log(name + '#method1')
  }

  function method2 () {
    console.log(name + '#method2')
  }

  window.moduleA = {
    method1: method1,
    method2: method2
  }
})()
//module-b.js
;(function ($) {
  var name = 'module-b'

  function method1 () {
    console.log(name + '#method1')
  }

  function method2 () {
    console.log(name + '#method2')
  }

  window.moduleB = {
    method1: method1,
    method2: method2
  }
console.log($('#a').html()); })(jQuery)
//index.html
 <script src="module-a.js"></script>
  <script src="module-b.js"></script>
  <script>
    moduleA.method1()
    moduleB.method1()
    // 模块私有成员无法访问
    console.log(moduleA.name) // => undefined
  </script>

优点:

      实现了成员私有化,模块内的变量外界不能随意访问

      通过立即执行函数,可以传递该模块所依赖的模块,使模块对外部的依赖更加清晰

小结:综上所述,随着模块化的发展,之前所提到的问题也逐一被解决,但还是存在一部分没有解决的问题。先前所有模块都是通过script标签引入的。若我们需要某个模块时,通过script标签引入了该模块,但可能会忘记引入该模块所依赖的模块。或者当我们删除某个模块时,我们也可能会忘记该模块所依赖的模块。

stage4-模块化规范的出现

     1.CommonJS

        特点:

1.一个文件就是一个模块

        2.每个模块都有一个单独的作用域

   3.每个模块暴露出去的成员都由module.exports导出

        4.使用其他模块的成员时使用require函数导入

       CommonJS规范主要引用在Node项目中,该规范加载模块时同步加载,倘若用在浏览器中,会有大量的同步请求导致应用比较慢,但在Node中不会有问题(因为Node是启动时加载模块)

    2.AMD规范(Asynchronous Module Definition

       代表库:require.js

       特点:

       不同于CommonJS的同步加载,该模块可以异步加载模块,提高程序在浏览器的执行效率

       但使用起来相对复杂

    3.CMD规范( Common Module Definition )

       代表库:Sea.js

       特点:

        使用上类似于require.js,写法类似于CommonJS,算是个重复的轮子

    4.ES Modules(ES2015的新语法)

       特点:

        语言层面推出的新语法,比较完善

        满足在web端开发使用模块化的需求

综上所述:目前前端模块化基本统一成了CommonJS(Node.js)和ES Modules(浏览器端开发),我们只需掌握这两种规范开发即可,后续会对着两个模块重点展开讨论

02-04 20:37