最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • JavaScript 主流模块(module)规范介绍

    正文概述 掘金(YanniLi)   2020-12-08   348

    主流模块规范

    目前主流模块规范有:

    规范名称运行环境实现加载方式
    AMD(异步模块定义)客户端require.js异步CMD(通用模块定义)客户端sea.js异步CommonJS服务端NodeJS同步(动态加载)es6客户端es6静态加载

    AMD和CMD(ES5)

    AMD 和 CMD 加载多个文件时都是异步加载

    区别:

    1. AMD推崇依赖前置,在定义模块的时候就要声明其依赖的模块
    2. CMD推崇就近依赖,只有在用到某个模块的时候再去require
    • AMD 模块规范
    • CMD 模块规范

    AMD

    required.js 是 AMD 的实现

    使用:define(id?, dependencies?, factory);;

    // 定义模块 myModule.js
    define(['dependency'], function(){
        var name = 'Byron';
        function printName(){
            console.log(name);
        }
    
        return {
            printName: printName
        };
    });
    
    // 加载模块
    require(['myModule'], function (my){
      my.printName();
    });
    

    CMD

    sea.js 是 CMD 的实现

    CMD 中一个模块就是一个文件

    使用:define(id?, dependencies?, factory);;

    // 定义模块  myModule.js
    define(function(require, exports, module) {
      var $ = require('jquery.js')
      $('div').addClass('active');
    });
    
    // 加载模块
    seajs.use(['myModule.js'], function(my){
    
    });
    

    CommonJS(ES5)

    CommonJS 模块就是对象,输入时必须查找对象属性。

    • CommonJS 的加载称为“运行时加载”或者动态加载
    • 一个文件就是一个模块,每个模块都是单独的作用域

    特点:

    • 所有代码都运行在模块作用域,不会污染全局作用域。
    • 模块可以多次加载,但是只在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
    • 模块加载的顺序,按照其在代码中出现的顺序。

    module.exports

    每个模块内部,都有一个module对象,代表当前模块。它有以下属性。

    • module.id 模块的识别符,通常是带有绝对路径的模块文件名。
    • module.filename 模块的文件名,带有绝对路径。
    • module.loaded 返回一个布尔值,表示模块是否已经完成加载。
    • module.parent 返回一个对象,表示调用该模块的模块。
    • module.children 返回一个数组,表示该模块要用到的其他模块。
    • module.exports 表示模块对外输出的值
    module.exports = 123
    

    exports

    为了方便,Node为每个模块提供一个 exports 变量,指向 module.exports

    注意:

    • exports 变量直接指向一个单一值。单一值只能使用 module.exports 输出
    // 相当于每个模块顶部都有这个声明
    var exports = module.exports;
    
    // 可以给导出的对象添加属性
    exports.area = function (r) {
      return Math.PI * r * r;
    };
    
    // 不可以导出一个单一值
    exports = 123; // 无效
    

    require

    require 命令用于加载模块文件,返回该模块的 exports 对象

    commonJS规范

    Module(ES6)

    ES6 模块不是对象,而是通过 export 命令显式指定输出的代码,再通过 import 命令输入。

    • ES6 的模块是“编译时加载”或者静态加载
    • ES6 使用基于文件的模块,也就是说一个文件一个模块
    • ES6 模块是单例。也就是说,模块只有一个实例,其中维护了它的状态。每次向其他模块导入这个模块的时候,得到的是对单个中心实例的引用。
    • ES6 模块的API是静态的。导出后的API是只读状态
    • 模块的公开 API 中暴露的属性和方法并不仅仅是普通的值或引用的赋值。它们是到内部模块定义中的标识符的实际绑定(几乎类似于指针)。
    • ES6 的模块自动采用严格模式

    优点:

    1. 编译时加载方法,效率高
    2. 不再需要对象作为命名空间了

    缺点:

    1. es6模块不是对象,所以无法引用模块本身

    export

    通过 export 命令规定模块的对外接口。有两种模式:

    • 命名导出(可以多个):export **
    • 默认导出(只有一个):export default
    // 导出单个
    export let name1 = 1;
    
    // 导出多个
    export { name1, name2, ...};
    
    // 重命名导出
    export {
      name1 as master
    }
    
    // 解构导出并重命名
    export const { name1, name2: bar } = o;
    
    // 默认导出
    export default expression;
    export { name1 as default, … };
    
    // 聚合模块 - 输入后立马输出
    export * from …; // does not set the default export
    export * as name1 from …;
    export { name1, name2, …, nameN } from …;
    export { import1 as name1, import2 as name2, …, nameN } from …;
    export { default } from …;
    

    import

    通过 import 命令加载模块

    • import 是静态的,在编译时就加载
    • import 命令会提升到顶部

    使用:

    • 可导入整个模块内容:使用 * 指定一个对象,所有输出值都加载在这个对象上
    • 可导入单个接口:使用大括号包裹需引入值
    • 可导入多个接口:使用大括号包裹需引入值,多个值用逗号分隔
    • 可导入有别名的接口:使用 as 可以设置别名
    • 执行加载模块:直接 import 某个模块,会执行,但不会输入任何值
    • 导入默认值:不需要大括号,直接指定任意值。对应模块内的 export default
    // 执行 lodash 模块,但不输入任何值
    import 'lodash';
    
    // 非默认导出需有大括号。可以使用 as 设置别名
    import { firstName as fn, persion } from './profile.js';
    
    // export default 默认导出不需要括号
    import class from './class.js';
    
    // 变量不允许改写
    fn = 123; // Syntax Error : 'firstName' is read-only;
    // 对象属性可以改写 - 不建议
    persion.age = 18;
    

    动态import

    import() 可以实现动态加载

    • import() 可以像调用函数一样动态导入模块,并返回一个promise
    • import() 支持 await 关键字
    • import() 可以在commonJS 模块中运行

    使用场景:

    • 不需要马上使用的模块(可用于异步加载,提高性能)
    • 需要根据场景判断才引入的模块
    import('/modules/my-module.js')
    .then((module) => {
      // Do something with the module.
    });
    
    async getModule() {
      if (true) {
        let module = await import('/modules/my-module.js');
      }
    }
    

    循环引用/循环加载

    "循环加载"(circular dependency)指的是,a脚本的执行依赖b脚本,而b脚本的执行又依赖a脚本。

    // a.js
    var b = require('b');
    
    // b.js
    var a = require('a');
    

    通常,"循环加载"表示存在强耦合,如果处理不好,还可能导致递归加载,使得程序无法执行。

    commonJS

    commonJS 是动态加载,执行一次就会缓存结果。如果出现某个模块被"循环加载",返回的是当前已经执行的部分的值,而不是代码全部执行后的值。所以,输入变量时需要非常小心。

    a.js

    exports.done = false;
    var b = require('./b.js');
    console.log('在 a.js 之中,b.done = %j', b.done);
    exports.done = true;
    console.log('a.js 执行完毕');
    

    b.js

    exports.done = false;
    var a = require('./a.js');
    console.log('在 b.js 之中,a.done = %j', a.done);
    exports.done = true;
    console.log('b.js 执行完毕');
    

    main.js

    var a = require('./a.js');
    var b = require('./b.js');
    console.log('在 main.js 之中, a.done=%j, b.done=%j', a.done, b.done);
    

    执行main.js

    node main.js
    
    在 b.js 之中,a.done = false
    b.js 执行完毕
    在 a.js 之中,b.done = true
    a.js 执行完毕
    在 main.js 之中, a.done=true, b.done=true
    
    

    ES6 模块

    ES6 是静态加载,不会缓存结果。 它只是生成一个指向被加载模块的引用,每次都会根据引用动态去加载模块取值。

    // a.mjs
    import {bar} from './b';
    console.log('a.mjs');
    console.log(bar);
    export let foo = 'foo';
    
    // b.mjs
    import {foo} from './a';
    console.log('b.mjs');
    console.log(foo);
    export let bar = 'bar';
    

    执行

    node --experimental-modules a.mjs
    b.mjs
    ReferenceError: foo is not defined -> 原因: foo未定义。
    

    解决:将foo写成函数,使它产生函数提升。函数表达式不行,因为它不能提升

    // a.mjs
    import {bar} from './b';
    console.log('a.mjs');
    console.log(bar());
    function foo() { return 'foo' }
    export {foo};
    
    // b.mjs
    import {foo} from './a';
    console.log('b.mjs');
    console.log(foo());
    function bar() { return 'bar' }
    export {bar};
    

    执行

    $ node --experimental-modules a.mjs
    b.mjs
    foo
    a.mjs
    bar
    

    例子2:

    // even.js
    import { odd } from './odd'
    export var counter = 0;
    export function even(n) {
      counter++;
      return n === 0 || odd(n - 1);
    }
    
    // odd.js
    import { even } from './even';
    export function odd(n) {
      return n !== 0 && even(n - 1);
    }
    

    执行

    $ babel-node
    > import * as m from './even.js';
    > m.even(10);
    true
    > m.counter
    6
    > m.even(20)
    true
    > m.counter
    17
    

    m.even(20) 执行的时候,此时 counter 已经等于 6,加上自己执行的 11 次,所以一共是 17 次


    起源地下载网 » JavaScript 主流模块(module)规范介绍

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元