commonJS核心
导出
commonJS 里我们可以使用exports导出,也可以通过module.exports导出,那么它们到底什么关系,曾经我一直一脸懵b,现在终于是搞明白了~~ 嗝
其实在commonJS规范里是没有module.exports这个导出规则的,只有通过exports进行导出。module的本质是node的一个构造函数,我们在node环境下创建一个js文件的本质就是实例化一个module,别不相信,事实就是这样。
node里之所以可以使用module.exports导出,是因为node做了这样的处理:
exports = module.exports = {},没错,就是引用赋值。所以我们在使用module.exports本质还是使用exports。
注意:node的模块里最终导出的是module.exports 而非exports。
如:
// 模块:bar.js
let name = 'jack'
let age = 12
function getSum() {
console.log('getSum')
}
// 导出方式1
exports.name = 'jack'
exports.age = 12
exports.getSum = function(){}
// 导出方式2
module.exports = {
name,
age,
getSum
}
导入
commonJS里通过require()函数导入
如上面bar.js模块导出的内容可以这样导入:
// 模块:index.js
// 方式1:导入的是一个对象
let bar = require('./bar.js')
console.log(bar) // { name: 'jack', age: 12, getSum: [Function: getSum] }
// 方式2: 将导入的对象直接解构导入
let {name, age, getSum } = require('./bar.js')
console.log(name, age, getSum) // jack 12 [Function: getSum]
练习1:index.js里输出的值是什么
// 模块1 : bar.js
let name = 'jack'
let age = 12
exports.name = name
exports.age = age
exports = {}
// 模块2 : index.js
let bar = require('./bar.js')
console.log(bar)
答案是: // {name: 'jack', age: 12}
分析:我们可以想象在模块的第一行有一行代码: exports = module.exports = {},相当于module.exports 和exports指向同一个引用地址,此时为一个空对象
执行完
let name = 'jack'
let age = 12
exports.name = name
exports.age = age
时,这个空对象有了name和age属性及值,即{name: 'jack', age: 12};
执行完exports = {} 时,exports不再指向上面说的那个对象,而是指向一个空对象,即此时只有module.export 指向上面所述的对象。
而我们说过node的模块里最终导出的是module.exports 而非exports。所以这个模块最终导出的是{name: 'jack', age: 12} 而非{}
这里涉及到js的按引用赋值知识;
练习2: index.js里输出的值是什么?
// 模块:bar.js
let name = 'jack'
let age = 12
function getSum() {
console.log('getSum')
}
exports = {
name,
age,
getSum
}
// 模块:index.js
let {name, age, getSum } = require('./bar.js')
console.log(name, age, getSum)
答案是 // undefined undefined undefined 原理同上一题
commonJS导入模块查找规则:
-
-
情况1:内置模块
-
如let url = require('url')
如果导入的模块为node内置模块,则会直接导入;因为url是node的内置模块,直接导入;
-
情况2:自定义模块(以./ 或者 ../ 或者 / 开头)
如let url = require('./home')
(1)会在对应得目录找是否有home文件,如果有,则导入;
(2)如果没有home文件,则会在对应目录下依次查找home.js,home.json,home.node这三个文件,如果查找到其中一个文件,则导入。(这也是为什么我们在使用commonJS时,导入模块不写后缀,却依然正常导入的原因);
(3)如果没有home.js,home.json,home.node文件,则会将home当做文件夹进行查找,如果有home文件夹,则依次查找home文件夹里的index.js,index.json,index.json,如果查找到这三个文件里的其中一个,则导入(这也是为什么我们在导入index.js文件时,有时候并不会精确到index.js,而是只写到index.js所在的目录的原因);
如果上面三种情况都没有查到对应文件,则报错;
-
情况3:第三方模块
如导入第三方模块axios: let server = require('axios')
因为axios不是node的内置模块,不符合情况一和情况二的查找规则,所以按照第三方模块进行查找
(1)在当前文件所在的目录下查看是否有node_modules文件夹,如果有,则在其中查找axios模块,查到,则导入;
(2)如果当前文件所在的目录下没有node_modules文件夹,则在当前文件的上一级目录查找node_modules文件夹,如果有,则在其中查找是否有axios模块,查到,则导入;
(3)如果上一级目录还没有node_modules 文件夹,则继续查找上一级......以此类推,直到查到根目录为止,如果最终没查到则报错,查到,则导入;
这也是为什么我们的项目依赖(node_modules)总是放在项目的根目录下的原因,因为这样,项目里的所有文件都可以导入node_modules里的依赖
小技巧:我们可以在文件里执行 console.log(module.paths) 代码,输出结果就是当前模块加载第三方模块的目录组成的数组,会依次按照这个数组进行查找