模块化代码结构
- Node采用的模块化结构是commonJS规范
- 模块与文件是一一对应关系,即加载一个模块,实际上就是加载对应的一个模块文件
CommonJS模块的特点
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
模块的分类
文件模块
就是我们自己写的功能模块文件
核心模块
Node平台自带的一套基本的功能模块,也有人称之为Node平台的API
第三方模块
社区或者第三方个人开发好的功能模块,可以直接拿回来用
模块化开发的流程
- 创建模块(foo.js)
- 导出成员(module.exports = {})
- 载入模块 (let foo = require(“./foo.js”)
- 使用模块 (foo.bar())
模块内的(伪)全局环境
我们在文件操作中必须使用绝对路径
- __dirname
- 用于获取当前文件所在目录的完整路径;
- 在REPL环境无效
- __filename
- 用来获取当前文件的完整路径;
- 在REPL环境无效;
- module
- 模块对象
- exports
- 映射到module.exports的别名
- require()
- require.cache
- require.extensions
- require.main
- require.resolve()
module对象
Node内部提供一个module构建函数。所有模块都是module的实例。有以下属性:
- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.filename 模块定义的文件的绝对路径。
- module.loaded返回一个布尔值,表示模块是否已经加载完毕。
- module.parent 返回一个对象,表示调用该模块的模块
- module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
载入一个模块就是构建一个module实例
看个例子
|— module
|— module.js
|— 05-module.js
module/module.js
|
|
05-module.js
|
|
然后我们执行05-module.js
|
|
模块的定义
- 一个新的js文件就是一个模块
- 一个合格的模块应该是有导出成员的,否则模块就失去了定义的价值。
- 模块内部是一个独立(封闭)的作用域(模块与模块之间不会冲突)
- 模块之间必须通过导出或导入的方式协同
- 导出方式:
- exports.name = value;
- module.exports = {name:value}
- module.exports和exports
- module.exports是用于为模块导出成员的接口
- exports是指向module.exports的别名,相当于在模块开始的时候执行:
var exports = module.exports;
- 一旦为module.exports赋值,就会切断之前两者的相关性;
- 最终模块的导出成员以module.exports为准。
看个例子
|— module
|— module.exports.js
|— 06-module.exports.js
module/module.exports.js
|
|
06-module.exports.js
|
|
然后我们执行06-module.exports.js
|
|
然后我们用module.exports的方式改写一下
|
|
执行06-module.exports.js
|
|
我们再用ES6的语法简化一下
|
|
执行结果和上面的一样。
载入模块
- Node使用commonJS模块规范,内置的require函数用于加载模块文件。
- require的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。
- 如果没有发现执行模块,会报错。
模块的加载机制
id:路径的情况就是直接以相对路径的方式找文件
require实现机制
- 将传入的模块ID通过加载规则找到对应的模块文件
- 读取这个文件里面的代码
- 通过拼接的方式为该段代码构建私有空间
- 执行该代码
- 拿到module.exports返回
我们手动写一个$require函数
|— module
|— module.exports.js
|— 07-$require.js
|
|
然后执行
|
|
require扩展名
require加载文件时可以省略扩展名:
require("./module")
require("./module.js")
此时文件按js文件执行require("./module.json")
此时文件按JSON文件解析require("./module.node")
此时文件预编译好的C++模块执行
优先级是js>json>node
require加载文件规则
通过./后../开头:则按照相对路径从当前文件所在文件夹开始寻找模块;
require("../file.js")
上级目录下找file.js文件通过/开头:则以系统根目录开始寻找模块;
require("/Users/melody0z/github/nodejs/second/module/file.js")
以绝对路径的方式寻找
如果参数字符串不以”./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于node的系统安装目录中)
require("fs")
加载核心模块中的文件系统(file system)模块或者从当前目录向上搜索node_modules目录中的文件:
require("my_module");
各级node_modules文件夹中搜索my_module.js文件;
require加载目录规则
如果require传入的是一个目录的路径,会自动查看该目录的package.json文件,然后加载main字段指定的入口文件
如果package.json文件没有main字段,或者根本就没有package.json文件,则默认找目录下的index.js文件作为模块
require("./module");
当前目录下找module文件夹中的index.js文件
模块的缓存
- 第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接冲缓存取出该模块的module.exports属性(不会再次执行该模块)
- 如果需要多次执行模块中的代码,一般可以让模块暴露行为(函数)
- 模块的缓存可以通过require.cache拿到,同样也可以删除
验证模块有缓存
目录结构
|— date.js
|— 08.cache.js
date.js
|
|
08-cache.js
|
|
执行08-cache.js
|
|
看,每次输出的时间戳一样,说明结果被缓存起来了。
我们再来看
目录结构
|— date.js
|— 09.cache2.js
09-cache2.js
|
|
执行
|
|
同样证明有缓存
缓存长什么样?
目录结构
|— date.js
|— 10.cache3.js
10-cache3.js
|
|
我们打印一下缓存看看
|
|
我们发现,缓存和module对象类似
了解了缓存之后,我们就知道如何清除缓存了
目录结构
|— date.js
|— 11-swipecache.js
11-swipecache.js
|
|
执行11-swipecache.js
|
|
注意:
先清除缓存,再require。
下面我们将自己写的$require加上缓存
|
|
执行
|
|
本文结束,感谢阅读。
本文作者:melody0z
本文链接:https://melodyvoid.github.io/Node/modular-structure.html
欢迎转载,转载请注明文本链接