【JS基础】一文看懂模块化规范
栏目: JavaScript · 发布时间: 6年前
内容简介:前端的模块化之路经历了漫长的过程,想详细了解的小伙伴可以看浪里行舟大神写的Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。CommonJS规范加载模块是
前言
前端的模块化之路经历了漫长的过程,想详细了解的小伙伴可以看浪里行舟大神写的 前端模块化详解(完整版) ,这里根据大佬们写的文章,做了汇总和整理,希望读完的小伙伴能有些收获。
什么是模块
- 将一个复杂的程序依据一定的规则(规范)封装成几个块(文件), 并进行组合在一起
- 块的内部数据与实现是私有的,只是向外部暴露一些接口(方法)与外部其它模块通信
CommonJS
Node 应用由模块组成,采用 CommonJS 模块规范。每个文件就是一个模块,有自己的作用域。在一个文件里面定义的变量、函数、类,都是私有的,对其他文件不可见。 在服务器端,模块的加载是运行时同步加载的;在浏览器端,模块需要提前编译打包处理。
CommonJS规范加载模块是 同步 的,也就是说,只有加载完成,才能执行后面的操作。
基本语法:
- 暴露模块:
module.exports = value或exports.xxx = value - 引入模块:
require(xxx),如果是第三方模块,xxx为模块名;如果是自定义模块,xxx为模块文件路径
但是,CommonJs有一个重大的局限使得它 不适用于浏览器环境 ,那就是 require 操作是同步的。这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于”假死”状态。
因此,浏览器端的模块,不能采用”同步加载”(synchronous),只能采用”异步加载”(asynchronous),这就是AMD规范诞生的背景。
AMD
特点:非同步加载模块,允许指定回调函数, 浏览器端一般采用AMD规范
代表作:require.js
用法:
//定义没有依赖的模块
define(function(){
return 模块
})
//定义有依赖的模块
define(['module1', 'module2'], function(m1, m2){
return 模块
})
//引入使用模块
require(['module1', 'module2'], function(m1, m2){
//使用m1/m2
})
CMD
特点:专门用于浏览器端,模块的 加载是异步的 ,模块使用时才会加载执行
代表作: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()
})
CMD与AMD区别
AMD和CMD最大的区别是 对依赖模块的执行时机处理不同 ,而 不是加载的时机或者方式不同 ,二者皆为 异步加载 模块。
AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;
而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。
一句话总结:
两者都是异步加载,只是执行时机不一样。AMD是依赖前置,提前执行,CMD是依赖就近,延迟执行。
UMD
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模块化
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)
ES6 模块与 CommonJS 模块的差异
① 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
参考资料
以上是本篇文章的内容,欢迎大家提出自己的想法,我们一起学习进步,与君共勉。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- Android模块化改造以及模块化通信框架
- Laravel 模块化开发模块 – Caffienate
- 前端模块化架构设计与实现(二|模块接口设计)
- 模块化编程的实践者 disconver 更新了用户模块
- ASP.NET Core模块化前后端分离快速开发框架介绍之4、模块化实现思路
- JavaScript模块化
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Security Testing Cookbook
Paco Hope、Ben Walther / O'Reilly Media / 2008-10-24 / USD 39.99
Among the tests you perform on web applications, security testing is perhaps the most important, yet it's often the most neglected. The recipes in the Web Security Testing Cookbook demonstrate how dev......一起来看看 《Web Security Testing Cookbook》 这本书的介绍吧!
RGB HSV 转换
RGB HSV 互转工具
RGB CMYK 转换工具
RGB CMYK 互转工具