[TOC]

在JavaScript发展初期就是为了实现简单的页面交互逻辑,寥寥数语即可;如今CPU、浏览器性能得到了极大的提升,很多页面逻辑迁移到了客户端(表单验证等),随着web2.0时代的到来,Ajax技术得到广泛应用,jQuery等前端库层出不穷,前端代码日益膨胀,此时在JS方面就会考虑使用模块化规范去管理。本文内容主要有理解模块化,为什么要模块化,模块化的优缺点以及模块化规范,并且介绍下开发中最流行的CommonJS, AMD, ES6、CMD规范。本文试图站在小白的角度,用通俗易懂的笔调介绍这些枯燥无味的概念,希望诸君阅读后,对模块化编程有个全新的认识和理解!

建议下载本文源代码,自己动手敲一遍,请猛戳GitHub个人博客
image.png

一、模块化的理解

1.什么是模块?

  • 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
  • 块的内部数据与实现是私有的, 只是向外部暴露一些接口(方法)与外部其它模块通信

    2.模块化的进化过程

  • 全局function模式 : 将不同的功能封装成不同的全局函数

    • 编码: 将不同的功能封装成不同的全局函数
    • 问题: 污染全局命名空间, 容易引起命名冲突或数据不安全,而且模块成员之间看不出直接关系
      function m1(){
      //...
      }
      function m2(){
      //...
      }
      
  • namespace模式 : 简单对象封装

    • 作用: 减少了全局变量,解决命名冲突
    • 问题: 数据不安全(外部可以直接修改模块内部的数据)
      let myModule = {
      data: 'www.baidu.com',
      foo() {
      console.log(`foo() ${this.data}`)
      },
      bar() {
      console.log(`bar() ${this.data}`)
      }
      }
      myModule.data = 'other data' //能直接修改模块内部的数据
      myModule.foo() // foo() other data
      
      这样的写法会暴露所有模块成员,内部状态可以被外部改写。
  • IIFE模式:匿名函数自调用(闭包)

    • 作用: 数据是私有的, 外部只能通过暴露的方法操作
    • 编码: 将数据和行为封装到一个函数内部, 通过给window添加属性来向外暴露接口
    • 问题: 如果当前这个模块依赖另一个模块怎么办?
      // index.html文件
      <script type="text/javascript" src="module.js"></script>
      <script type="text/javascript">
      myModule.foo()
      myModule.bar()
      console.log(myModule.data) //undefined 不能访问模块内部数据
      myModule.data = 'xxxx' //不是修改的模块内部的data
      myModule.foo() //没有改变
      </script>
      
      // module.js文件
      (function(window) {
      let data = 'www.baidu.com'
      //操作数据的函数
      function foo() {
      //用于暴露有函数
      console.log(`foo() ${data}`)
      }
      function bar() {
      //用于暴露有函数
      console.log(`bar() ${data}`)
      otherFun() //内部调用
      }
      function otherFun() {
      //内部私有的函数
      console.log('otherFun()')
      }
      //暴露行为
      window.myModule = { foo, bar } //ES6写法
      })(window)
      
      最后得到的结果:
      image.png
  • IIFE模式增强 : 引入依赖

这就是现代模块实现的基石

// module.js文件
(function(window, $) {
  let data = 'www.baidu.com'
  //操作数据的函数
  function foo() {
    //用于暴露有函数
    console.log(`foo() ${data}`)
    $('body').css('background', 'red')
  }
  function bar() {
    //用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }
  function otherFun() {
    //内部私有的函数
    console.log('otherFun()')
  }
  //暴露行为
  window.myModule = { foo, bar }
})(window, jQuery)
 // index.html文件
  <!-- 引入的js必须有一定顺序 -->
  <script type="text/javascript" src="jquery-1.10.1.js"></script>
  <script type="text/javascript" src="module.js"></script>
  <script type="text/javascript">
    myModule.foo()
  </script>

上例子通过jquery方法将页面的背景颜色改成红色,所以必须先引入jQuery库,就把这个库当作参数传入。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显

3. 模块化的好处

  • 避免命名冲突(减少命名空间污染)
  • 更好的分离, 按需加载
  • 更高复用性
  • 高可维护性

    4. 引入多个