export等等那些故事(一)CommonJS规范

2020-03-12  本文已影响0人  growYdp

简介

因为今天要梳理的export我实在是区分不清楚,把它们的关系弄的很乱,今天就彻底梳理一下吧。

规范

想理清楚他们之间的关系,要先从规范开始看起。

CommonJS规范

1. 概述:
// name.js 文件

const name = "Jack";
const sayName = function (value) {
  return `hello ${name}`
}

module.exports.name = name
module.exports.sayName = sayName
// index.js 文件
const name = require("./name.js")

console.log(name.name)
console.log(name.sayName("Jone"))
2. module对象

Node内部提供一个Module构建函数,所有模块都是Module的实例。

function Module(id, parent) {
  this.id = id
  this.exports = {}
  this.parent = parent
}

每个模块内部,都有一个module对象,代表当前模块。
console.log(module)
可以试一试
3. module.exports属性

表示当前模块对外输出的接口,其他文件加载该模块,实际上就是读取module.exports变量

4. exports 变量

Node为每个模块提供一个exports变量,指向module.exports这等同在每个模块的头部,有var exports = module.exports命令

console.log(exports === module.exports) // true

对外输出模块接口时,可以向exports对象添加方法

exports.sayName = function (name) {
  return `hello ${name}`
}

不能直接将exports变量指向一个值,这样就切断了exports与module.exports的联系。(也就是指向另一个值,然后exports就不再指向module.exports

exports = "change"
console.log(exports === module.exports) // false

所以这种写法是无效的。
···
name.js
exports.sayName = function(name) {
return "hello " + name
}
module.exports = "haha"
···

index.js
const name = require("./name")
console.log(name)

name.js中的sayName是无法对外输出的,因为module.exports被重新赋值了。如果一个模块的对外接口,就是一个单一的值,不能使用exports输出,只能使用module.exports输出。

5. require命令

用于加载模块文件(用于读入并执行一个Javascript文件),然后返回该模块的exports独享。如果没有发现指定模块,会报错。

name.js
exports.sayName = function(name) {
  return "hello " + name
}
exports.name = "testing"
const name = require("./name")

console.log(name) // { sayName: [Function], name: 'testing' }

如果输出的是一个函数,就不能定义在exports对象上面,而要定义在module.exports变量上面。

module.exports = function (name) {
  console.log("hello " + name)
}
require('./name')('haha') // hello haha

require命令调用自身,等于是执行module.exports,因此会输出hello haha.(调用自己,顺便传了个参数,前一段时间做express的时候就把app传递给router,js了 ~)

6. 加载规则

require命令用于加载文件,后缀名默认为.js
也就是在加载js文件的时候加不加后缀名都可以!根据参数的不同格式,require命令去不同路径寻找模块文件。

require('element-ui/css/index.css')

就是找到element-ui 模块,然后找到这个模块下的css路径下的index.css文件。

const name = require.resolve("./name")

console.log(name) // D:\zamall\vueblog\server\name.js
7. 目录的加载规则

为目录设置一个入口文件,让require方法可以通过这个入口文件,加载这个目录

在目录中设置package.json 文件 将入口文件写入main字段
8.模块的缓存

上面讲过,Node在加载某个模块的时候,Node会缓存该模块。以后再次加载该模块,就直接从缓存中取出该模块的module.exports属性。

name.js
index.js
require('./name.js')
require('./name.js').name = "hello"
console.log(require('./name.js').name) // hello

连续三次调用require命令。第二次加载的时候,为输出对象添加一个name属性,第三次加载的时候,这个name属性依然存在,require命令并没有重新加载模块文件,而是输出了缓存。
所有缓存的模块保存在require.cache之中,如果想删除模块的缓存。

// 删除指定模块的缓存 -moduleName 为要删除模块缓存的模块名称
delete require.cache[moduleName]

// 删除所有模块的缓存
Object.keys(require.cache).forEach(function(key) {
  delete require.cache[key];
})
// tips:缓存是根据 绝对路径 识别模块的,所以相同的模块名,他们的路径不同,`require`命令还是会重新加载该模块
9.模块的循环加载
name1.js

exports.name = 'name11'
console.log('a.js ', require("./name2").name)
exports.name = 'name12'
name2.js

exports.name = 'name21'
console.log('a.js ', require("./name1").name)
exports.name = 'name22'
main.js

console.log('main.js ', require('./name1').name)
console.log('main.js ', require('./name2').name)
结果
node main
name2.js  name11
name1.js  name22
main.js  name12
main.js  name22

如果在main.js中再次打印一份到控制台
输出的结果还是上面main.js相同的两份。因为第一次加载就缓存起来了。所以再次调用,会直接取出缓存中的内容

上一篇下一篇

猜你喜欢

热点阅读