JavaScript模块化

作者: 海娩 | 来源:发表于2018-07-15 22:52 被阅读16次

    参考探索js的模块化

    什么是JavaScript模块化?

    模块化在我看来,就是把一些公共的函数封装起来给其他地方调用,而不用重复去写一些冗余的函数代码。

    JavaScript模块化大致发展过程

    CommonJS(服务端) => AMD (浏览器端) => CMD / UMD => ES Module

    CommonJS

    CommonJS主要用于服务器端。 这个规范是同步的

    特点:
    • 模块可以多次加载,首次加载的结果将会被缓存,想让模块重新运行需要清除缓存。

    • 模块的加载是一项阻塞操作,也就是同步加载。

        // a.js
        module.exports = {
          moduleFunc: function() {
            return true;
          };
        }
        // 或
        exports.moduleFunc = function() {
          return true;
        };
      
        // 在 b.js 中引用
        var moduleA = require('a.js');
        // 或
        var moduleFunc = require('a.js').moduleFunc;
      
        console.log(moduleA.moduleFunc());
        console.log(moduleFunc())
      

    AMD规范

    在commonJS中, 模块的加载过程是一个同步的过程, 很明显地,如果在浏览器端, 肯定就会引起浏览器页面的阻塞,因此,这时候对模块异步加载的需求就出现了。从而出现AMD规范

    异步模块定义规范(AMD)制定了定义模块的规则,这样模块和模块的依赖可以被异步加载。这和浏览器的异步加载模块的环境刚好适应(浏览器同步加载模块会导致性能、可用性、调试和跨域访问等问题)

    这时候RequireJS应运而生

    RequireJS

    特点:
    • 前置依赖,异步加载

    • 便于管理模块之间的依赖性,有利于代码的编写和维护。

        // a.js
        define(function (require, exports, module) {
          console.log('a.js');
          exports.name = 'Jack';
        });
      
        // b.js
        define(function (require, exports, module) {
          console.log('b.js');
          exports.desc = 'Hello World';
        });
      
        // main.js
        require(['a', 'b'], function (moduleA, moduleB) {
          console.log('main.js');
          console.log(moduleA.name + ', ' + moduleB.desc);
        });
      
        // 执行顺序:
        // a.js
        // b.js
        // main.js
      

    然而, 他也有他的不足之处
    按照 AMD 的规范,在定义模块的时候需要把所有依赖模块都罗列一遍(前置依赖),而且在使用时还需要在 factory 中作为形参传进去。

    define(['a', 'b', 'c', 'd', 'e', 'f', 'g'], function(a, b, c, d, e, f, g){ ..... });
    

    是不是看起来又丑又复杂。。

    RequireJS 模块化的顺序是这样的:模块预加载 => 全部模块预执行 => 主逻辑中调用模块,所以实质是依赖加载完成后还会预先一一将模块执行一遍,这种方式会使得程序效率有点低。

    这个时候就出现了CMD规范,典型的就是seajs模块化

    SeaJS

    SeaJS 模块化的顺序是这样的:模块预加载 => 主逻辑调用模块前才执行模块中的代码,通过依赖的延迟执行,很好解决了 RequireJS 被诟病的缺点。

        // a.js
        define(function (require, exports, module) {
          console.log('a.js');
          exports.name = 'Jack';
        });
    
        // main.js
        define(function (require, exports, module) {
          console.log('main.js');
          var moduleA = require('a');
          console.log(moduleA.name);
        });
    
        // 执行顺序
        // main.js
        // a.js
    

    ES6的module

    ES Module 的思想是尽量的静态化,即在编译时就确定所有模块的依赖关系,以及输入和输出的变量,和 CommonJS 和 AMD/CMD 这些标准不同的是,它们都是在运行时才能确定需要依赖哪一些模块并且执行它。ES Module 使得静态分析成为可能

    • 模块功能主要由两个命令构成:export 和 import。export 命令用于规定模块的对外接口,import 命令用于输入其他模块提供的功能。

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

        模块的定义
        /**
         * export 只支持对象形式导出,不支持值的导出,export default 命令用于指定模块的默认输出,
         * 只支持值导出,但是只能指定一个,本质上它就是输出一个叫做default 的变量或方法
         */
        // 写法 1
        export var m = 1;
        // 写法 2
        var m = 1;
        export { m };
        // 写法 3
        var n = 1;
        export { n as m };
        // 写法 4
        var n = 1;
        export default n;
      
       模块的引入
        // 解构引入
        import { firstName, lastName, year } from 'a-module';
        // 为输入的变量重新命名
        import { lastName as surname } from 'a-module';
        // 引出模块对象(引入所有)
        import * as ModuleA from 'a-module';
      

    在使用 ES Module 值得注意的是:import 和 export 命令只能在模块的顶层,在代码块中将会报错
    这是因为 ES Module 需要在编译时期进行模块静态优化,import 和 export 命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行,这种设计有利于编译器提高效率,但也导致无法在运行时加载模块(动态加载)。

    相关文章

      网友评论

        本文标题:JavaScript模块化

        本文链接:https://www.haomeiwen.com/subject/fyawyftx.html