JavaScript 模块化

106 8434

JavaScript模块化

模块化进化史

全局 function 模式

  • 概念:即把所有的函数全都存储到一个全局对象中global
  • 缺点:容易造成命名冲突(比如函数冲突)
  • 代码示例
    • module1.js
      
          //数据
          let data = 'com'
          
          //操作数据的函数
          function foo() {
            console.log(`foo() ${data}`)
          }
          function bar() {
            console.log(`bar() ${data}`)
          }
      
      
    • module2.js
       let data2 = 'other data';
          
       function foo() {  //与另一个模块中的函数冲突了
        console.log(`foo() ${data2}`)
       }
      
    • test.html
          <script type="text/javascript" src="module1.js"></script>
          <script type="text/javascript" src="module2.js"></script>
          <script type="text/javascript">
            let data = "修改后的数据"
            foo()
            bar()
          </script>
      

namespace 模式

  • 概念
    • 将函数封装到对象之中
  • 缺点
    • 不安全(可以直接修改模块内的数据)
  • 代码示例
    • module1.js
      let myModule = {
        data: 'com',
        foo() {
          console.log(`foo() ${this.data}`)
        },
        bar() {
          console.log(`bar() ${this.data}`)
        }
      }
      
    • module2.js
      let myModule2 = {
        data: 'com2222',
        foo() {
          console.log(`foo() ${this.data}`)
        },
        bar() {
          console.log(`bar() ${this.data}`)
        }
      }
      
    • test2.html
      <script type="text/javascript" src="module2.js"></script>
      <script type="text/javascript" src="module22.js"></script>
      <script type="text/javascript">
        myModule.foo()
        myModule.bar()
      
        myModule2.foo()
        myModule2.bar()
      
        myModule.data = 'other data' //能直接修改模块内部的数据
        myModule.foo()
      
      </script>
      

IIEF 模式

  • 概念
    • 匿名函数自调用(闭包)
    • immediately-invoked function expression(立即调用函数表达式)
  • 作用
    • 数据是私有的, 外部只能通过暴露的方法操作
  • 问题: 如果当前这个模块依赖另一个模块怎么办?
  • 代码示例
// 定义了一个立即调用函数
(function (window) {
  //数据
  let data = '.com'

  //操作数据的函数
  function foo() { //用于暴露有函数
    console.log(`foo() ${data}`)
  }

  function bar() {//用于暴露有函数
    console.log(`bar() ${data}`)
    otherFun() //内部调用
  }

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

  //暴露行为
  window.myModule = {foo, bar} // 将window对象增加了一个对象,该对象包含 foo 和 bar 方法
})(window)
  • [[JavaScript模块化之IIEF模式示例代码]]

IIEF 模式增强

  • 概念
    • IIFE模式增强 : 引入依赖
    • 这就是现代模块实现的基石
  • 代码示例
  • 引入jquery到项目中
    • module4.js
      (function (window, $) {
        //数据
        let data = '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)
      
    • test4.html
      <script type="text/javascript" src="jquery-1.10.1.js"></script>
      <script type="text/javascript" src="module4.js"></script>
      <script type="text/javascript">
        myModule.foo()
      </script>
      

页面加载多 js 问题

页面加载多个js的问题

  • 代码示例
    <script type="text/javascript" src="module1.js"></script>
    <script type="text/javascript" src="module2.js"></script>
    <script type="text/javascript" src="module3.js"></script>
    <script type="text/javascript" src="module4.js"></script>
    
  • 说明
    • 一个页面需要引入多个js文件
    • 问题:
      • 请求过多
      • 依赖模糊
      • 难以维护
      • 这些问题可以通过现代模块化编码和项目构建来解决

CommonJS-Node 模块化

  • 1、下载安装nodejs

  • 2、创建如下结构项目

    |-modules
        |-module1.js
        |-module2.js
        |-module3.js
    |-app.js
    |-package.json
    	{
          "name": "commonJS-node",
          "version": "1.0.0"
        }
    
  • 3、下载第三方模块

    npm install uniq --save
    
  • 4、模块化编码

    • module1.js 暴露一个函数
      module.exports = {
        foo() {
          console.log('moudle1 foo()')
        }
      }
      
    • module2.js 暴露一个匿名函数
      module.exports = function () {
        console.log('module2()')
      }
      
    • module3.js 暴露一个对象
      exports.foo = function () {
        console.log('module3 foo()')
      }
      
      exports.bar = function () {
        console.log('module3 bar()')
      }
      
    • app.js
      /**
        1. 定义暴露模块:
          module.exports = value;
          exports.xxx = value;
        2. 引入模块:
          var module = require(模块名或模块路径);
       */
      "use strict";
      //引用模块
      let module1 = require('./modules/module1')
      let module2 = require('./modules/module2')
      let module3 = require('./modules/module3')
      
      let uniq = require('uniq')
      let fs = require('fs')
      
      //使用模块
      module1.foo()
      module2()
      module3.foo()
      module3.bar()
      
      console.log(uniq([1, 3, 1, 4, 3]))
      
      
      
  • 5、通过node运行app.js
    * 命令: node app.js
    * 工具: 右键–>运行

CommonJS-Browserify 模块化

  • 1、创建项目结构
    |-js
      |-dist //打包生成文件的目录
      |-src //源码所在的目录
        |-module1.js
        |-module2.js
        |-module3.js
        |-app.js //应用主源文件
    |-index.html
    |-package.json
      {
        "name": "browserify-test",
        "version": "1.0.0"
      }
    
  • 2、下载browserify
    • 全局: npm install browserify -g
    • 局部: npm install browserify --save-dev
  • 3、 定义模块代码
    • module1.js
      module.exports = {
        foo() {
          console.log('moudle1 foo()')
        }
      }
      
    • module2.js
      module.exports = function () {
        console.log('module2()')
      }
      
    • module3.js
      exports.foo = function () {
        console.log('module3 foo()')
      }
      
      exports.bar = function () {
        console.log('module3 bar()')
      }
      
    • app.js (应用的主js)
      //引用模块
      let module1 = require('./module1')
      let module2 = require('./module2')
      let module3 = require('./module3')
      
      let uniq = require('uniq')
      
      //使用模块
      module1.foo()
      module2()
      module3.foo()
      module3.bar()
      
      console.log(uniq([1, 3, 1, 4, 3]))
      
  • 打包处理js:
    • browserify js/src/app.js -o js/dist/bundle.js
  • 页面使用引入:
    <script type="text/javascript" src="js/dist/bundle.js"></script> 
    

AMD-RequireJS 模块化

  • 1、下载require.js, 并引入
  • 2、创建项目结构
        |-js
          |-libs
            |-require.js // 存放第三方库
          |-modules
            |-alerter.js
            |-dataService.js
          |-main.js
        |-index.html
    
  • 3、定义require.js的模块代码
    • dataService.js
      // 使用define传入了一个匿名函数
      define(function () {
        let msg = 'com'
      
        function getMsg() {
      	return msg.toUpperCase()
        }
      
        return {getMsg}
      })
      
    • alerter.js
      // 使用define传入了第三方库和一个匿名函数
      define(['dataService', 'jquery'], function (dataService, $) {
        let name = 'Tom2'
      
        function showMsg() {
          $('body').css('background', 'gray')
          alert(dataService.getMsg() + ', ' + name)
        }
      
        return {showMsg}
      })
      
  • 4、应用主入口:main.js
        // 定义一个立即调用函数
        (function () {
          //配置
          requirejs.config({
            //基本路径
            baseUrl: "js/",
            //模块标识名与模块路径映射
            paths: {
              "alerter": "modules/alerter",
              "dataService": "modules/dataService",
            }
          })
          
          //引入使用模块
          requirejs( ['alerter'], function(alerter) {
            alerter.showMsg()
          })
        })()
    
  • 5、页面使用模块:
    • index.html
      // 参数1指定了入口的配置文件,参数2指定了库位置
        <script data-main="js/main" src="js/libs/require.js"></script>
      
  • 6、使用第三方基于require.js的框架(jquery)
    • 1、将jquery的库文件导入到项目:
      • js/libs/jquery-1.10.1.js
    • 2、在main.js中配置jquery路径
      paths: {
                'jquery': 'libs/jquery-1.10.1'
            }
      
    • 3、在alerter.js中使用jquery
      define(['dataService', 'jquery'], function (dataService, $) {
          var name = 'xfzhang'
          function showMsg() {
              $('body').css({background : 'red'})
              alert(name + ' '+dataService.getMsg())
          }
          return {showMsg}
      })
      
  • 7、使用第三方不基于require.js的框架(angular)
    • 1、将angular.js导入项目
      • js/libs/angular.js
    • 2、在main.js中配置
      (function () {
        require.config({
          //基本路径
          baseUrl: "js/",
          //模块标识名与模块路径映射
          paths: {
            //第三方库
            'jquery' : './libs/jquery-1.10.1',
            'angular' : './libs/angular',
            //自定义模块
            "alerter": "./modules/alerter",
            "dataService": "./modules/dataService"
          },
          /*
           配置不兼容AMD的模块
           exports : 指定与相对应的模块名对应的模块对象
           */
          shim: {
            'angular' : {
              exports : 'angular'
            }
          }
        })
        //引入使用模块
        require( ['alerter', 'angular'], function(alerter, angular) {
          alerter.showMsg()
          console.log(angular);
        })
      })()	
      
  • [[JavaScript模块化之AMD-RequireJS完整配置示例代码]]

CMD-Sea.js模块化

  1. 下载sea.js, 并引入
  1. 创建项目结构
|-js
  |-libs
    |-sea.js
  |-modules
    |-module1.js
    |-module2.js
    |-module3.js
    |-module4.js
    |-main.js
|-index.html
  1. 定义sea.js的模块代码
  • module1.js
    define(function (require, exports, module) {
      //内部变量数据
      var data = 'com'
      //内部函数
      function show() {
        console.log('module1 show() ' + data)
      }
    
      //向外暴露
      exports.show = show
    })
    
  • module2.js
    define(function (require, exports, module) {
      module.exports = {
        msg: 'I Will Back'
      }
    })
    
  • module3.js
    define(function (require, exports, module) {
      const API_KEY = 'abc123'
      exports.API_KEY = API_KEY
    })
    
  • module4.js
    define(function (require, exports, module) {
      //引入依赖模块(同步)
      var module2 = require('./module2')
    
      function show() {
        console.log('module4 show() ' + module2.msg)
      }
    
      exports.show = show
      //引入依赖模块(异步)
      require.async('./module3', function (m3) {
        console.log('异步引入依赖模块3  ' + m3.API_KEY)
      })
    })
    
  • main.js : 主(入口)模块
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
    
  1. index.html:
<!--
使用seajs:
  1. 引入sea.js库
  2. 如何定义导出模块 :
    define()
    exports
    module.exports
  3. 如何依赖模块:
    require()
  4. 如何使用模块:
    seajs.use()
-->
<script type="text/javascript" src="js/libs/sea.js"></script>
<script type="text/javascript">
  seajs.use('./js/modules/main')
</script>

ES6-Babel-Browserify

  • 1、 定义package.json文件
    {
      "name" : "es6-babel-browserify",
      "version" : "1.0.0"
    }
    
  • 2、安装babel-cli, babel-preset-es2015和browserify
      npm install babel-cli browserify -g 
      npm install babel-preset-es2015 --save-dev 
      preset 预设(将es6转换成es5的所有插件打包)
    
  • 3、定义.babelrc文件
    {
      "presets": ["es2015"]
    }
    
  • 4、编码
    • js/src/module1.js 分别暴露
      export function foo() {
        console.log('module1 foo()');
      }
      export function bar() {
        console.log('module1 bar()');
      }
      export const DATA_ARR = [1, 3, 5, 1]
      
    • js/src/module2.js 统一暴露
      let data = 'module2 data'
      
      function fun1() {
        console.log('module2 fun1() ' + data);
      }
      
      function fun2() {
        console.log('module2 fun2() ' + data);
      }
      
      export {fun1, fun2}
      
    • js/src/module3.js
      export default {
        name: 'Tom',
        setName: function (name) {
          this.name = name
        }
      }
      
    • js/src/app.js
      import {foo, bar} from './module1'
      import {DATA_ARR} from './module1'
      import {fun1, fun2} from './module2'
      import person from './module3'
      
      import $ from 'jquery'
      
      $('body').css('background', 'red')
      
      foo()
      bar()
      console.log(DATA_ARR);
      fun1()
      fun2()
      
      person.setName('JACK')
      console.log(person.name);
      
  • 5、编译
    • 使用Babel将ES6编译为ES5代码(但包含CommonJS语法) : babel js/src -d js/lib
    • 使用Browserify编译js : browserify js/lib/app.js -o js/lib/bundle.js
  • 6、页面中引入测试
        <script type="text/javascript" src="js/lib/bundle.js"></script>
    
  • 7、引入第三方模块(jQuery)
    • 1). 下载jQuery模块:
      • npm install jquery@1 --save
    • 2). 在app.js中引入并使用
      import $ from 'jquery'
      $('body').css('background', 'red')
      
  • [[JavaScript模块化之ES6-Browserify完整示例代码]]

Prev Post CSS选择器
Next Post webpack5开发模式配置