参考探索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 引擎静态分析,先于模块内的其他语句执行,这种设计有利于编译器提高效率,但也导致无法在运行时加载模块(动态加载)。
作者:海娩
链接:https://www.jianshu.com/p/2d4a8d48b54b