module的学习参考了以下大大们的博客~仅为自己的学习笔记。
http://es6.ruanyifeng.com/#docs/module
1. 模块化的需求
为什么要使用模块化,前端模块化的发展历程,在我的上一篇学习笔记《前端模块化的学习笔记》中有写
https://blog.csdn.net/hongtaochi0464/article/details/92572860
在学习这一章的时候,应当将ES6标准的模块化与CommonJS规范的模块化进行区分。下面先学习一下ES6模块化的语法~
2. ES6模块化的语法
在ES6中,我们使用export导出接口,使用import引入模块。
2.1 export
在模块化中,我们希望一个模块是独立的文件,外部不能随意访问和获取我们定义的变量。只能通过我们给出的接口来进行操作。ES6模块中,使用export来导出接口,具体的写法如下:
//module1.js
export var userName = "cjx"
export var age = "18"
export function speak(){
console.log(userName+"is speaking");
}
也可以export一个对象(使用{}进行export),如:
//module1.js
var userName = "cjx"
var age = "18"
function speak(){
console.log(userName+"is speaking");
}
export { userName, age, speak }
一般情况下,export输出的变量就是本来的名字。可以使用as关键字重命名。
//module1.js
var userName = "cjx"
var age = "18"
function speak(){
console.log(userName+"is speaking");
}
export { userName as name, age, speak }
export命令必须处于模块顶层,不能处于块级作用域内。
2.2 import
在ES6中,使用import命令来加载其他模块export出来的对外接口。
如我们想要在main.js中引入上面module.js暴露出来的接口,就可以用下面的写法
//main.js
import { userName, age, speak } from "./module.js";
speak();
console.log("我叫"+userName+",今年"+age+"岁")
import 也可以使用as关键字来对引入的变量进行重命名,写成如下:
//main.js
import {userName as name} from "./module.js"
2.3 *
除了指定加载某个(些)接口,还可以使用*指定一个对象,将所有的接口都加载在这个对象上。如下面的例子:
//module1.js
var userName = "cjx"
var age = "18"
function speak(){
console.log(userName+"is speaking");
}
export { userName, age, speak }
现在我们使用*来加载这个模块
//main.js
import * as m1 from "./module.js"
m1.speak();
console.log("我叫"+m1.userName+",今年"+m1.age+"岁")
2.4 export default
使用export default,可以为模块指定默认输出。用其他模块加载该模块时,import命令可以为该接口指定任意名字。如以下写法:
//module.js
export var userName = "cjx"
export var age = "18"
export default function speak(){
console.log(userName+"is speaking");
}
(export default命令也可用在非匿名函数之前,加载的时候会视同匿名函数来加载)
//main.js
import f1 from "./module.js"
f1()
本质上,export default就是输出一个叫做default的变量或方法,然后系统允许你为它取任意名字。因此,一个模块只能有一个默认输出。export default命令在一个模块中只能使用一次。且import命令后面不用加大括号就能唯一获取到default命令。
ES6模块中还支持同时import默认方法和其他接口,如:
//main.js
import f1, {userName,age} from "./module.js"
3. commonJS模块化的语法
在commonJS中,我们使用module.exports导出接口,使用require来引入模块。
3.1 module.exports
将上面的module.js用commonJS模块化的语法写成如下
//module.js
module.exports = {
userName : "cjx",
age : "18",
speak : function(){
console.log(this.userName+" is speaking");
}
}
3.2 require
将上面的main.js用commonJS模块化的语法写成如下:
//main.js
var o1 = require("./module.js")
o1.speak();
console.log(o1)
我们使用node来执行一下main.js,输出结果如下:
4. 比较ES6模块化和commonJS模块化
(1)ES6模块的加载是“编译时加载”,commonJS模块的加载是"运行时加载"。
对于commonJS模块来说
let { userName, age, speak } = require("./module.js")
等同于
let m1 = require("./module.js")
let userName = m1.userName
let age = m1.age
let speak = m1.speak
即相当于 加载了整个module模块再去从加载出的对象中分别读取方法。
而对于ES6模块来说
let { userName, age, speak } from "./module.js"
就是单纯的从module模块中加载三个方法,其他方法是不加载的。
(2)import命令具有提升效果,会提升到整个模块的头部执行。如以下的代码不会报错:
speak();
console.log("我叫"+userName+",今年"+age+"岁")
import { userName, age, speak } from "./module.js";
(3)require可以实现条件加载
// import报错
if (x === 2) {
import module from "./module.js"
}
//require可运行
if (x === 2) {
var module = require("./module.js")
}
[注]目前已有提案使用import()来进行条件加载。import()返回一个Promise对象。更多的可看阮一峰的博客~
(4)export语句输出的接口,与其对应的值是动态绑定关系,即通过该接口,可以取到模块内部实时的值。
//module.js
export let age = 18;
export oneYearAfter(){
age++;
}
//main.js
import {age,oneYearAfter} from "./module.js"
console.log(age); //18
oneYearAfter();
console.log(age); //19
CommonJS 模块输出的是值的拷贝,也就是说,一旦输出一个值,模块内部的变化就影响不到这个值。
//module.js
module.exports = {
age : 18,
oneYearAfter : function(){
this.age++
}
}
let {age,oneYearAfter} = require("./module.js")
console.log("age1:"+age);
oneYearAfter();
console.log("age2:"+age);
我们使用node运行这个main.js,结果如下: