前言
陆陆续续两三天,一直在寻找好的学习资源,想跟着一套视频一直学习完整个系列过程,NodeJS发展实在太快,很多资源教程使用的版本,官方文档就已经更新换代,直接看官网的文档,又太过枯燥无味,特别是对于我从前端转过来,没有后端基础,对于很多概念不理解,直接看文档和源码也很难看懂.
实践又无从下手,所以结合网上良莠不齐的技术博客,教程书籍,视频实战,进行一个Node核心技术的学习,往后再是站点的搭建,希望在这过程中的关键知识点都能够做一个记录。
CommonJS
概念
Node应用由模块组成,采用CommonJS模块规范,为解决全局变量污染的问题;
JS作为函数是编程语言,支持闭包,把一段JS代码用函数包装起来,其所有全局变量就变成了函数的局部变量,每个函数作为一个模块,互相调用来实现模块机制;
根据这个规范,每个文件就是一个模块;
//example.js
var x = 5;
var addX = function (value) {
return value + x;
};
module.exports.x = x;
module.exports.addX = addX;
//client.js
var example = require('./example');
console.log(example.x);
console.log(example.addX(1));
CommonJS的模块特点:
- 所有代码都运行在模块作用域,不会污染全局作用域。
- 模块可以多次加载,但是只会在第一次加载时运行一次,然后运行结果就被缓存了,以后再加载,就直接读取缓存结果。要想让模块再次运行,必须清除缓存。
- 模块加载的顺序,按照其在代码中出现的顺序。
原理
Node内部提供一个Module构建函数,所有模块都是Module的实例;
每个module对象,代表当前模块,都有以下属性:
- module.id 模块的识别符,通常是带有绝对路径的模块文件名。
- module.filename 模块的文件名,带有绝对路径。
- module.loaded 返回一个布尔值,表示模块是否已经完成加载。
- module.parent 返回一个对象,表示调用该模块的模块。
- module.children 返回一个数组,表示该模块要用到的其他模块。
- module.exports 表示模块对外输出的值。
// example.js
var jquery = require('jquery');
exports.$ = jquery;
console.log(module);
{ id: '.',
exports: { '$': [Function] },
parent: null,
filename: '/path/to/example.js',
loaded: false,
children:
[ { id: '/path/to/node_modules/jquery/dist/jquery.js',
exports: [Function],
parent: [Circular],
filename: '/path/to/node_modules/jquery/dist/jquery.js',
loaded: true,
children: [],
paths: [Object] } ],
paths:
[ '/home/user/deleted/node_modules',
'/home/user/node_modules',
'/home/node_modules',
'/node_modules' ]
}
exports AND module.exports
module通过其属性exports对外暴露要要输出的内容:
module.exports.x = x;
为了方便,Node为每个模块提供了一个exports变量,指向module.exports,等同于在每个模块的头部,添加了这样一行代码:
var exports = module.exports;
所以在对外输出接口时,也可以使用exports;
exports.x = x;
通过给exports的属性赋值,等同于给module.exports这个空对象添加属性和值;
但是,不能直接对exports赋值,因为在Node内部保存的是module对象,而对exports直接赋值,exports就不再指向module.exports这个对象,也就切断了exports和module.exports的联系;
require
require命令的基本功能是,读入并执行一个JavaScript文件,然后返回该模块的exports对象。如果没有发现指定模块,会报错。
根据参数的不同格式,require命令去不同路径寻找模块文件。
如果参数字符串以“/”开头,则表示加载的是一个位于绝对路径的模块文件。比如,require('/home/marco/foo.js')将加载/home/marco/foo.js。
如果参数字符串以“./”开头,则表示加载的是一个位于相对路径(跟当前执行脚本的位置相比)的模块文件。比如,require('./circle')将加载当前脚本同一目录的circle.js。
如果参数字符串不以“./“或”/“开头,则表示加载的是一个默认提供的核心模块(位于Node的系统安装目录中),或者一个位于各级node_modules目录的已安装模块(全局安装或局部安装)。
如果参数字符串不以“./“或”/“开头,而且是一个路径,比如require('example-module/path/to/file'),则将先找到example-module的位置,然后再以它为参数,找到后续路径。
如果指定的模块文件没有发现,Node会尝试为文件名添加.js、.json、.node后,再去搜索。.js件会以文本格式的JavaScript脚本文件解析,.json文件会以JSON格式的文本文件解析,.node文件会以编译后的二进制文件解析。
如果想得到require命令加载的确切文件名,使用require.resolve()方法。
模块加载机制和缓存机制后面再更新;
参考文章:
http://javascript.ruanyifeng.com/nodejs/module.html
http://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434502419592fd80bbb0613a42118ccab9435af408fd000