模块
ES6模块的设计思想是尽量静态化。使得编译时就能确定模块质检的依赖关系,以及输入和输出的变量,commonJS和AMD模块都只能在运行时确定这些东西
// CommonJS模块
let { stat, exists, readFile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
1.运行时加载
整体加载fs模块,生成一个对象_fs 然后再从这个对象上面独取3个方法,这种加载称之为“运行时加载”,因为只有在运行时才能拿到这个对象,所以没办法在编译时做静态优化
// ES6模块
import { stat, exists, readFile } from 'fs';
2编译时加载
ES6模块不是对象,而是通过export 命令显式指定输出的代码,再通过import命令输入。上面代码实质是从fs模块加载3个方法,其他方法不加载,这种加载称为“编译时加载”,在编译时就完成加载,效率比CommonJS模块的加载方式高。
注 ES6模块中顶层 this
指向 undefined
export
一个模块,就是一个独立的文件,该文件内部所有的变量外部无法获取,如果你希望外部能够访问独取模块内部的某个变量,就需要用到export关键字输出该变量
export单个输出接口
export var a = 1
export function(x,y) {
return x+y
}
export var name = "dada"
export可以输出一组变量
var a = 1
var name = "dada"
export {a, name}
通常情况,export输出的变量就是本来的名字,但是可以使用as关键字重命名
function v1(){]
function v2(){}
export {
v1 as name1,
v2 as name2,
v2 as name3
}
export命令规定的是对外的接口必须与模块内部的变量建立一一对应的关系
export 1
// 报错
var m = 1
export m
// 报错
上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量m,还是直接输出 1。1只是一个值,不是接口。正确的写法是下面这样。
// 写法一
export var m = 1;
// 写法二
var m = 1;
export {m};
// 写法三
var n = 1;
export {n as m};
同样的,function和class的输出,也必须遵守这样的写法。
// error
function f(){}
export f
// true
export function f(){]
// true
function f(){}
export {f}
import
使用export命令定义了模块对外的接口以后,其他js文件就可以通过import命令加载这个模块
import {a, b} from './test.js'
console.log(a)
大括号里面的变量名,必须与被导入模块test.js对外接口的名称相同
如果你想为输入变量重新命名,import命令要使用as关键字。
import {a as newname} from "./test.js"
import命令输入的变量都是只读的不允许在加载模块的脚本里改写接口
import {a} from './test.js'
a = {} // error
如果a是一个对象。改写a的属性是允许的
import {a} from './test.js'
a.name = 'dda' // 合法操作
import后面的from指定模块的路径。可以是相对路径也可以是绝对路径,.js后缀可以省略。如果只是模块名,不带有路径,则必须有配置文件。告诉JavaScript引擎该模块的位置
import {util} from 'util' // 必须配置util怎么取
import命令具有提升效果,会提升到整个模块的头部,首先执行
foo()
import {foo} from './module'
import会执行所加载的模块
import 'lodash
仅仅执行lodash模块,但是不会输入任何值
如果多次重复执行同一句import,那么只会执行一次
模块的整体加载
import * as foo from './circle'
console.log(foo.area)
export default
export default 为模块指定默认输出 一个模块只能有一个export default
// export-default.js
export default function () {
console.log('foo');
}
// import-default.js
import customName from './export-default'; // 为模块指定任意名字
customName(); // 'foo'
使用export default 默认输出在import的时候不需要大括号,变量名也可以自定义,
本质上export default 就是输出一个叫做default的变量或方法,然后系统允许你为他取任意名字
所以一下写法有效
// modules.js
function add(x, y) {
return x * y;
}
export {add as default};
// 等同于
// export default add;
// app.js
import { default as foo } from 'modules';
// 等同于
// import foo from 'modules';
因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
// 正确
export var a = 1
// 正确
var a = 1
export default a
export default 32
// 报错
export default var a = 1
export default a 的就是将变量a赋值给变量default 所以最后一种写法报错
如果你想在一个import语句中同时输入默认方法和其他接口
import foo {a,b] from './test'
export default 可以用来输出类
// main.js
export default class{}
// app.js
import MyClass from './mian.js'
let p = new MyClass ()
import()
import 命令会被JavaScript引擎静态分析,先于模块内的其他语句执行,所以以下代码会报错
if(x === 1) {
import module from './mymodule'
}
引擎处理import语句时实在编译时,这时不会去分析或执行if语句,所以import语句放在if代码块中毫无意义,因此会报语法错误,所以import export 命令只能在模块的顶层,这样的设计固然有利于编译器提高效率,但是也导致无法动态加载模块,如果import命令要取代node的require方法,这就形成了一个障碍,因为require是运行时加载,import是运行时加载,import命令无法取代require的动态加载功能,
因此import()
出现了。
import()
返回一个Promise对象
const main = document.querySelector('main');
import(`./section-modules/${someVariable}.js`)
.then(module => {
module.loadPageInto(main);
})
.catch(err => {
main.textContent = err.message;
});
import()函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,import()函数与所加载的模块没有静态连接关系,这点也是与import语句不相同。import()类似于 Node 的require方法,区别主要是前者是异步加载,后者是同步加载。
- 按需加载
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
2.条件加载
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
3.动态的模块路径
import(foo()).then() // 根据foo()函数返回结果,加载不同模块
import()
加载模块成功之后,这个模块会作为一个对象,当做then方法的参数。因此可以使用对象的结构赋值获取输出接口
import('./modules').then(({moduleA,moduleB}) => {
// ...
})
如果模块有default 可以直接用参数获得
import('./modules').then((module) => {
// ...
})
加载多个模块
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});
用在async函数之中
async function main() {
const myModule = await import('./myModule.js');
const {export1, export2} = await import('./myModule.js');
const [module1, module2, module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main();
参考:http://es6.ruanyifeng.com/#README