文章目录
- 4.模块化标准规范(ES Modules)
- 4.1 如何使用ES6规范
- 4.2 严格模式
- 4.3 实际使用
- 4.4 默认导出
- 4.5 <scrpit>加载文件的顺序
- 参考文章
4.模块化标准规范(ES Modules)
ES6 提供的模块化方案叫做 ES Module,简称 esm
,现在我开始接触的基本就是这种规范了,写nodejs的时候,偶尔有些第三方框架还是使用的commonJS
规范
4.1 如何使用ES6规范
浏览器环境
正常情况下,我们在html中直接可以使用
script
标签引入我们的js文件
<script src="./main.js"></script><!-- <script type="application/javascript" src="./main.js"></script> -->
script 标签有一个 type 属性,默认情况为:
application/javascript
。所以大多数情况都简写了。但是在 ES Module 中,为了告诉浏览器我们是用的 ES Module,需要修改 type属性为module
。
<script src="./main.js" type="module"></script>
NodeJs环境
方式一:
.mjs
,就是将js文件的后缀名改成.mjs
方式二: 配置项目的package.json
中的type
属性为module
注意
:
-
NodeJs的8.9之后的版本,就开始支持 ES6了 ,但是在 13.2 版本之后才开启 默认支持运行 ES Module。所以使用版本在 8.9~13.2 之间的 NodeJs ,执行 ES Module 的 js文件。需要添加配置项 --experimental-modules,开启 默认关闭的 ES Module 支持。 -
上面是我在文章中看到的,我的理解是只要node版本在13.2以上,不按照上面2种方式也能使用
ES 6
标准,但是实际操作不行,我的版本是16.18.0
,后面还是使用方式二npm init
4.2 严格模式
Es Module 第一个特点:默认开启了严格模式。即便你没有在文件的开头添加
use strict
主要有以下限制:
- 变量必须声明后再使用
- 函数的参数不能有同名属性,否则报错
- 不能使用 with 语句
- 不能对只读属性赋值,否则报错
- 不能使用前缀 0 表示八进制数,否则报错
- 不能删除不可删除的属性,否则报错
- 不能删除变量 delete prop,会报错,只能删除属性 delete global[prop]
- eval 不会在它的外层作用域引入变量
- eval 和 arguments 不能被重新赋值
- arguments 不会自动反映函数参数的变化,
这个还不理解
- 不能使用 arguments.callee
- 不能使用 arguments.caller
- 禁止 this 指向全局对象
- 不能使用 fn.caller 和 fn.arguments 获取函数调用的堆栈
- 增加了保留字(比如 protected、static 和 interface)
4.3 实际使用
// mian.js
// 可以用*号全部导入 并起别名
import * as student from './student.js'var studentScore = student.score();
student.skills[0]().then((message) => console.log(message));
console.log(studentScore)
// student.js
// 在没有别的处理的情况下 需要加后缀 .js
// 具体跟搜索有关 后期需要再了解
import { make_food } from "./cook.js";var maths = 80;
var chinese = 90;
var skills = [make_food];
function score() {return maths + chinese;
}
// 批量导出
export { skills, score }
// cook.js
var name = "煮饭";
// 单独导出
export function make_food() {return new Promise(function (resolve, reject) {setTimeout(() => {resolve("煮好饭了")}, 1000);})
}
package.json
{"name": "es6model","version": "1.0.0","description": "","main": "main.js","type": "module","scripts": {"test": "node ./main.js"},"author": "","license": "ISC"
}
注意事项
- import 的变量是只读的
- import 的变量实际上是对原本模块中变量的引用,
也就是说改变原模块变量值,引入的变量值也会改变
- 和 export 同理, import 语句不允许放在块级作用域中使用,会直接报错;
- 由于ES Module是静态编译,所以 import会被提升到最顶部执行 ;
- import 的执行逻辑 优先深度遍历,先子后父,
也就是说,会先将一个文件import执行到底,我就不写了,借用博主的代码了
,交叉引用的情况也要也要先把子模块全部加载完先- 多次重复执行同一句 import 语句,只会执行一次;
// 第五条的测试
// a.mjs
console.log('a开始执行啦')
// 按理来说上面是要先执行 但是 import会提升
import { say } from './b.mjs'
import { edit } from './c.mjs'
export var a = 1
console.log('a结束了')// b.mjs
console.log('b开始执行啦')
export function say() {console.log('开始说话')
}
import { a } from './a.mjs'
// b中又引用到a 但是这个时候b都还没加载完,所以a模块肯定还没加载完 那么a变量就是个undefined
console.log(a, '----我在这里输出的')
console.log('b结束了')// c.mjs
console.log('c开始执行啦')
export function edit() {console.log('开始编辑')
}
console.log('c结束了')// main.mjs
import { a } from './a.mjs'// b开始执行啦
// undefined ----我在这里输出的
// b结束了
// c开始执行啦
// c结束了
// a开始执行啦
// a结束了
4.4 默认导出
为什么
export default
不使用 var 声明一个变量导出呢?
原因是export default
可以看做就是输出一个叫做default
的变量或方法,然后系统允许你为它取任意名字。
/* 模拟 default*/
var a = 1
export {a as default}
4.5 加载文件的顺序
- 默认情况
默认情况下,浏览器是
同步加载 JavaScript
脚本,即渲染引擎遇到<script>
标签就会停下来,等到执行完脚本,再继续向下渲染。如果是外部脚本,还必须加入脚本下载的时间。
2.异步加载
添加属性,defer 或者 async,都会开启异步加载
defer与async的区别是:
defer
要等到整个页面在内存中正常渲染结束
(DOM 结构完全生成,以及其他脚本执行完成),才会执行;async
一旦下载完,渲染引擎就会中断渲染
,执行这个脚本以后,再继续渲染。
一句话,defer是“渲染完再执行”
,async是“下载完就执行”
。
注意:
另外,如果有多个defer脚本,会按照它们在页面出现的顺序加载,而多个async脚本是不能保证加载顺序的。
- 加载文件的逻辑
<script src="xxx.js" defer></script>
<script src="xxx.js" async></script>
<script type="module" src="./foo.js"></script>
加载文件的逻辑所以
<script type="module" src="./foo.js"></script>
可以理解为,异步加载 ./foo.js 文件,等页面渲染完毕之后再开始执行我们的脚本。
<script type="module" src="./foo.js"></script>
<!-- 等同于 -->
<script type="module" src="./foo.js" defer></script>
参考文章
再苦再累也必须要弄懂的:ES6的ES Module