背景
js原本是没有模块体系的,我们之前也是在一个html中引入js文件,但没有在js文件中引入另外一个文件过
es6之前 :commonjs用于服务器 AMd用于浏览器
现在es6实现来模块功能,可以取代commonjs 和 AMD 成为浏览器和服务器同通用的模块解决方案
es6模块 静态化 在编译的时候就能够确认模块的依赖关系 以及输入输出的变量 commonjs和 AMD运行时确定
commonJs模块就是对象,就算只使用其中几个方法,也需要加载这个对象中的所有方法,且只有运行时才能拿到这个对象
基础用法
es6 模块不是对象 是通过export命令指定输出的代码,再通过import命令引入
import {stat} from 'fs' 上面的代码就是从fs模块中加载1个方法
模块功能主要用两个命令构成: export 规定对外 import 输入
一个模块就是一个独立的文件,文件内部的变量 外部无法获取,需要用export关键字输出变量 就可以被外部读取了
export var firstNAme = "Michael"
export var lastNAme = "chael"
或者(推荐,一眼看清导出了哪些变量)
var firstName = "jackson"
var lastNAme = "chael"
export {firstName,lastNAme}
导出 方法
export function v1(){} 这样 外部文件也是使用v1
也可以用as关键字来重命名
export {
v1 as stream1,
v1 as streamssss1
}
export命令不能处于块级作用域内
另外,export语句输出的接口,值是动态绑定的关系,会实时更新
使用 as 取别名
import 导入 和导出一样的,怎么导出的就怎么而导入,还可以使用as取别名
import {d as dd} from '../d.js'
不允许改写接口
d = {} //read-only
在d是一个对象的情况下,改写属性是可以允许的
d.foo = 'friendly'
虽然d的属性可以改写,但由于其他模块也可以读写到改写后的值,这种写法很难查错
就算加载多次,也只会执行一次,所以呀 叶力他们还说我哼!又没什么影响。
模块的整体加载,使用* ,原来这叫整体加载 害! 不看文档就是这样不止其所以然,分不清
比如我们先在a文件中导出两个方法
export function area(radius){}
export function circum(radius){}
重点:加载
然后在b文件中使用整体加载
import * as circle from './a';
circle.area(4)
circle.circum(14)
原来如此
注意:虽然在这里使用整体加载,把两个方法放在一个对象里面,但不能用修改对象的属性修改她
circle.foo = 'helo' NO
circle.area = function(){}
这样是不允许的,因为这不是a文件中存在的对象。
export default 是es提供的一种快捷方式
正是因为export default命令其实只是输出一个叫做default的变量,所以它后面不能跟变量声明语句。
使用export default导出后要怎么导入?
- 不需要大括号
- 使用import 后面不需要加大括号,因为export Default 在一个文件里面只能使用一次,也就是导出的内容是唯一的,所以不需要加大括号
- 可以自定义名字
- 另外export default 本来就是为了导出提供便捷性,用户不需要知道源文件导出的变量名,可以随便指定名字。 import anyname from filename.
example:
.vue文件都是默认导出,比如下面这个组件,就是这样引入的
import passwordDetailDialog from '@/components/ChangePassword.vue'
居然还有复合写法,难怪我看不懂,所以一定要懂规则
export {foo} from 'my_module';
import {foo, bar} from 'my_module'; export {foo,bar} 先引入 ,后导出 为什么不是先导出后引入呢? 没有这个对象,怎么导出这个对象呢?
注意,他们只是转发而已 ,导致当前模块不能直接使用foo bar
export {} from '../'就是先引入 后导出,且本文件不可以用引入后导出的模块
模块的继承
如果在a文件中使用了export default 命令, 但在b文件中使用了export * from a.js 那么export * 会忽略掉a文件中的defalut方法。
eport defalut func import anyName from a
import 和export只能在模块的顶层,就算写在代码块中,也会先编译,导致写在代码块中没有意义,会出现句法错误,属于编译时执行 这样 就导致一个问题,美没有办法根据条件引入不同的模块。 而node 的require可以,他是运行时执行,所以import没有办法取代require的动态加载功能
import()无法替代node的require()的动态加载功能?
ES2020提案 引入import()函数,支持动态加载模块 import(specifier) specifier:要加载的模块位置 返回一个promise对象 这样就可以实现运行时执行,也就是说代码执行到这一句才会执行
import() 与Node 的require方法,主要区别是前者是异步加载,后者是同步...
import的使用场合
1.按需加载
在监听到点击事件后
button.addEventListener('click',event => {
import('./dialogBox.js').then(dialogBox=>{dialogBox.open();}).catch(error=>{})
})
- 条件加载
if(){import('').then}
- 动态的模块路径
import (f()).then();
注意:
import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数,因此可以使用对象解构赋值的方式获取输出接口。
import('./myModule.js').then(({export1,export2})=>{
})
`export1`和`export2`都是`myModule.js`的输出接口
如果模块又defalut输出接口,可以用参数直接获得
import('./moModule.js').then(myModule=>{
console.log(myModule.default);
})
也可以使用具名输入的方式
import('./mym').then(({default:theDefault})=>{
})
如果想同时加载多个模块
Promise.all([
import('./module.js'),
import('./module2.js'),
import('./module3.js'),
])
.then(([module,module2,module3])=>{
...
})
也可以用在async函数中
async function main(){
const myModule = await import('./myModule.js');
const {export1,export2} = await import('./x.js');
const [module1,module2,module3] =
await Promise.all([
import('./module1.js'),
import('./module2.js'),
import('./module3.js'),
]);
}
main()