1. ES6 Module
1.1 export
export
导出模块,导出的是对象,因此当值改变后,导出内容会跟着变,一个模块可以导出多个内容
export let val = 0
setTimeout(() => {
val = 1
}, 100)
export default
导出指定的默认输出,一个模块只允许有一个。本质是将后面跟着的值赋予default
变量再导出,因此后面只能跟值或者具有值的内容,不能跟声明语句
1.2 import
import
引入并执行模块,会在编译时执行,具有变量提升效果
由于编译时执行,不能使用表达式或变量
import { 'f' + 'oo' } from './test' // 报错
多次引入模块只会执行一次
import { foo } from './test'
import { bar} from './test'
// 等同于
import { foo, bar } from './test'
整体加载后不允许运行时修改
import * as test from './test'
test.foo = () => {} // 报错
import
无法脱离HTML运行,而require
可以
import test from './test' // 当运行单个js文件时报错
let test = require('./test') // 正常导入模块
1.3 异步加载
浏览器加载script
通常是同步的,在加载并执行过程中会停止渲染。这样会造成不好的用户体验,因此加入了异步加载
defer
遇到标签时开始下载,页面渲染结束后执行,多个defer
能保证顺序加载
async
遇到标签时开始下载,文件下载好立马执行,多个async
无法保证顺序
2. CommenJS模块
2.1 模块化问题
由于语言问题,Javascript天生不支持模块化,因此为了解决这个问题有很多方案。
浏览器中:
<script>
标签引用加载,必须考虑加载顺序问题require.js
第三方库AMDsea.js
第三方库CMD
无论是CommonJS
、AMD
、CMD
、UMD
、ES6 Modules
都是为了解决Javascript
模块化问题
ES6 Modules
是官方提供的JS模块化解决方案
Node
本身提供了模块化,并且在8.5版本后对ES6 Modules
进行了支持
2.2 模块加载
- 模块分为核心模块和文件模块,核心模块中完全由C/C++编写的称为内建模块
- 优先从缓存加载模块,缓存的是编译执行之后的对象
- 核心模块加载最快,路径形式其次,自定义模块最慢(会逐层向上的
node_modules
目录中查找) - 分析包时会首先以
package.json
文件中main
属性指定的文件名为入口,如果失败则依次查找index.js
,index.json
,index.node
- 模块内和模块外互不影响,无法访问除导出内容以外的一切内容
2.3 模块编译
- 每个文件模块都是一个对象,通过
require.extensions
上的方法对不同扩展名进行加载 - 通过在外部包装
function
传入require
,exports
,module
,__filename
,__dirname
等变量
2.4 模块导入导出
require
和exports
。require可以执行被加载模块中的代码并获取被导出的对象, exports可以导出模块中想要导出的内容exports
是形参传入,指向module.exports
,默认是空对象。Node
是exports
,ES6是export
- ES6中
export
可以出现多次且每个都需要变量名,export default
只能出现一次 Node
中实际导出的是module.exports
,而exports = xxx
的语法实际修改的是形参的引用,并不会影响module.exports
,所以会导致结果失败
2.5 npm
npm init -y // 快速生成package.json文件
npm install --global cnpm // 全局安装cnpm
npm install jquery --regisrty=https://registry.npm.taobao.org // 不安cnpm使用淘宝镜像
npm config set registry https://registry.npm.taobao.org // 将淘宝镜像配置进文件
npm config list // 查看配置文件
2.6 package.json 和package-lock.json
- npm 5 以前是没有
package-lock.json
文件的, npm 5 以后才加入 package.json
文件会储存项目的基本信息,npm install
时会以这个文件的依赖为准下载依赖包- 当安装包时,npm 会生成或者更新
package-lock.json
这个文件 package-lock.json
文件会保存node_modules
中所有包的信息(版本,下载地址),当重新npm install
时可以提升下载速度package-lock.json
文件可以锁定依赖版本,防止自动升级新版
3. ES6模块与CommenJS模块差异
CommonJS
模块输入的是一个值的复制,ES6模块输出的是值的引用CommenJS
模块是运行时加载,ES6 模块是编译时输出接口
4. 循环引用
由于实际运用中,很难避免模块之间的互相依赖,因此模块的循环加载是必须考虑的问题
4.1 CommonJS
模块a
加载时依赖模块b
,停止加载模块a
去加载模块b
,模块b
中遇到加载模块a
时,返回模块a
已加载的部分,继续加载模块b
至完成后,再返回加载模块a
扫描二维码关注公众号,回复:
10636641 查看本文章
// test.js
exports.done = false
var test2 = require('./test2')
console.log('在test中,test2.done = %j', test2.done)
exports.done = true
console.log('test执行完毕')
// test2.js
exports.done = false
var test = require('./test')
console.log('在test2中,test.done = %j', test.done)
exports.done = true
console.log('test2执行完毕')
// main.js
var test = require('./test')
var test2 = require('./test2')
console.log('执行完毕')
// 在test2中,test.done = false
// test2执行完毕
// 在test中,test2.done = true
// test执行完毕
// 执行完毕
4.2 ES6 模块
同上,依旧是模块a
加载模块b
,模块b
加载模块a
时获取模块a
的返回,执行完模块b
后再执行模块a
区别在于:
ES6
模块返回的是对象,后续的改变会影响已返回的值
CommonJS
返回的是值,后续的改变不会影响已返回的值
因此如下代码,ES6
可以执行,CommonJS
则不行
// even.js
import { odd } from './odd.js'
export let counter = 0
export function even(n) {
counter ++
return n == 0 || odd(n - 1)
}
// odd.js
import { even } from './even.js'
export function odd(n) {
return n != 0 && even(n - 1)
}
// main.js
import * as res from './even.js'
res.even(10)
console.log(res.counter) // 6