• 【转】前端模块化小总结—commonJs,AMD,CMD, ES6 对比


    随着前端快速发展,需要使用javascript处理越来越多的事情,不在局限页面的交互,项目的需求越来越多,更多的逻辑需要在前端完成,这时需要一种新的模式 --模块化编程 

    模块化的理解:模块化是一种处理复杂系统分解为更好的可管理模块的方式。简单来说就是解耦,简化开发,一个模块就是实现特定功能的文件,可以更方便地使用别人的代码,想要什么功能,就加载什么模块。模块开发需要遵循一定的规范

    CommonJS规范

    CommonJS就是一个JavaScript模块化的规范,是用在服务器端的node的模块规范,前端的webpack也是对CommonJS原生支持的。

    特点:1. 模块输出的是一个值的拷贝, 模块是运行时加载,同步加载

       2.CommonJS 模块的顶层this指向当前模块

    有两个API : 

    require : 加载所要依赖的其他模块
    module.exports 或者exports :对外暴露的接口

    示例:新建两个模块文件A.js 和 B.js

     
    A.js
       //写法一
    module.exports = {
         a:1,
         b:2
    }
        //写法二
    // module.exports.a=1;
    // module.exports.b = 2;
    

    //写法三 // exports.a=1; // exports.b = 2

    // 三种写法结果是一样,对外暴露的接口的结果是一致的
    B.js 

    console.log(require('./B.js'));//{a:1,b:2}
     

    注意

     (1). exports 与module.exports 的区别:exports 是对 module.exports 的引用,不能直接给exports 赋值,直接赋值无效,结果是一个空对象,module.exports 可以直接赋值:如示例

     
    // module.exports = 123; //123
    // module.exports = function () { //[Function] // console.log(123) // }
    // exports = 123; //{} // exports = function(){ //{} // console.log(123) // }
     

    (2). 一个文件不能写多个module.exports ,如果写多个,对外暴露的接口是最后一个module.exports

    (3). 模块如果没有指定使用module.exports 或者exports 对外暴露接口时,在其他文件就引用该模块,得到的是一个空对象{}

    AMD规范

    AMD 即 Asynchronous  Module  Definition,中文名是“异步模块定义”的意思。它是一个在浏览器端模块化开发的规范,AMD 是 RequireJS 在推广过程中对模块定义的规范化产出,所以AMD规范的实现,就是的require.js了

    特点 :异步加载,不阻塞页面的加载,能并行加载多个模块,但是不能按需加载,必须提前加载所需依赖

    Amd 的规范中定义了两个重要的api

    define(id?,[]?,callbakc): //定义声明模块,参数id 模块id标识(可选),参数二是一个数组(可选),依赖其他模块,最后是回调函数
    
    require([module],callback):// 加载模块,参数一,是数组,指定加载的模块,参数二回调函数,模块加载完成后执行

    还有一个配置属性API:

     
    require.config({
    
    baseUrl: //基本路径
    
    paths:// 对象,对外加载的模块名称  : 键值关系,键:自定义模块名称,值 :文件名或者文件路径(不要写文件后缀.js),可以是字符串,数组(如果第一个加载失败,会加载第二个)
    
    shim://对象,配置非AMD 模式的文件,每个模块要定义(1)exports:值(指在js文件暴露出来的全局变量,如:window.a)(2)deps: 数组,表明该模块的依赖性
    
    })
     

    注意:paths 的设置加载优化与shim 中配置(AMD模式优先非AMD模式)

    以下是例子,加载不同情况的(非AMD 模式,AMD 模式)的例子

    示例1 :加载AMD模式模块

     
    //首先,引入requiresJS
     //requirejs.html
       <!DOCTYPE html>
    <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
            <!--data-main :设置data-main属性,设置加载主文件路径,默认设置该目录为根目录 如:../js/index.js ,默认把 ../js 作为模块文件的根模块 -->
            <script src="../js/require.js" data-main="../js/index.js"  defer async="async" type="text/javascript"></script>
        </head>
    </html>
    
    //Math.js
    // 定义了AMD 模式的模块,并且引入了jquery 模块 (这里的jquery属于AMD模式)
    define(['jquery'],function(_){
        //define(['moudel1','moudel2'],callback())
        //如果一个模块不依赖其他模块,直接使用define()函数写,
        //如果一个模块依赖其他模块,define()第一个参数是数组
        //回调函数的参数对应第一个参数数组的值,按顺序
        console.log(_);//ƒ (a,b){return new m.fn.init(a,b)}
      return { 
            add:function(x,y){
                return x+y;
            },
        }
    })
    
    // index.js (主入口)
    //配置好加载路径
     require.config({
        baseUrl:"../js",
        paths:{    
            "index":"index", // 主入口文件 index: 
         "jquery":"jquery.min",// jquery 库,符合AMD模式(1.7 版本以上符合AMD)
            "maths":"Math", //自定义AMD 模式的模块
        }
    })
    
    //加载maths.js
       require(["maths"],function(_math){
        console.log( _math); // {add: ƒ }
    })
     

    示例2:加载非AMD 模式的模块,依赖非AMD模块 ,第三方插件(如jquery .lazyload 插件)

    这里用到了shim属性配置(加载非AMD 模式)

     
    //index.js (主入口)
    //配置好加载路径
     require.config({
        baseUrl:"../js",
        paths:{    
            "index":"index", // 主入口文件 index: 
            "jquery":"jquery.min",//jquery 库(1.7 版本以上符合AMD)
            "jquery.lazyload":[ // 非AMD 模式 依赖jquery 库的第三方插件
           "http://apps.bdimg.com/libs/jquery-lazyload/1.9.5/jquery.lazyload.min",
          "jquery.lazyload"
          ]
         //这里加载了百度静态资源cdn和本地的资源(如果第一个加载失败,会加载本地)},   shim:{ "jquery.lazyload":{ deps:["jquery"]//配置 jquery 依赖 } } })   //加载jquery 和jquery.lazyload require(["jquery","jquery.lazyload"],function($){ $("img.lazy").lazyload({effect: "fadeIn"}); })
     

    示例3   加载非AMD 模式 (闭包形式)的模块,有依赖(非AMD模式),自定义(与示例2,差不多)

    指定了config 配置的 shim 中exports属性,有返回值

     
    //util.js,
    //非闭包,暴露了两个全局变量 utils ,obj
    var utils = {};  
    var obj = {};
    utils.add = function(v1,v2){  
        return v1+v2;  
    }; 
    obj.reduce = function(x,y){
        return x-y;
    }
    //test1.js
    //非AMD 模式的 (闭包形式),一般插件写法,使用wondow 对外暴露了objTest 变量
    (function(window,$,obj,utils){
        console.log($);//ƒ (a,b){return new m.fn.init(a,b)}
        console.log(utils);//{add: ƒ}
        console.log(obj);//{reduce: ƒ}
        window.objTest = {};//window等于对外暴露接口
        
    })(window,jQuery,obj,utils)//  只能写接口$或者 jQuery(不能写AMD 的模块名称jquery,会报错undefined)
    
    //index.js (主入口)
    //配置好加载路径
     require.config({
        baseUrl:"../js",
        paths:{    
            "index":"index", // 主入口文件 index: 
            "jquery":"jquery.min",//jquery 库(1.7 版本以上符合AMD)
        },
      shim:{
          "test1":{// 非AMD 模式的(闭包形式)
                deps:["util"],//设置依赖util:实际是,加载 了 "../js/util.js" 依赖该js 中的所有全局变量
                exports:"objTest" , //在test1.js 文件中,使用了window.objTest 对外暴露了接口
            },
          "util":{ // 非AMD 模式  (非闭包)
                deps:["jquery"],//设置依赖
            }
        }
    })
    //加载 test1.js
    require(["test1"],function(_){
        console.log(_);//返回的是一个对象 {},因为在配置中设置了exports
        console.log("load finshing...")
    });
    //加载 util.js
    require(["util"],function(_){
        console.log(_);//返回的是一个undefind,没有配置exports
        console.log("load finshing...")
    });
    
    
     

     分析示例3:在 test1.js 中,闭包函数,一共传了四个值,分别是  window,jQuery,obj,utils ,

    window 就不用说了,jQuery 从哪里传来呢?index.js 主文件中的shim 中test1 模块的依赖只有util (等于加载了util.js 文件,依赖了该文件中的所有全局变量,obj 和utils); 回归jQuery 从哪里加载进来,别忘paths 的配置加载优先shim 配置,所以先加载了paths 中的jquery 模块,即使jquery 也是支持AMD ,但是也是暴露了window.jQuery 全局变量,所以后于jquery 模块的加载,无论是AMD 还是非AMD 都可以得到该全局变量jQuery

    注意 :非AMD 模块中依赖AMD 模块,是不可以直接在deps属性中设置AMD的模块名作为依赖,这样是直接报错的

     

    CMD规范

    CMD规范是阿里的玉伯提出来的,实现js库为sea.js。 它和requirejs非常类似,即一个js文件就是一个模块,但是CMD的加载方式更加优秀,是通过按需加载的方式,而不是必须在模块开始就加载所有的依赖。

    玉伯说过能够亲眼看到seajs死掉也是一种幸福(说明了什么,你懂的)

     
    seajs.config({
    //设置别名,方便调用
    alias: { 'jquery': ' http://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js' }
    });
    define(function(require, exports, module) {
        //引用jQuery模块
        var $ = require('jquery');
    });
    // 加载多个模块,在加载完成时,执行回调
    seajs.use(['./a', './b'], function(a, b) {
      a.doSomething();
      b.doSomething();
    });
     

    es6 的module规范

    ES6 在语言标准的层面上,实现了模块功能,而且非常简单,ES6到来,完全可以取代CommonJS 和 AMD规范,成为浏览器和服务器通用的模块解决方案。由vue,Angular  React这些mvvm 模式的框架发展,让前端的编程变得模块化,组件化。

    特点:1. ES6 模块之中,顶层的this指向undefined,即不应该在顶层代码使用this

       2.  自动采用严格模式"use strict"。须遵循严格模式的要求

       3. ES6 模块的设计思想是尽量的静态化,编译时加载”或者静态加载,编译时输出接口

       4. ES6 模块exportimport命令可以出现在模块的任何位置,但是必须处于模块顶层。如果处于块级作用域内,就会报错

       5.ES6 模块输出的是值的引用

    模块功能主要由两个命令构成:export 和 import

    export用于规定模块的对外接口,

    import用于输入其他模块提供的功能。

    1.export 命令

    export命令除了输出变量,还可以输出函数或类(class),不同的数据类型 

     export.js

     
    //变量
    export var m = 1; //函数 export function fn(x, y) { return x * y; }; //类class export class class1{} //export输出的变量就是本来的名字,但是可以使用as关键字重命名 function v1() { ... } function v2() { ... } export { v1 as streamV1, v2 as streamV2, v2 as streamV3 };
     

    2.import命令

    使用export命令定义了模块的对外接口以后,其他 JS 文件就可以通过import命令加载这个模块。

    main.js

     
    //静态加载,只加载export.js 文件中三个变量,其他不加载
    import {m, fn, streamV1} from './export.js';
    
    //import命令要使用as关键字,将输入的变量重命名。
    import {fn as fn1} from './export.js';
    
    //整体加载模块
    improt * as all from './export.js'
     

    3.export default 命令

    本质上,export default就是输出一个叫做default的变量或方法

     
    // export-default.js
    export default function foo() {
      console.log('foo');
    }
    
    // 或者写成
    
    function foo() {
      console.log('foo');
    }
    //foo函数的函数名foo,在模块外部是无效的。加载的时候,视同匿名函数加载 export default foo;
    //import-default.js import myfoo from './export-default.js';
     

    比较一下默认输出和正常输出

     
    // 第一组
    export default function crc32() { // 输出
      // ...
    }
    
    import crc32 from 'crc32'; // 输入
    
    // 第二组
    export function crc32() { // 输出
      // ...
    };
    
    import {crc32} from 'crc32'; // 输入
     

    分析:上面代码的两组写法,

    第一组是使用export default时,对应的import语句不需要使用大括号;

    第二组是不使用export default时,对应的import语句需要使用大括号。

     以下的写法是有效的

     
    // modules.js
    function add(x, y) {
      return x * y;
    }
    export {add as default};
    // 等同于
    // export default add;
    
    // app.js
    import { default as foo } from 'modules';
    // 等同于
    // import foo from 'modules';
     

    浏览器加载规则:

     1、浏览器加载 ES6 模块,也使用<script>标签,但是要加入type="module"属性。

    浏览器对于带有type="module"<script>,都是异步加载,不会造成堵塞浏览器,

    <script type="module" src="./foo.js"></script>

    2.ES6 模块也允许内嵌在网页中,语法行为与加载外部脚本完全一致。

     
    <script type="module">
      import utils from "./utils.js";
    
      // other code
    </script>
     

    es6模块推荐参考:http://es6.ruanyifeng.com/#docs/module

    原文: https://www.cnblogs.com/beyonds/p/8992619.html

    完结。。。谢谢

  • 相关阅读:
    1. Dubbo原理解析-Dubbo内核实现之SPI简单介绍 (转)
    经典算法问题的java实现 (二)
    经典算法问题的java实现 (一)
    Bitmap的秘密
    Java Networking: UDP DatagramSocket (翻译)
    Java字节码浅析(二)
    Sql server 浅谈用户定义表类型
    Jquery 动态生成表单 并将表单数据 批量通过Ajax插入到数据库
    ASP.NET获取上传图片的大小
    ASP.Net大文件上传组件详解
  • 原文地址:https://www.cnblogs.com/cfsxgogogo/p/10394451.html
Copyright © 2020-2023  润新知