三、了解前端模块化系列(4)——模块化规范-CMD & ES6

164 阅读6分钟

了解前端模块化系列内容:
三、了解前端模块化系列(1)——模块化的理解
三、了解前端模块化系列(2)——模块化规范-CommonJS
三、了解前端模块化系列(3)——模块化规范-AMD
三、了解前端模块化系列(4)——模块化规范-CMD & ES6
三、了解前端模块化系列(5)——模块化规范-UMD & 总结(CommonJS AMD CMD ES6 UMD)

三、模块化规范

3.3 CMD(Common Module Definition)

3.3.1 概念

  • CMD 专用 浏览器端
  • 异步加载模块
  • 使用时加载执行模块
  • CMD 整合 CommonJS 和 AMD 的特点。
  • Sea.js——JS 模块遵循 CMD 模块定义规范。

3.3.2 基本语法

  1. 模块定义

    • 无依赖 CMD 定义
    define(function (require, exports, module) {
      // 无依赖 CMD ——定义
      exports.xxx = value
      module.exports = value
    })
    
    • 有依赖 CMD 定义
    define(function (require, exports, module) {
      // 有依赖 CMD ——定义
      var module2 = require('./module2') //引入(同步)依赖模块
    
      require.async('./module3', function (m3) {}) //引入(异步)依赖模块
    
      exports.xxx = value //暴露模块
    })
    
  2. 模块使用

    • 引入/使用 CMD 模块
    // CMD ——引入/使用
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
    })
    

3.3.3 CMD 实现

  1. 下载 sea-2.2.3.js ,并引入。

  2. 创建项目结构

    |-js
      |-libs
        |-sea.js
      |-modules
        |-module1.js
        |-module2.js
        |-module3.js
        |-module4.js
        |-main.js
    |-index.html
    
  3. 定义 modules 目录下的模块代码

    js/modules/module1.js

    // module1.js文件
    define(function (require, exports, module) {
      var data = 'djsz3y.com' //内部变量数据
    
      function show() {
        console.log('module1 show() ' + data)
      } //内部函数
    
      exports.show = show //向外暴露
    })
    

    js/modules/module2.js

    // module2.js文件
    define(function (require, exports, module) {
      module.exports = {
        msg: 'I am djsz3y'
      }
    })
    

    js/modules/module3.js

    // module3.js文件
    define(function (require, exports, module) {
      const API_KEY = 'abcd1234'
      exports.API_KEY = API_KEY //向外暴露
    })
    

    js/modules/module4.js

    // 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)
      }) //引入(异步)依赖模块
    
      // console.log("--------与上面相反一下--------");
    
      // var module3 = require("./module3"); //引入(同步)依赖模块 module3
    
      // function show2() {
      //   console.log("module4 show2() " + module3.API_KEY);
      // } //内部函数
    
      // exports.show2 = show2; //向外暴露
    
      // require.async("./module2", function (m2) {
      //   console.log("异步引入依赖模块2  " + m2.msg);
      // }); //引入(异步)依赖模块 module2
    })
    

    js/modules/main.js

    // main.js文件
    define(function (require) {
      var m1 = require('./module1')
      var m4 = require('./module4')
      m1.show()
      m4.show()
      // m4.show2();
    })
    

    index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Modular Demo(CMD-seajs)</title>
      </head>
      <body>
        <script type="text/javascript" src="js/libs/sea.js"></script>
        <script type="text/javascript">
          seajs.use('./js/modules/main')
        </script>
      </body>
    </html>
    
  4. 效果:

    module1 show() djsz3y.com
    module4 show() I am djsz3y
    异步引入依赖模块3  abcd1234
    

3.3.4 AMD VS CMD

AMD VS CMD 区别,在于两个方面:

  1. 处理依赖
    • AMD 依赖前置:依赖数组,提前声明模块依赖
    • CMD 依赖就近:require 方法,动态引入
  2. 处理模块输出
    • AMD 输出:return 返回值
    • CMD 输出:赋值 module.exports

AMD

// AMD

//【1.处理依赖】——AMD 依赖前置:依赖数组,提前声明模块依赖
define(['Module1'], function (module1) {
  var result1 = module1.exec()
  return {
    result1: result1
  } //【2.处理模块输出】——AMD 输出:return 返回值
})

CMD

// CMD

define(function (requie, exports, module) {
  var module1 = require('Module1') //【1.处理依赖】——CMD 依赖就近:require方法,动态引入
  var result1 = module1.exec()
  module.exports = {
    result1: result1
  } //【2.处理模块输出】——CMD 输出:赋值 module.exports
})

3.4 ES6 模块化

3.4.1 概念

ES6 模块
(设计思想:静态化)
CommonJS 模块AMD 模块
1.何时确定
依赖关系
编译时运行时运行时
2.何时确定
输入输出
编译时运行时运行时
3.比如对象,
输入时必须查找对象属性
4.本质区别输出值引用
编译时输出接口
静态定义生成在代码静态解析阶段)。
输出值拷贝
运行时加载
加载对象:module.exports 属性,生成在脚本运行完后)。

3.4.2 基本使用

  1. 统一暴露,引用名称

    • 定义模块——export 对外暴露:统一暴露
    • 引用模块——import 对内输入:引用时,知道加载变量名、函数名
    // 1.定义 math.js
    var basicNum = 0
    var add = function (a, b) {
      return a + b
    }
    export { basicNum, add } // 1.定义模块——export 对外暴露:统一暴露
    
    // 2.引用
    import { basicNum, add } from './math' // 2.引用模块——import 对内输入:引用时,知道加载变量名、函数名
    function test(ele) {
      ele.textContent = add(99 + basicNum)
    }
    
  2. 默认暴露,自定义名称

    • 定义模块——export default 对外暴露:默认暴露
    • 引用模块——import 对内输入:引用时,自定义名称
    // 1.定义 export-default.js
    export default function () {
      console.log('foo')
    } // 1.定义模块——export default 对外暴露:默认暴露
    
    // 2.引用 import-default.js
    import customName from './export-default' // 2.引用模块——import 对内输入:引用时,自定义名称
    customName() // 'foo'
    
  3. 对 3.4.1 中本质区别举例

    【与 CommonJS 的不同】:ES6 动态引用,不会缓存值;模块内变量 绑定所属模块

    // lib.js
    export let counter = 3
    export function incCounter() {
      counter++
    }
    // main.js
    import { counter, incCounter } from './lib'
    // 【与 CommonJS 的不同】:ES6 动态引用,不会缓存值;模块内变量 绑定所属模块
    console.log(counter) // 3
    incCounter()
    console.log(counter) // 4
    

3.4.3 ES6 实现

使用 Babel : 1.ES6 编译为 ES5; 2.Browserify 编译打包 js。

  1. npm init & 定义 package.json

    package.json

    {
      "name": "es6-babel-browserify",
      "version": "1.0.0",
      "description": "ES6 实现",
      "main": "app.js",
      "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
      },
      "author": "djsz3y",
      "license": "ISC"
    }
    
  2. 安装

    1. 全局安装 babel-cli & browserify
      npm install babel-cli browserify -g

      我怕电脑卡,我局部安装 babel-cli & browserify
      npm install babel-cli browserify --save-dev

    2. 项目局部安装 babel-preset-es2015
      npm install babel-preset-es2015 --save-dev

  3. 定义 .babelrc

    .babelrc

    {
      "presets": ["es2015"]
    }
    
  4. 定义模块

    //module1.js文件
    // 分别暴露
    export function foo() {
      console.log('foo() module1')
    }
    export function bar() {
      console.log('bar() module1')
    }
    
    //module2.js文件
    // 统一暴露
    function fun1() {
      console.log('fun1() module2')
    }
    function fun2() {
      console.log('fun2() module2')
    }
    export { fun1, fun2 }
    
    //module3.js文件
    // 默认暴露 可以暴露任意数据类项,暴露什么数据,接收到就是什么数据
    export default () => {
      console.log('默认暴露')
    }
    
    // app.js文件
    import { foo, bar } from './module1'
    import { fun1, fun2 } from './module2'
    import module3 from './module3'
    // import $ from "jquery";
    foo()
    bar()
    fun1()
    fun2()
    module3()
    // $("body").css("background", "green");
    
  5. 编译

    使用 Babel : 1.ES6 编译为 ES5; 2.Browserify 编译打包 js。

    1. ES6 编译为 ES5

      1.1 全局编译: 如果前面全局安装,那么直接根目录编译: babel js/src -d js/lib

      1.2 局部编译: 由于前面项目局部安装——2.安装1.局部安装 babel-cli & browserify,所以这里——编译方法为法 Ⅰ & 法 Ⅱ

      • 法 Ⅰ:
        根目录\node_modules\.bin目录下,
        babel ../../js/src -d ../../js/lib

        XXX\es6-babel-browserify\node_modules\.bin>babel ../../js/src -d ../../js/lib
        // ..\..\js\src\app.js -> ..\..\js\lib\app.js
        // ..\..\js\src\module1.js -> ..\..\js\lib\module1.js
        // ..\..\js\src\module2.js -> ..\..\js\lib\module2.js
        // ..\..\js\src\module3.js -> ..\..\js\lib\module3.js
        
      • 法 Ⅱ:
        npx babel js/src -d js/lib2

        XXX\es6-babel-browserify>npx babel  js/src -d js/lib2
        // js\src\app.js -> js\lib2\app.js
        // js\src\module1.js -> js\lib2\module1.js
        // js\src\module2.js -> js\lib2\module2.js
        // js\src\module3.js -> js\lib2\module3.js
        
      • 对比文件一致:法 Ⅰ & 法 Ⅱ 编译结果 lib & lib2 的文件一致。

      • 法 Ⅰ & 法 Ⅱ 思路的参考链接:
        【建议改成】读完这篇你还不懂 Babel 我给你寄口罩

    2. Browserify 编译打包 js :

      2.1 全局编译: browserify js/lib/app.js -o js/lib/bundle.js

      2.2 局部编译: 同理。

      • 法 Ⅰ:
        根目录\node_modules\.bin目录下,browserify ../../js/lib/app.js -o ../../js/lib/bundle.js

        XXX\es6-babel-browserify\node_modules\.bin>browserify ../../js/lib/app.js -o ../../js/lib/bundle.js
        
      • 法 Ⅱ:
        npx browserify js/lib2/app.js -o js/lib2/bundle.js

        XXX\es6-babel-browserify>npx browserify  js/lib2/app.js -o js/lib2/bundle.js
        
  6. 引入 index.html

    index.html

    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="utf-8" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <title>Modular Demo(es6-babel-browserify)</title>
      </head>
      <body>
        <script type="text/javascript" src="js/lib/bundle.js"></script>
      </body>
    </html>
    
  7. 效果,输出

    foo() module1
    bar() module1
    fun1() module2
    fun2() module2
    默认暴露
    
  8. 引入第三方库

    1. 安装依赖npm install jquery@1

    2. app.js 文件中引入

      //...
      import $ from 'jquery'
      
      //...
      $('body').css('background', 'green')
      
    3. 编译

      • 法 Ⅰ:

        XXX\es6-babel-browserify\node_modules\.bin>babel ../../js/src -d ../../js/lib
          // ..\..\js\src\app.js -> ..\..\js\lib\app.js
          // ..\..\js\src\module1.js -> ..\..\js\lib\module1.js
          // ..\..\js\src\module2.js -> ..\..\js\lib\module2.js
          // ..\..\js\src\module3.js -> ..\..\js\lib\module3.js
        XXX\es6-babel-browserify\node_modules\.bin>browserify ../../js/lib/app.js -o ../../js/lib/bundle.js
        
      • 法 Ⅱ:

        XXX\es6-babel-browserify>npx babel  js/src -d js/lib2
        XXX\es6-babel-browserify>npx browserify  js/lib2/app.js -o js/lib2/bundle.js
        

        测试法 Ⅱ 的话,要修改 index.html :

        <script type="text/javascript" src="js/lib2/bundle.js"></script>
        

        刷新浏览器 index.html ,结果确实变绿。

  9. 目录结构:

    es6-babel-browserify

    |-js
      |-lib
        |-app.js
        |-bundle.js
        |-module1.js
        |-module2.js
        |-module3.js
      |-lib2
        |-app.js
        |-bundle.js
        |-module1.js
        |-module2.js
        |-module3.js
      |-src
        |-app.js
        |-module1.js
        |-module2.js
        |-module3.js
    |-node_modules
    |-.babelrc
    |-index.html
    |-package.json
    

友情链接