前端的模块化之路经历了漫长的过程,想详细了解的小伙伴能够看浪里行舟大神写的前端模块化详解(完整版),这里根据几位大佬们写的文章,将模块化规范部分作了汇总和整理,但愿读完的小伙伴能有些收获,也但愿以为有用的小伙伴能够点个赞,笔芯。javascript
Node 应用由模块组成,采用 CommonJS 模块规范。每一个文件就是一个模块,有本身的做用域。在一个文件里面定义的变量、函数、类,都是私有的,对其余文件不可见。在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块须要提早编译打包处理。前端
CommonJS规范加载模块是同步的,也就是说,只有加载完成,才能执行后面的操做。java
基本语法:git
module.exports = value
或 exports.xxx = value
require(xxx)
,若是是第三方模块,xxx为模块名;若是是自定义模块,xxx为模块文件路径可是,CommonJs有一个重大的局限使得它不适用于浏览器环境,那就是require
操做是同步的。这对服务器端不是一个问题,由于全部的模块都存放在本地硬盘,能够同步加载完成,等待时间就是硬盘的读取时间。可是,对于浏览器,这倒是一个大问题,由于模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。es6
所以,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous),这就是AMD规范诞生的背景。github
特色:非同步加载模块,容许指定回调函数,浏览器端通常采用AMD规范编程
表明做:require.js浏览器
用法:缓存
//定义没有依赖的模块 define(function(){ return 模块 }) //定义有依赖的模块 define(['module1', 'module2'], function(m1, m2){ return 模块 }) //引入使用模块 require(['module1', 'module2'], function(m1, m2){ //使用m1/m2 })
特色:专门用于浏览器端,模块的加载是异步的,模块使用时才会加载执行服务器
表明做:Sea.js
用法:
//定义没有依赖的模块 define(function(require, exports, module){ exports.xxx = value module.exports = value }) //定义有依赖的模块 define(function(require, exports, module){ //引入依赖模块(同步) var module2 = require('./module2') //引入依赖模块(异步) require.async('./module3', function (m3) { }) //暴露模块 exports.xxx = value }) //引入使用模块 define(function (require) { var m1 = require('./module1') var m4 = require('./module4') m1.show() m4.show() })
AMD和CMD最大的区别是对依赖模块的执行时机处理不一样,而不是加载的时机或者方式不一样,两者皆为异步加载模块。
AMD依赖前置,js能够方便知道依赖模块是谁,当即加载;
而CMD就近依赖,须要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是不少人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到能够忽略。
一句话总结:
二者都是异步加载,只是执行时机不同。AMD是依赖前置,提早执行,CMD是依赖就近,延迟执行。
UMD是AMD和CommonJS的糅合:
AMD模块以浏览器第一的原则发展,异步加载模块。
CommonJS模块以服务器第一原则发展,选择同步加载,它的模块无需包装(unwrapped modules)。
这迫令人们又想出另外一个更通用的模式UMD (Universal Module Definition)。但愿解决跨平台的解决方案。
UMD先判断是否支持Node.js的模块(exports)是否存在,存在则使用Node.js
模块模式。
在判断是否支持AMD(define是否存在),存在则使用AMD方式加载模块。
(function (window, factory) { if (typeof exports === 'object') { module.exports = factory(); } else if (typeof define === 'function' && define.amd) { define(factory); } else { window.eventUtil = factory(); } })(this, function () { //module ... });
ES6 模块的设计思想是尽可能的静态化,使得编译时就能肯定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时肯定这些东西。好比,CommonJS 模块就是对象,输入时必须查找对象属性。
ES6 Module默认目前尚未被浏览器支持,须要使用babel,在平常写demo的时候常常会显示这个错误:
ES6模块使用import
关键字导入模块,export
关键字导出模块:
/** 导出模块的方式 **/ var a = 0; export { a }; //第一种 export const b = 1; //第二种 let c = 2; export default { c }//第三种 let d = 2; export default { d as e }//第四种,别名 /** 导入模块的方式 **/ import { a } from './a.js' //针对export导出方式,.js后缀可省略 import main from './c' //针对export default导出方式,使用时用 main.c import 'lodash' //仅仅执行lodash模块,可是不输入任何值
export {<变量>}
这种方式通常称为 命名式导出 或者 具名导出,导出的是一个变量的引用。export default
这种方式称为 默认导出 或者 匿名导出,导出的是一个值。
举例:
// a.js let x = 10 let y = 20 setTimeout(()=>{ x = 100 y = 200 },100) export { x } export default y // b.js import { x } from './a.js' import y from './a.js' setTimeout(()=>{ console.log(x,y) // 100,20 },100)
① CommonJS 模块输出的是一个值的拷贝,ES6 模块输出的是值的引用。
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。并且,CommonJS 模块不管加载多少次,都只会在第一次加载时运行一次,之后再加载,返回的都是第一次运行结果的缓存,除非手动清除系统缓存。
ES6 模块的运行机制与 CommonJS 不同,JS 引擎对脚本静态分析的时候,遇到模块加载命令import
,就会生成一个只读引用,等到脚本真正执行时,再根据这个只读引用,到被加载的那个模块里面去取值。换句话说,ES6 的import
有点像 Unix 系统的“符号链接”,原始值变了,import
加载的值也会跟着变。所以,ES6 模块是动态引用,而且不会缓存值,模块里面的变量绑定其所在的模块。
② CommonJS 模块是运行时加载,ES6 模块是编译时输出接口。
CommonJS 加载的是一个对象(即module.exports
属性),该对象只有在脚本运行完才会生成。即在输入时是先加载整个模块,生成一个对象,而后再从这个对象上面读取方法,这种加载称为“运行时加载”。
例如:
// CommonJS模块 let { stat, exists, readFile } = require('fs'); // 等同于 let _fs = require('fs'); let stat = _fs.stat; let exists = _fs.exists; let readfile = _fs.readfile;
上面代码的实质是总体加载fs模块(即加载fs的全部方法),生成一个对象(_fs),而后再从这个对象上面读取 3 个方法。由于只有运行时才能获得这个对象,致使彻底没办法在编译时作“静态优化”。
ES6 模块不是对象,它的对外接口只是一种静态定义,在代码静态解析阶段就会生成。经过export
命令显式指定输出的代码,import
时采用静态命令的形式。即在import
时能够指定加载某个输出值,而不是加载整个模块,这种加载称为“编译时加载”或者“静态加载”。
// ES6模块 import { stat, exists, readFile } from 'fs';
上面代码的实质是从fs模块加载 3 个方法,其余方法不加载。即 ES6 能够在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。固然,这也致使了无法引用 ES6 模块自己,由于它不是对象。
因为 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,好比引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。
除了静态加载带来的各类好处,ES6 模块还有如下好处:
navigator
对象的属性。Math
对象),将来这些功能能够经过模块提供。Node.js
中运行。不过,依赖SPM打包,模块的加载逻辑偏重。以上是本篇文章的内容,欢迎你们提出本身的想法,咱们一块儿学习进步,与君共勉。
前端模块化详解(完整版)
ECMAScript 6 入门
近一万字的ES6语法知识点补充
完全搞清楚javascript中的require、import和export
CommonJS,AMD,CMD,ES6,require 和 import 详解