什么是模块化?

  • 模块化就是把单独的一个功能封装到一个模块(文件)中,模块之间相互隔离,但是可以通过特定的接口公开内部成员,也可以依赖别的模块;
  • 将一个项目拆分为若干块,每块之间以一定的形式进行通信,而每块内部的内容都是独立的;

模块化的好处?

  • 抽离公共的代码,提高了代码的维护性和复用性;
  • 隔离作用域,避免变量的冲突;
  • 将一个复杂的系统分解为多个子模块,便于开发和维护;

常用的模块化规范?

CommonJs

nodejs使用的commonJs规范;在nodejs中,有且仅有一个入口文件(启动文件),而开发一个应用肯定会涉及多个文件配合,因此,nodejs对模块化的需求比浏览器端要大得多;

特点

  • CommonJs最大的特点就是同步加载;(缺点)
  • 每一个文件都是一个Module对象,通过关键字module.exports或者exports来暴露内容,并可以通过require来引入指定模块;
  • 模块一旦加载一次之后就会被缓存;

工作环境

服务端;两个原因使CommonJs无法在浏览器端使用:

  • 根本原因:CommonJs中使用了Node的api,无法在浏览器中运行。
  • 直接原因:作为一门同步加载的模块规范必须得在require完全加载好之后才能执行下一步,所以会有阻塞的情况,在网络状态比较差的情况下,就会导致浏览器的假死现象。(对于服务端,require的等待时间是硬盘读取时间,而对于浏览器端来说,这取决于网速的快慢。)

使用

  • 使用 exports.x 或者module.exports暴露模块;exports是一个空对象==>exports={}
  • 通过require来完成对模块的引入;
  • 如果一个JS文件存在exportsrequire,该JS文件就是一个模块,拥有独立的作用域;

使用exports.x暴露;

使用exports.x暴露;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 新建m1.js文件
let num = 10;

function sum(a,b){
return a+b
}

class Animal{
constructor(){
this.age=10;
}
}
// exports是个对象;给exports对象上面添加属性进行导出;
// exports.x
exports.num1=num; //num1是随便取的;尽量和变量名保持一致
exports.sum=sum;
exports.Animal=Animal;

使用require引入m1.js

1
2
3
4
5
6
7
8
9
10
// 新建index.js文件用来引入m1.js
// require函数的返回值是个对象,是exports对象;
const M1 = require("./m1.js"); //这行代码的本质是 M1=exports;(对象赋值,其实是地址)
console.log(M1); //{ num1: 10, sum: [Function: sum], Animal: [class Animal] }

console.log(M1.num1); //10
console.log(M1.sum(2,3)); // 5

const DOG = new M1.Animal(); //const声明的常量 建议大写;
console.log(DOG.age) //10

使用module.exports暴露多个;

使用module.exports暴露;在一个自定义模块中,默认情况下,module.exports={};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// 新建m2.js文件
let num = 10;

function sum(a,b){
return a+b
}

class Animal{
constructor(){
this.age=10;
}
}
// methods 1

// module.exports是个对象;给module.exports对象上面添加属性进行导出;
// module.exports.num=num;
// module.exports.sum=sum;
// module.exports.Animal=Animal;

//methods 2

// module.exports导出方式 es6对象简写形式(上面声明了变量,下面json的键值对名一样,可以简写;(值就是上面的变量))
module.exports={
num,
sum,
Animal,
}

使用require引入m2.js

1
2
3
4
5
6
7
8
9
// 两种方式的引入方式都是一样的,都是require("相对路径");
const M2 = require("./m2.js"); //require函数的返回值是个对象,是module.exports对象;
console.log(M2); //{ num: 10, sum: [Function: sum], Animal: [class Animal] }

console.log(M2.num); //10
console.log(M2.sum(5,3)); //8

const CAT = new M2.Animal(); //const声明的常量 建议大写;
console.log(CAT.age) //10

使用module.exports暴露单个;

使用module.exports暴露;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新建m2.js文件
let num = 10;

function sum(a,b){
return a+b
}

class Animal{
constructor(){
this.age=10;
}
}

// module.exports是个对象,但是不加属性,直接导出;
module.exports=sum;

使用require引入m2.js

1
2
3
4
5
const M2 = require("./m2.js"); 
console.log(M2); //[Function: sum] 这里的M2就是m2.js里导出的东西,就不再是对象了;

// console.log(M2.sum(5,3)); // TypeError: M2.sum is not a function
console.log(M2(5,3)); // 8

module.exports与exports有什么关系或者区别呢?

  • 我们追根溯源,通过维基百科中对CommonJS规范的解析:
    • CommonJS中是没有module.exports的概念的;
    • 但是为了实现模块的导出,Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是
      module;
    • 所以在Node中真正用于导出的其实根本不是exports,而是module.exports;
    • 因为module才是导出的真正实现者;
  • 但是,为什么exports也可以导出呢?
    • 这是因为module对象的exports属性是exports对象的一个引用;
    • 也就是说 module.exports = exports = index.js中的M2;
module对象

Node中使用的是Module的类,每一个模块都是Module的一个实例,也就是module;

module

module.exports 对象

module.exports=xxx的过程其实是引用赋值(浅拷贝)的过程;exports(也是一个对象)之所以可以导出,就是把exports对象的地址赋值给了module.exports,借助module.exports导出;

  • 使用 require() 方法导入模块时,导入的结果,永远以 最下面的module.exports 指向的对象为准;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新建m2.js文件
let num = 10;

function sum(a,b){
return a+b
}

// module.exports是个对象;给module.exports对象上面添加属性进行导出;
module.exports.num=num;
module.exports.sum=sum;

// module.exports开辟了新的引用类型地址;
module.exports={
name:"开辟新的引用类型地址"
}
1
2
const M2 = require("./m2.js"); //require函数的返回值是个对象,是module.exports对象;
console.log(M2); //{ name: '开辟新的引用类型地址' }
  • 上面的module.exports属性要是在下面结果就不一样了;相当于在原来的对象上添加了属性;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 新建m2.js文件
let num = 10;

function sum(a,b){
return a+b
}

// module.exports开辟了新的引用类型地址;
module.exports={
name:"开辟新的引用类型地址"
}

// module.exports是个对象;给上面的那个module.exports对象上面添加属性进行导出;
module.exports.num=num;
module.exports.sum=sum;
1
2
const M2 = require("./m2.js"); //require函数的返回值是个对象,是module.exports对象;
console.log(M2); //{ name: '开辟新的引用类型地址', num: 10, sum: [Function: sum] }
  • exports与module.exports同时导出

由于真正用于导出的其实根本不是exports,而是module.exports;所以同时导出的时候module.exports开辟了新的内存地址,exports就相当于被架空了;

1
2
3
4
5
6
7
8
9
10
11
12
13
// 新建m2.js文件
let num = 10;

function sum(a,b){
return a+b
}

module.exports.num=num;
module.exports.sum=sum;

exports={
name:"开辟新的引用类型地址"
}
1
2
const M2 = require("./m2.js"); //require函数的返回值是个对象,是module.exports对象;
console.log(M2); //{ num: 10, sum: [Function: sum] }

ES6

特点

  • ES6最大的特点是按需加载;

工作环境

  • 是浏览器端与服务器端通用的模块化开发规范;

使用

  • 目前浏览器使用如下方式引入一个ES6模块文件: <script src="入口文件" type="module"></script>
  • 使用 export 或者export default暴露模块;
  • 通过import来完成对模块的引入;
  • export defalut每个js模块里面只能有一个;export可以有多个;

使用export直接导出

使用export暴露;

1
2
3
// 新建m1.js文件
export let name = "张三";
export let age = 12;

使用import {xx,xx} from "xxx"引入;

1
2
3
4
// 新建index.js文件用来引入m1.js
import {name,age} from "./m1.js";
console.log(name);//张三
console.log(age);//12

使用export一起导出

使用export{}暴露;

1
2
3
4
5
6
7
// 新建m1.js文件
let name = "张三";
let age = 13;
export {
name,
age
}

使用import {xx,xx} from "xxx"引入;

1
2
3
4
// 新建index.js文件用来引入m1.js
import {name,age} from "./m1.js";
console.log(name);//张三
console.log(age);//13

使用export default导出

使用export default暴露;

1
2
3
4
5
6
7
// 新建m1.js文件
let name = "张三";
let age = 14;
export default{ //默认导出:一个js文件中只能写一次
name,
age
}

使用import xxx from "xxx"引入;

1
2
3
4
5
6
// 新建index.js文件用来引入m1.js
import m1 from "./m1.js"; //import 后面的 m1 是随便起的;
console.log(m1); //{ name: '张三', age: 14 }

console.log(m1.name);//张三
console.log(m1.age);//14

混用

使用exportexport default暴露;

1
2
3
4
5
6
7
8
// 新建m1.js文件
export let email = "nodeJs@163.com";
let name = "张三";
let age = 18;
export default{ //默认导出一个js文件中只能写一次
name,
age
}

使用import xxx,{xx,xx} from "xxx"引入;

1
2
3
4
5
6
7
// 新建index.js文件用来引入m1.js
import m1,{email} from "./m1.js";
console.log(m1); //{ name: '张三', age: 18 }

console.log(m1.name);//张三
console.log(m1.age);//18
console.log(email);//nodeJs@163.com

参考文献