模块概念
- 模块可以被理解成一个包含了js代码的一个文件,也可以被理解成一个命名空间
- 模块之间互相独立,也可以互相引用。
- 模块内的内容默认状态下都是private,但也可以通过暴露方式,将其转换成public,被其他模块访问
为什么要有模块?
- 防止全局变量被污染,无法保证不与其他模块发生变量名冲突
- 可以提高代码的复用率
模块规范有哪些?
在es6以前,js并没有关于模块的官方规范没有模块体系(连css都有@import),因此社区制定了一些非官方的模块功能: CommonJS、AMD
CommonJS
Node.js的模块规范(运行时加载
)
- 在CommonJS中,每一个模块都会有一个module对象。使用module.exports和exports暴露模块
- 在CommonJS中,有一个全局性方法require(),用于加载模块
CommonJS模块规范中的注意点
CommonJS规范加载模块是同步
-
CommonJS中每个模块是一个对象这个对象的名字就是该文件的名字
- 因此每次加载都必须将整个模块都加载进来
-
CommonJS模块的加载机制是,输入的是被输出的值的拷贝。也就是说,一旦输出一个值,模块内部的变化就影响不到这个值
//a.js let a = 3 module.exports = a a = 5 //b.js const a = require('./a.js') console.log(a) // 是3而不是5
执行时机是运行时,只能在运行时确定模块之间的依赖关系,而不能进行静态优化,因此require可以直接用作动态引用。
AMD(异步模块定义)
为浏览器环境设计,因为 CommonJS 模块系统是同步加载的(简单的总结,就是让你可以使用回调)
- 产生背景:
- 有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行
- 但是CommonJS 模块系统是同步加载的,这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于"假死"状态
由于现在有了ES6模块规范,因此不多介绍AMD了
ES6模块规范
ES6 在语言标准的层面上,实现的模块功能,旨在成为浏览器和服务器通用的模块解决方案(显然nodejs还在沿用CommonJS方案)。编译时加载
- 模块功能主要由两个命令构成:export和import
-
还包含export default,但只是export的语法糖
//a.js const a = 5 const b = 8 export a; export default b; //等价于 export { b as default} //b.js import b from 'a.js' //等价于 import {b as default} from 'a.js' import {a} from 'a.js'
-
ES6模块规范中的注意点
- ES6 在编译时就能确定模块的依赖关系.
- import命令会被 JavaScript 引擎静态分析,在编译时就引入模块代码,而不是在代码运行时加载,所以无法实现条件加载。也正因为这个,使得静态分析成为可能。
- ES6模块不是对象
- 因此通过export 命令显式指定输出的代码,而不是将某部分代码放入模块对象中
- 在import时可以指定加载某个输出值,而不是加载整个模块
- ES6模块加载(import)的是输出(export)的引用,指代的是同一个对象
- 因此export之后再次修改被export的值,import到的是修改后的值,因为是同一个引用
CommonJs和ES6模块对比
-
对于模块的加载时机不同
- CommonJS运行时加载
- ES6编译时加载
例子:
if(//条件){ import react from 'react' } if(//条件){ export const a = 3; } //以上两种情况都会报错 //'import' and 'export' may only appear at the top level if(//条件){ const React = require('react'); } if(//条件){ const a = 3; module.exports a; } //以上两种情况都不会报错
- 当你把es6的import或者export放在某个block中会出现这样的报错'import' and 'export' may only appear at the top level.
A module's structurebeing static means that you can determine imports and exports at compile time (statically) – you only have to look at the source code, you don’t have to execute it. 链接
- 使用require和module.exports是运行时因此不会报错
-
模块类型不同
- ES6不是对象
- CommonJS是一个module对象
例子:
const React = require('react'); const Component = React.Component; // commonJs必须将整个模块对象引入,然后使用`.`找出模块中的属性 import { Component } from 'react'; //但是es6可以指定加载某个输出值
-
加载的模块和暴露的模块之间关系
- CommonJS:加载的模块是暴露的模块的拷贝
- ES6:加载的模块是暴露的模块的引用,指代同一个对象
import/export 比 module.exports/require 用法更多样一些
动态引用: require和import()区别
- require同步的加载,返回加载回来的module
- import()函数返回的是一个 Promise