ES6之前,虽然有commonJS和AMD两种模块加载方案,但是JS在语言标准的层面上是没有模块体系的
为了解决这一问题,便引入了export import
命令来实现模块化
ES6模块化与commonJS区别:
一、
// CommonJS模块
let { state, exiets, redafile } = require('fs');
// 等同于
let _fs = require('fs');
let stat = _fs.stat;
let exists = _fs.exists;
let readfile = _fs.readfile;
commonJS require的加载方式为‘运行时加载’,即require资源之后,生成一个对象_fs,然后再去_fs中读取需要的内容,只有在运行时才能得到这个引入的对象
// ES6模块
import { stat, exists, readFile } from 'fs';
上面代码的实质是从
fs模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。
因为是编译时加载
,所以使用import命令时不能使用表达式和变量,这些只有运行时才能的得到结果的语法结构
二、
export 输出的接口,与其对应的值是动态绑定关系,即暴露的接口对应的值发生变化,import导入时是会动态更新的
commonJS模块输出的是值的缓存,不存在动态更新
export let num = 12
setTimeout(() => num = 28, 500)
export输出的变量num,值为12,500ms后,变为28
export命令
一个模块就是一个独立的文件,该文件内部的所有变量,在外部都是访问不到的,如果想要被外部访问到,那么就需要使用export命令导出
export导出变量:
export let a = 'Luffy'
export let b = 'Zero'
export let c = 'Sanji'
还可以写成这样:
let a = 'Luffy'
let b = 'Zero'
let c = 'Sanji'
export {a, b, c}
导入时使用的方法都是一样的:
import {a, b, c} from '路径'
export导出函数和类:
export class myModule {
constructor(){
...
}
}
//或
class myModule{
}
export {
myModule
}
export function add(x, y){
return x + y
}
export 为导出的变量重命名
let a = 'Jack'
let b = 18
export {
a as name,
b as age,
b as length
}
使用as 关键字 ,重新命名变量a和b的对外接口,并且变量b可以用不同的名字输出两次
export命令不能用于块级作用域中
function foo() {
export default 'bar' // SyntaxError
}
foo()
将export命令放在函数中,会报错,因为export是编译时执行的,放在块级作用域中无法做静态优化
export defaut 命令
使用import
命令时,我们需要知道需要加载的变量名和函数名,否则无法正确加载。
使用export default
命令,可以使我们为模块指定默认输出,在导入时在为其命名
//a.js 导出一个匿名函数
export default funciton (){
console.log('function')
}
//导入时可以为其随意命名,且import后面无需使用大括号
import fn from './a.js'
fn() //funciton
export default
命令用在非匿名函数前,也是可以的。
// export-default.js
export default function foo() {
console.log('foo');
}
// 或者写成
function foo() {
console.log('foo');
}
export default foo;
上面代码中,foo
函数的函数名foo
,在模块外部是无效的。加载的时候,视同匿名函数加载。
export default
本质就是导出一个叫做default
的变量, 并在语句后面为其赋值
因此:
// 正确
export var a = 1;
// 正确
var a = 1;
export default a;
// 错误
export default var a = 1;
import命令
使用export
命令定义了模块的对外接口以后,其他 JS 文件就可以通过import
命令加载这个模块
// main.js
import { name, age} from '路径';
console.log(name, age)
上面代码的import
命令,用于加载对应位置的文件,并从中输入变量。
import命令接受一对大括号,里面指定要从其他模块导入的变量名。大括号里面的变量名,必须与被导入模块(
profile.js`)对外接口的名称相同。
重命名
同样是使用as
关键字
import {name as username} from "路径"
console.log(name) // 错误
console.log(username) //正确
import载入的变量都是只读的
import载入的变量不允许修改,但是分数据类型
基本数据类型不允许修改
引用数据类型可以修改属性,但不能改变其指向,且修改过后,其他引入该变量的模块也会得到同样的结果
//基本数据类型
import {a} from '路径'
a = 12 //报错
//引用数据类型
import {a} from '路径'
a.name = 'Chopper' //可以
a = {name: 'Chopper'} //报错
路径
import路径可以是绝对路径
,也可以是相对路径
但是,如果不带有路径名,只是一个模块名
,必须通过相应配置,告知JS引擎该模块位置
变量提升
import语句存在变量提升
,即会提升到模块头部,优先执行
say()
import {say} from '路径'
上面的代码不会报错,因为import
的执行早于say
的调用。这种行为的本质是,import
命令是编译阶段执行的,在代码运行之前。
不能使用表达式和变量
由于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';
}
上面三种写法都会报错,因为它们用到了表达式、变量和if
结构。在静态分析阶段,这些语法都是没法得到值的。
加载整个模块
import命令可以加在整个模块,即用星号(*
)指定一个对象,所有输出值都加载在这个对象上面。
//a.js
export function add(x,y){
return x+y
}
export function multiply(x,y){
return x*y
}
import * as methods from './a.js'
console.log(methods.add(1,2), methods.multiply(1,2)) // 3, 2
import方法
用于export import命令是编译时执行
的,只能运行在模块顶层,不能运行在块级作用域中
因此导致无法在运行时加载模块。在语法上,条件加载就不可能实现。如果import
命令要取代 Node 的require
方法,这就形成了一个障碍。因为require
是运行时加载模块,import
命令无法取代require
的动态加载功能。
ES2020提案中 引入import()
函数,用于实现动态加载模块
import(specifier)
上面代码中,import
函数的参数specifier
,指定所要加载的模块的位置。import
命令能够接受什么参数,import()
函数就能接受什么参数,两者区别主要是后者为动态加载。
与require方法不同的是,import方法是异步
的,返回一个promise对象,require方法是同步的
适用场合
下面是import()
的一些适用场合。
(1)按需加载。
import()
可以在需要的时候,再加载某个模块。
{
path: '/login',
component: () => import('@/views/login/index'),
hidden: true
},
在我们实现路由懒加载的过程中,使用import()
命中 /login路由时,才会加载对应的模块
(2)条件加载
import()
可以放在if
代码块,根据不同的情况,加载不同的模块。
if (condition) {
import('moduleA').then(...);
} else {
import('moduleB').then(...);
}
上面代码中,如果满足条件,就加载模块 A,否则加载模块 B。
(3)动态的模块路径
import()
允许模块路径动态生成。
import(f())
.then(...);
上面代码中,根据函数f
的返回结果,加载不同的模块。
注意点
import()
加载模块成功以后,这个模块会作为一个对象
,当作then
方法的参数。因此,我们可以随意操作得到的内容
参考:阮一峰-ES6入门