什么是模块化?
◼ 到底什么是模块化、模块化开发呢?
事实上模块化开发最终的目的是将程序划分成一个个小的结构; 这个结构中编写属于自己的逻辑代码,有自己的作用域,定义变量名词时不会影响到其他的结构;
这个结构可以将自己希望暴露的变量、函数、对象等导出给其结构使用;
也可以通过某种方式,导入另外结构中的变量、函数、对象等;
上面说提到的结构,就是模块;按照这种结构划分开发程序的过程,就是模块化开发的过程;
无论你多么喜欢JavaScript,以及它现在发展的有多好,它都有很多的缺陷:
比如var定义的变量作用域问题;
比如JavaScript的面向对象并不能像常规面向对象语言一样使用class;
比如JavaScript没有模块化的问题
没有模块化带来的问题
早期没有模块化带来了很多的问题:比如命名冲突的问题
当然,我们有办法可以解决上面的问题:立即函数调用表达式
但是,我们其实带来了新的问题:
第一,我必须记得每一个模块中返回对象的命名,才能在其他模块使用过程中正确的使用;
第二,代码写起来混乱不堪,每个文件中的代码都需要包裹在一个匿名函数中来编写;
第三,在没有合适的规范情况下,每个人、每个公司都可能会任意命名、甚至出现模块名称相同的情况;
所以,我们会发现,虽然实现了模块化,但是我们的实现过于简单,并且是没有规范的。
我们需要制定一定的规范来约束每个人都按照这个规范去编写模块化的代码;
这个规范中应该包括核心功能:模块本身可以导出暴露的属性,模块又可以导入自己需要的属性;
JavaScript社区为了解决上面的问题,涌现出一系列好用的规范,接下来我们就学习具有代表性的一些规范。
CommonJS规范
我们需要知道CommonJS是一个规范,最初提出来是在浏览器以外的地方使用,并且当时被命名为ServerJS,后来为了体现它 的广泛性,修改为CommonJS,平时我们也会简称为CJS
CommonJS规范----模块中要导出的内容:exports 模块中要导入的内容:require
下面我们看以下例子了解它们的使用方法
// 这个是lisi.js
let name = "李四";
let phone = 66666666;
let sex = "女";
function getSort(arr){
arr.sort(function(a,b){
return a - b
})
};
function getReverse(arr){
return arr.reverse();
}
// 将lisi的方法也导出
exports.getSort=getSort;
exports.getReverse=getReverse;
exports.phone=phone;
可以看到-使用exports方法便可导出我们模块中的变量与方法
exports导出
exports是一个对象,我们可以在这个对象中添加很多个属性,添加的属性会导出(只有导出了才会从其他js中导入)
exports方法默认返回的是一个空对象,所以我们使用对象的追加属性方法
exports.自定义属性名=属性值(变量名与方法名)
当然导出不只是有这一种方法 还有其他
// 第一种方法导出
// 这个方法默认是一个空对象 所以使用对象的方法添加属性 exports.自定义属性名 = 属性值
exports.name = name;
exports.eamil=eamil;
exports.getRandom=getRandom;
exports.getSum=getSum;
// console.log("导出的对象",exports);
// 第二种方法导出 在第一种方法前添加 module 模块的意思 其实这两个方法差不多 都是追加对象属性
module.exports.name = name;
module.exports.eamil=eamil;
module.exports.getSum=getSum;
module.exports.getRandom=getRandom;
console.log(module.exports);
// 第三种方法 我们知道前两种方法都是追加对象属性 而我们第三种直接赋值一个对象
// module.exports={
// name : name,
// eamil : eamil,
// getSum : getSum,
// getRandom : getRandom
// };
// console.log(module.exports);
// 如果对象的自定义属性 与属性值一样的话 如上 那么便可以简写 只写一个要导出的 函数 或 变量
module.exports = {
name,
eamil,
getSum,
getRandom
}
require导入
// 接收lisi的导出对象 第一种导入方式
const lisi = require("./lisi");
const zhangsan = require("./zhangsan");
const一个变量名,接收其他js导出的对象 (一般接收的变量名与接收的页面名一样,为了方便区分)
注意:当我们导入当前模块时,当前模块会被执行一次
require细节
require是一个函数,可以帮助我们引入一个文件(模块)中导出的对象。
◼ 那么,require的查找规则是怎么样的呢?
const 变量名 = require("X");
情况一: X是一个Node核心模块,比如path、http 、fs
核心模块也是一个对象,里面包含的许多系统性的方法(系统对象)
如果是核心模块直接就返回一个对象,不需要寻找路径.
情况二:X是以 ./ 或 ../ 或 /(根目录)开头的(有些许麻烦)
第一步:将X当做一个文件在对应的目录下查找;
1.如果有后缀名,按照后缀名的格式查找对应的文件 2.如果没有后缀名,会按照如下顺序:
✓ 1> 直接查找文件X
✓ 2> 查找X.js文件
✓ 3> 查找X.json文件
✓ 4> 查找X.node文件
第二步:没有找到对应的文件,将X作为一个目录(文件夹)
查找目录下面的index文件
✓ 1> 查找X/index.js文件
✓ 2> 查找X/index.json文件
✓ 3> 查找 X/index.node文件
如果没有找到,那么报错:not foun
情况三:直接是一个X(没有路径),并且X不是一个核心模块
如果只是导入一个名称,没有路径并且也不是一个核心模块.
require会寻找当前目录下有没有node_module文件夹 查找一个叫X的文件夹,如果有就会自动寻 找这个X文件夹里面的index.js,系统会自动拼接这个文件(我们不写,系统也会帮我们做)
如果没有node_module文件夹,那么系统就会报错
模块的加载过程
◼ 结论一:模块在被第一次引入时,模块中的js代码会被运行一次
◼ 结论二:模块被多次引入时,会缓存,最终只加载(运行) 一次