了解es6模块化,看这篇就够了
ES Module
es module 是es6近几年最新出的一个模块化规范。相对于AMD规范,它有了更加严谨的语法标准,使用更加便捷,并且现在大多浏览器都已经支持该特性。
ES 的基本特性
script标签中使用ES
我们只需要给script标签加上一个属性type=module
,那么该script就是一个es模块了。而实际上每个js文件也是一个es模块
<script type="module">
// 这就是一个es模块
</script>
自动采用严格模式
对于每个es模块,模块内部会自动采用严格模式,最直观的表现就是,非严格模式下的this指向的是window,严格模式下则this则是undefined
<script>
console.log(this)
</script>
<script type="module">
console.log(this)
</script>
结果
独立的私有作用域
普通的script标签都是全局作用域的,而每个es模块都有独立的私有作用域,很好的解决了命名冲突问题
<script type="module">
const a = '111'
console.log(a)
</script>
<script type="module">
const a = '222'
console.log(a)
</script>
结果
ESM通过CORS去请求外部JS模块
这个的意思是,es模块是通过跨域的方式请求外部js模块,所以请求的该js模块必须是支持跨域的,否则会报错
<script type="module" scr="请求的模块路径必须是支持跨域的"></script>
ESM的script脚本会延迟执行
ESM的script脚本会相对与普通的script脚本会延迟执行
<script type="module">
console.log('我是ESM script')
</script>
<script>
console.log('我是普通script')
</script>
执行结果
我们可以看到,虽然es模块的script标签在普通标签前执行,但却是普通标签的内容先打印。这说明esm的script脚本执行比普通的script脚本慢
ES 导入和导出
我们说过,es模块内部都是由私有作用域的,那么如何暴露变量和导入,主要是通过export
来暴露变量,import
导入变量
export
export <成员声明变量>
export 可以导出成员声明变量,函数,类
// 导出变量,变量可以是任意类型
export const name = 'es module'
// 导出方法
export function foo() {}
// 导出类
export class Person{}
误区
以下这几种导出是错误的,不被支持,export不支持直接导出变量和值
const name = 'aaa'
export name // 导出变量
export 'aaa' // 导出值
export { 变量 }
export还支持导出多个变量,而且在开发的时候用这样方法其实更加的合理,易于观察
const name = 'es module'
function foo () {}
class Person {}
export { name, foo, Person }
这里需要特别注意的是export { name, foo, Person }
中的{ }
并不是对象,他是export导出变量的一种语法,和function() {}
中的{ }
是类似的,而且我们上面也说过,export无法直接导出值,因此下面这段代码也是错误的,因为它并不是一个对象,而是一种语法规范,它规定我们就是这样写的
export {
name: name,
foo: foo,
Person: Person
}
as关键字
如果我们希望对导出的变量名称进行修改,则可以使用as关键字来进行修饰变量 as 别名
,但这个方法只适用于export {}
const name = 'es module'
export { name as alias }
default关键字
顾名思义,default代表了一个默认导出的变量,export default可以导出任意的变量或者值
const name = 'es module'
export default name
or
export default { name } // 这里的{},是一个对象
or
export {name as default} // 这样也可以默认导出name
和export{}
不同,export default {}
中的{}
是一个对象,因为export default
可以导出任意的变量或者值,所以export default {}
导出的就是一个对象,并且使用default
关键字后,导入的规则也有所不同,这个后面会讲到。
import
既然有导出,那么久肯定有导入,否则无法去接收导出的变量,常规语法import {} form 'path'
基础用法
对于export导出一样,import同样有特殊的规定,这里我们针对普通的导出,非export default
// 对于普通的导出,非export default
import {name} from './modules.js'
console.log(name)
需要注意的是,和export {}
一样,import {} form 'path'
中的{ }
并不是我们熟悉的解构赋值语法,而是import的一种导入变量的规定规范,这里我们需要区分开来,并且{ }
内的变量名称需要和export导出的变量名称相同,否则无法找到对应变量
导入defualt变量
之前说过,普通的export变量和export default变量,在import导入的时候方式稍有不同,export default不需要{ }
,且可以使用任意变量名称来接收变量
// modules.js
export default 'es module'
import name from './modules.js'
console.log(name)
or
import {default as name} './modules.js'
// log: es module
as关键字
和export一样,import也有as关键字,它也是定义变量的别名
import { name as alias} from './modules.js'
// 也可以接收export default的变量
import { default as alias} from './modules.js'
*关键字
我们有时候需要导入模块内所有的成员变量,但使用常规的方式一个个导入容易出错,而且不方便,这个时候可以使用*
关键字
import {a, b, c, d, e} from './modules.js'
// or
import * as param from './modules.js'
console.log(param)
// { ... } param是一个包含所有成员的对象
使用*
关键字接收变量的时候一定需要配合as
关键字来指定接收所有成员的变量,且最终param
接收的结果是一个对象
import导入的变量的特性
使用import {}
导入的变量并不是将值拷贝一份,而是指向其变量的地址,所以模块内导出的值发生变化,导入时的变量也会发生变化,且该变量不可被修改(这里的修改是指重新赋值的意思)
- 先定义name的值,并导出,1s后修改name的值
// a.js
let name = 'befor'
// 1s后修改模块内的值
setTimeout(() => {
name = 'after'
}, 1000)
export { name }
- 观察导入的name两次打印的结果,并在延时结束后修改name的值
import { name } from './a.js'
console.log(name)
// 2s 后看nam是否修改
setTimeout(() => {
console.log(name)
name = 'end' // 重新赋值
}, 2000)
- 打印结果
可以看到,主文件的name会随着模块内变量的改变而改变,并且无法被修改
接收普通变量和default变量
有可能模块中存在export导出的普通成员变量和default变量的情况,而且在两者导入的方式有差异,那如何在主模块中导入呢?
// 默认值在前,普通变量在后,用逗号隔开,语法可以正常使用
import defaultParam, {normalParam ...} from './modules.js'
// of
import defaultParam, * as nomalParam from './modules.js'
import路径问题
import 导入的路径可以是绝对路径、相对路径和完整的链接,但不可以省略./
和后缀
动态import
import {} from 'path'
中的path不支持变量,且只能定义在开头,如果我们希望使用动态导入则需要使用import('path')
方法
// import()返回一个promise,then方法回调接收导入的参数
import('path').then((param) => {
// param 是导入的成员变量
})
以上内容仅供学习总结和参考,如有疑问欢迎交流