一、初识模块化
模块化即解决一个复杂问题时,自顶向下逐层把系统划分成若干模块的过程。对于整个系统来说,模块化是可组合、分解和更换的单元。
编程领域中的模块化就是遵守固有的规则,把一个大文件拆成独立并互相依赖的多个小模块
1.1 模块化拆分的好处
提高了代码的复用性
提高了代码的可维护性
可以实现按需加载
1.2 浏览器开发中的模块
- 在浏览器开发中为了避免命名冲突, 方便维护等等
- 我们采用类或者立即执行函数的方式来封装JS代码, 来避免命名冲突和提升代码的维护性
- 其实这里的一个类或者一个立即执行函数就是浏览器开发中一个模块
let obj = {
模块中的业务逻辑代码
};
;(function(){
模块中的业务逻辑代码
window.xxx = xxx;
})();
存在的问题:没有标准没有规范
1.3 模块化规范
模块化规范就是对代码进行模块化的拆分与组合时,需要遵守的那些规则
例如:
- 使用什么样的语法格式来引用模块
- 在模块中使用什么样的与语法格式向外暴露成员
- ......
模块化规范的好处:大家都遵循同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己
1.4 NodeJS开发中的模块
nodejs中根据来源的不同,将模块分为了3大类:
- 内置模块(内置模块是由nodejs官方提供的,例如:fs、path、http等)
- 自定义模块(用户创建的每个.js文件,都是自定义模块)
- 第三方模块(由第三方开发出来的模块,并非官方提供的内置模块,也不是用户创建的自定义模块,使用前需要先下载)
NodeJS采用CommonJS规范实现了模块系统
CommonJS规范规定了如何定义一个模块, 如何暴露(导出)模块中的变量函数, 以及如何使用定义好的模块
- - 在CommonJS规范中一个文件就是一个模块
- - 在CommonJS规范中每个文件中的变量函数都是私有的,对其他文件不可见的
- - 在CommonJS规范中每个文件中的变量函数必须通过exports暴露(导出)之后其它文件才可以使用
- - 在CommonJS规范中想要使用其它文件暴露的变量函数必须通过require()导入模块才可以使用
如何加载模块:
使用强大的 require() 方法,可以加载需要的内置模块、用户自定义模块、第三方模块进行使用,例如:
// 1. 加载内置的 fs 模块
const fs = require('fs');
//2. 加载自定义模块
const custom = require('./cuustom.js');
//3.加载第三方模块
const moment = require('moment');
示例:
a.js,
let name = "jack";
function sum(a, b) {
return a + b;
}
exports.str = name;
exports.fn = sum;
b.js,
let aModule = require("./06-a");
console.log(aModule);
console.log(aModule.str);
let res = aModule.fn(10, 20);
console.log(res);
1.5 使用require的注意点
require导入模块时可以不添加导入模块的类型:
例如:(let aModule = require("./09");)
如果没有指定导入模块的类型, 那么会依次查找.js .json .node文件
无论是三种类型中的哪一种, 导入之后都会转换成JS对象返回给我们
导入自定义模块时必须指定路径:
require可以导入"自定义模块(文件模块)"、"系统模块(核心模块)"、"第三方模块"
导入"自定义模块"模块时前面必须加上路径(let aModule = require("./09-a.js");)
导入"系统模块"和"第三方模块"是不用添加路径
导入"系统模块"和"第三方模块"是不用添加路径的原因:
如果是"系统模块"直接到环境变量配置的路径中查找
如果是"第三方模块"会按照module.paths数组中的路径依次查找
二、模块作用域
2.1 什么是模块作用域
和函数作用域类似,在自定义模块中定义的变量、方法等成员,只能在当前模块内被访问,这种模块级别的访问机制,即模块作用域
2.2 使用模块作用域的优点
防止了全局变量污染的问题
三、node模块导出数据的几种方式
3.1 module.exports 对象
在每个 .js 自定义模块中,都有一个 module 对象,它里面存储了和当前模块相关的信息
在自定义模块中,可以使用 module.exports 对象,将模块内的成员共享出去,供外界使用
外界用 require() 导入自定义模块时,得到的就是 module.exports 所指向的对象
语法:module.exports.xx = yy
a.js,
let name = "iwen";
function sum(a, b) {
return a + b;
}
//方式1 module.exports.xx = yy
// module.exports.str = name;
// module.exports.fn = sum;
//或
module.exports = {
str: name,
fn: sum
};
b.js,
let aModule = require("./07-a");
console.log(aModule);
console.log(aModule.str);
console.log(aModule.fn(10, 20));
// let res = aModule.fn(10, 20);
// console.log(res);
3.2 exports对象
由于 module.exports 单词写起来比较复杂,为了简化向外共享成员的代码,node提供了 exports 对象。
默认情况下,exports 和 module.exports 指向同一个对象,最终共享的结果还是以 module.exports 指向的对象为准,即 require() 模块时,永远都是 module.exports 指向的对象为准
语法:exports.xx = yy
a.js
let name = "iwen";
function sum(a, b) {
return a + b;
}
//方式1 exports.xx = yy
exports.str = name;
exports.fn = sum;
b.js
let aModule = require("./07-a");
console.log(aModule);
console.log(aModule.str);
console.log(aModule.fn(10, 20));
3.3 global 对象
语法:global.xx = yy
a.js
let name = "iwen";
function sum(a, b) {
return a + b;
}
//方式3 global.xx = yy
global.str = name;
global.fn = sum;
b.js
let aModule = require("./07-a");
console.log(str);
let res = fn(10, 20);
console.log(res);
3.4 exports和module.exports区别
exports只能通过 exports.xxx方式导出数据, 不能直接赋值:
a.js,
let name = "lnj";
// exports.str = name;
// module.exports.str = name;
exports = name;
b.js,
let aModule = require("./08-a.js");
console.log(aModule);
module.exports既可以通过module.exports.xxx方式导出数据, 也可以直接赋值:
a.js,
let name = "lnj";
// exports.str = name;
// module.exports.str = name;
module.exports = name;
b.js,
let aModule = require("./08-a.js");
console.log(aModule);
require() 模块时,永远都是 module.exports 指向的对象为准:
注意点:
在企业开发中无论哪种方式都不要直接赋值, 这个问题只会在面试中出现