(1)require
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个方法。这种方法称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没有办法做“静态优化”
(2)import
语法:import 命令接受一对大括号,里面指定要从其他模块导入的变量名。
import { stat, exists, readFile } from 'fs';
上面代码的实质是从fs模块加载3个方法,其他方法不加载。这种加载称为"编译时加载"或者静态加载。
可以使用as关键字,将输入的变量重新命名
import { lastName as surname } from './profile.js';
import {a} from './xxx.js'
a = {}; // Syntax Error : 'a' is read-only;
a.foo = 'hello'; // 合法操作
import 命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面改写接口。
上面的代码中,a的属性可以改写成功,并且其他模块也可以读到改写的后的值。不过这种写法很难查错,建议凡是输入的变量,都当作完全只读,不要轻易改变它的属性。
import 命令具有提升效果,会提升到整个模块的头部,首先执行。
foo();
import { foo } from 'my_module';
上面的代码不会报错,因为import的执行早于foo的调用。这种行为的本质是,import 命令是编译阶段执行的,在代码运行前面。所以下面的代码会报错。
// 报错
import { 'f' + 'oo' } from 'my_module';
// 报错
let module = 'my_module';
import { foo } from module;
// 报错
if (x === 1) {
import { foo } from 'module1';
} else {
import { foo } from 'module2';
}
Q: 如果在业务中有需求需要根据不同条件导入,我们应该怎么做那?应该用import()函数,后面有介绍。
(3)export
export 命令后面,使用大括号指定所要输出的一组变量
var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
export { firstName, lastName, year };
也可以这样写
// profile.js
export var firstName = 'Michael';
export var lastName = 'Jackson';
export var year = 1958;
可以使用as为输出的变量重新赋值
function v1() { ... }
function v2() { ... }
export {
v1 as streamV1,
v2 as streamV2,
v2 as streamLatestVersion
};
export 语句输出的接口,与其对应的值是动态绑定关系
export var foo = 'bar';
setTimeout(() => foo = 'baz', 500);
上面的代码输出变量foo,值为bar,500ms 之后变成baz
export命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于模块作用域内,就会报错。
function foo() {
export default 'bar' // SyntaxError
}
foo()
(4)模块的整体加载
import * as circle from './circle';
console.log('圆面积:' + circle.area(4));
console.log('圆周长:' + circle.circumference(14));
注意: 模块整体加载所在的那个对象(上例是circle)应该是可以静态分析的,所以不允许运行时改变。下面的写法都是不允许的
import * as circle from './circle';
// 下面两行都是不允许的
circle.foo = 'hello';
circle.area = function () {};
(5)import()
ES2020提案 引入import()函数,支持动态加载模块。
import()函数是异步加载,require方法是同步加载。
下面是require 动态加载功能
const path = './' + fileName;
const MyModual = require(path)
个人理解:异步加载是还没加载完毕就执行后面的代码,同步加载是等加载完,再执行后面的代码
下面的是import()的一些适用场合 (1) 按需加载
button.addEventListener('click', event => {
import('./dialogBox.js')
.then(dialogBox => {
dialogBox.open();
})
.catch(error => {
/* Error handling */
})
});
上面的代码中,import()方法放在click 事件的监听函数之中,只有用户点击了按钮,才会加载这个模块。
(2) 条件加载 import() 可以放在if 代码块中,根据不同的情况,加载不同的模块
if(condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...)
}
上面的代码中,如果满足条件,就加载模块A,否则加载模块B
(3) 动态的模块路径 import()允许模块路径动态生成
import(f())
.then(...)
上面的代码中,根据函数f的返回结果,加载不同的模块。
(4) 对象的解构赋值 import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。因此可以使用对象的解构赋值的语法,获取输出接口。
import('./myModule.js)
.then(({export1, export2}) => {
})
(5) 如果模块中有default 输出接口,可以用参数直接获得。
import('./myModule')
.then(myModule => {
console.log(myModule.default)
})
(6) 如果同时加载多个模块,可以采用下面的写法。
Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module1, module2, module3]) => {
···
});
(7) import()也可以用在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();