文章:https://nodejs.org/api/modules.html
var app = require('express')();
var http = require('http').Server(app);
require:和java 中 import 导入一样,表示当前.js 要用express(可以是目录或单个的.js).
一、Modules
module。exports:是一个全局变量
Node.js has a simple module loading system. In Node.js, files and modules 是一一对应的 (一个文件对应一个模板).
如,有一个foo.js文件,文件里面包括:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
在第一行,foo.js 装载同一目录下的circle.js文件。 下面是circle.js,内容:
const { PI } = Math;
exports.area = (r) => PI * r ** 2;
exports.circumference = (r) => 2 * PI * r;
模板circle.js 可以输出函数area() 和circumference(). 函数和对象被增加到一个module的根,通过在指定exports 对象增加属性,哪这个方法和属性属于一个文件的全局变量了?????
module的本地变量将成为私有,因为模板通过Node.js被打包在一个函数里(see module wrapper). 在整个例子 circle.js 里,变量PI 是私有的.
module.exports 属性能被赋一个新值 (如一个函数或对象).
下面, bar.js 用到了square 模板,输出一个构造函数。:
const square = require('./square.js');
const mySquare = square(2);
console.log(`The area of my square is ${mySquare.area()}`);
square 模板对应square.js:
// 对exports赋值将不能修改module,必须使用module.exports
module.exports = (width) => {
return {
area: () => width ** 2
};
};
The module 系统是在require('module') 模板实现.
访问主模板#
当一个文件从Node.js直接运行, require.main 设置它的模板. 这意味着,通过测试 require.main === module来决定是否一个文件被直接运行的可能性。
如果运行通过node foo.js,对一个文件 foo.js是true , 但是通过require('./foo')是false
因为module 提供一个 filename 属性 ( __filename), 通过检查require.main.filename,当前应用的入口点 能被包含。
Addenda: 包管理 Tips#
Node.js's的语法 require()函数 设计通用大量的支持许多合理的目录结构. 包管理程序如 dpkg, rpm, and npm 将希望能从Node.js模块,不用修改,建立本地包。
下面,我将提供建议的能工作的目录结构:
我们想有一个文件夹 /usr/lib/node/<some-package>/<some-version>保存 一个包特定版本的内容。
包可以相互依赖. 为了安装包foo, 必须安装一个特定版本包bar.bar 也许也有依赖, 在一些情况下, 这些有可能冲突或形成循环依赖.
因为 Node.js 寻找它装载某些模板的realpath (也就是说, 解决了符号连接), 然后寻找在node_modules 文件夹里他们的依赖,这种情况很简单,可以用下面的体系结构解决,发现一个包对应node_modules(就是放一个文件的依赖关系)。
/usr/lib/node/foo/1.2.3/- Contents of thefoopackage, version 1.2.3,找到包路径,找包里面的依赖/usr/lib/node/bar/4.3.2/- Contents of thebarpackage,foo包依赖bar包,找到包路径,找包里面的依赖/usr/lib/node/foo/1.2.3/node_modules/bar- 符号连接到/usr/lib/node/bar/4.3.2/,/usr/lib/node/bar/4.3.2/node_modules/*- 符号连接to the packages thatbardepends on.
Thus, even if a cycle is encountered, or if there are dependency conflicts, every module will be able to get a version of its dependency that it can use.
当在foo包里的代码写有require('bar')时,它将得到一个版本是被符号连接到 /usr/lib/node/foo/1.2.3/node_modules/bar. 然后,当在bar 包的代码调用require('quux'), 它将得到版本是符号连接到/usr/lib/node/bar/4.3.2/node_modules/quux.
此外, 为了模板查找过程更好,而不是把包直接放在 /usr/lib/node, 我们能把他们放在 /usr/lib/node_modules/<name>/<version>. 然后Node.js 不需要费心去找丢失的依赖在 /usr/node_modules or /node_modules.
为了是模板对Node.js REPL可用,给环境变量$NODE_PATH=/usr/lib/node_modules就可以了,因为模板寻找使用node_modules 文件夹是相对的, 且调用require(),是基于文件的真实路径,包本身可以在任何地方.
所有的在一起...#
当require() 被调用时,为了得到准确的装载的文件名,使用require.resolve() 函数.
require.resolve() 步骤如下:
require(X) 语句来自路径Y的module
1. If X 是一个核心模块,
a. return 核心模块
b. STOP
2. If X begins with '/'
a. set Y to be the filesystem root
3. If X begins with './' or '/' or '../'
a. LOAD_AS_FILE(Y + X)
b. LOAD_AS_DIRECTORY(Y + X)
4. LOAD_NODE_MODULES(X, dirname(Y))
5. THROW "not found"
LOAD_AS_FILE(X)
1. If X is a file, load X as JavaScript text. STOP
2. If X.js is a file, load X.js as JavaScript text. STOP
3. If X.json is a file, parse X.json to a JavaScript Object. STOP
4. If X.node is a file, load X.node as binary addon. STOP
LOAD_INDEX(X)
1. If X/index.js is a file, load X/index.js as JavaScript text. STOP
2. If X/index.json is a file, parse X/index.json to a JavaScript object. STOP
3. If X/index.node is a file, load X/index.node as binary addon. STOP
LOAD_AS_DIRECTORY(X)
1. If X/package.json is a file,
a. Parse X/package.json, and look for "main" field.
b. let M = X + (json main field)
c. LOAD_AS_FILE(M)
d. LOAD_INDEX(M)
2. LOAD_INDEX(X)
LOAD_NODE_MODULES(X, START)
1. let DIRS=NODE_MODULES_PATHS(START)
2. for each DIR in DIRS:
a. LOAD_AS_FILE(DIR/X)
b. LOAD_AS_DIRECTORY(DIR/X)
NODE_MODULES_PATHS(START)
1. let PARTS = path split(START)
2. let I = count of PARTS - 1
3. let DIRS = []
4. while I >= 0,
a. if PARTS[I] = "node_modules" CONTINUE
b. DIR = path join(PARTS[0 .. I] + "node_modules")
c. DIRS = DIRS + DIR
d. let I = I - 1
5. return DIRS
Caching#
Modules are cached after the first time they are loaded. This means (among other things) that every call to require('foo')will get exactly the same object returned, if it would resolve to the same file.
Multiple calls to require('foo') may not cause the module code to be executed multiple times. This is an important feature. With it, "partially done" objects can be returned, thus allowing transitive dependencies to be loaded even when they would cause cycles.
To have a module execute code multiple times, export a function, and call that function.
Module Caching Caveats#
Modules are cached based on their resolved filename. Since modules may resolve to a different filename based on the location of the calling module (loading from node_modules folders), it is not a guarantee that require('foo') will always return the exact same object, if it would resolve to different files.
Additionally, on case-insensitive file systems or operating systems, different resolved filenames can point to the same file, but the cache will still treat them as different modules and will reload the file multiple times. For example, require('./foo') and require('./FOO') return two different objects, irrespective of whether or not ./foo and ./FOO are the same file.
Core Modules#
Node.js has several modules compiled into the binary. These modules are described in greater detail elsewhere in this documentation.
The core modules are defined within Node.js's source and are located in the lib/ folder.
Core modules are always preferentially loaded if their identifier is passed to require(). For instance, require('http') will always return the built in HTTP module, even if there is a file by that name.
Cycles#
When there are circular require() calls, a module might not have finished executing when it is returned.
Consider this situation:
a.js:
console.log('a starting');
exports.done = false;
const b = require('./b.js');
console.log('in a, b.done = %j', b.done);
exports.done = true;
console.log('a done');
b.js:
console.log('b starting');
exports.done = false;
const a = require('./a.js');
console.log('in b, a.done = %j', a.done);
exports.done = true;
console.log('b done');
main.js:
console.log('main starting');
const a = require('./a.js');
const b = require('./b.js');
console.log('in main, a.done=%j, b.done=%j', a.done, b.done);
When main.js loads a.js, then a.js in turn loads b.js. At that point, b.js tries to load a.js. In order to prevent an infinite loop, an unfinished copy of the a.js exports object is returned to the b.js module. b.js then finishes loading, and its exports object is provided to the a.js module.
By the time main.js has loaded both modules, they're both finished. The output of this program would thus be:
$ node main.js
main starting
a starting
b starting
in b, a.done = false
b done
in a, b.done = true
a done
in main, a.done=true, b.done=true
Careful planning is required to allow cyclic module dependencies to work correctly within an application.
File Modules#
If the exact filename is not found, then Node.js will attempt to load the required filename with the added extensions: .js, .json, and finally .node.
.js files are interpreted as JavaScript text files, and .json files are parsed as JSON text files. .node files are interpreted as compiled addon modules loaded with dlopen.
A required module prefixed with '/' is an absolute path to the file. For example, require('/home/marco/foo.js') will load the file at /home/marco/foo.js.
A required module prefixed with './' is relative to the file calling require(). That is, circle.js must be in the same directory as foo.js for require('./circle') to find it.
Without a leading '/', './', or '../' to indicate a file, the module must either be a core module or is loaded from a node_modulesfolder.
If the given path does not exist, require() will throw an Error with its code property set to 'MODULE_NOT_FOUND'.
Folders as Modules#
It is convenient to organize programs and libraries into self-contained directories, and then provide a single entry point to that library. There are three ways in which a folder may be passed to require() as an argument.
The first is to create a package.json file in the root of the folder, which specifies a main module. An example package.json file might look like this:
{ "name" : "some-library",
"main" : "./lib/some-library.js" }
If this was in a folder at ./some-library, then require('./some-library') would attempt to load ./some-library/lib/some-library.js.
This is the extent of Node.js's awareness of package.json files.
Note: If the file specified by the "main" entry of package.json is missing and can not be resolved, Node.js will report the entire module as missing with the default error:
Error: Cannot find module 'some-library'
If there is no package.json file present in the directory, then Node.js will attempt to load an index.js or index.node file out of that directory. For example, if there was no package.json file in the above example, then require('./some-library') would attempt to load:
./some-library/index.js./some-library/index.node
Loading from node_modules Folders#
If the module identifier passed to require() is not a core module, and does not begin with '/', '../', or './', then Node.js starts at the parent directory of the current module, and adds /node_modules, and attempts to load the module from that location. Node will not append node_modules to a path already ending in node_modules.
If it is not found there, then it moves to the parent directory, and so on, until the root of the file system is reached.
For example, if the file at '/home/ry/projects/foo.js' called require('bar.js'), then Node.js would look in the following locations, in this order:
/home/ry/projects/node_modules/bar.js/home/ry/node_modules/bar.js/home/node_modules/bar.js/node_modules/bar.js
This allows programs to localize their dependencies, so that they do not clash.
It is possible to require specific files or sub modules distributed with a module by including a path suffix after the module name. For instance require('example-module/path/to/file') would resolve path/to/file relative to where example-module is located. The suffixed path follows the same module resolution semantics.
Loading from the global folders#
If the NODE_PATH environment variable is set to a colon-delimited list of absolute paths, then Node.js will search those paths for modules if they are not found elsewhere.
Note: On Windows, NODE_PATH is delimited by semicolons instead of colons.
NODE_PATH was originally created to support loading modules from varying paths before the current module resolutionalgorithm was frozen.
NODE_PATH is still supported, but is less necessary now that the Node.js ecosystem has settled on a convention for locating dependent modules. Sometimes deployments that rely on NODE_PATH show surprising behavior when people are unaware that NODE_PATH must be set. Sometimes a module's dependencies change, causing a different version (or even a different module) to be loaded as the NODE_PATH is searched.
Additionally, Node.js will search in the following locations:
- 1:
$HOME/.node_modules - 2:
$HOME/.node_libraries - 3:
$PREFIX/lib/node
Where $HOME is the user's home directory, and $PREFIX is Node.js's configured node_prefix.
These are mostly for historic reasons.
Note: It is strongly encouraged to place dependencies in the local node_modules folder. These will be loaded faster, and more reliably.
The module wrapper#
Before a module's code is executed, Node.js will wrap it with a function wrapper that looks like the following:
(function(exports, require, module, __filename, __dirname) {
// Module code actually lives in here
});
By doing this, Node.js achieves a few things:
- It keeps top-level variables (defined with
var,constorlet) scoped to the module rather than the global object. - It helps to provide some global-looking variables that are actually specific to the module, such as:
- The
moduleandexportsobjects that the implementor can use to export values from the module. - The convenience variables
__filenameand__dirname, containing the module's absolute filename and directory path.
- The
The module scope#
__dirname#
The directory name of the current module. This the same as the path.dirname() of the __filename.
Example: running node example.js from /Users/mjr
console.log(__dirname);
// Prints: /Users/mjr
console.log(path.dirname(__filename));
// Prints: /Users/mjr
__filename#
The file name of the current module. This is the resolved absolute path of the current module file.
For a main program this is not necessarily the same as the file name used in the command line.
See __dirname for the directory name of the current module.
Examples:
Running node example.js from /Users/mjr
console.log(__filename);
// Prints: /Users/mjr/example.js
console.log(__dirname);
// Prints: /Users/mjr
Given two modules: a and b, where b is a dependency of a and there is a directory structure of:
/Users/mjr/app/a.js/Users/mjr/app/node_modules/b/b.js
References to __filename within b.js will return /Users/mjr/app/node_modules/b/b.js while references to __filenamewithin a.js will return /Users/mjr/app/a.js.
exports#
A reference to the module.exports that is shorter to type. See the section about the exports shortcut for details on when to use exports and when to use module.exports.
module#
A reference to the current module, see the section about the module object. In particular, module.exports is used for defining what a module exports and makes available through require().
require()#
To require modules.
require.cache#
Modules are cached in this object when they are required. By deleting a key value from this object, the next require will reload the module. Note that this does not apply to native addons, for which reloading will result in an Error.
require.extensions#
Stability: 0 - Deprecated
Instruct require on how to handle certain file extensions.
Process files with the extension .sjs as .js:
require.extensions['.sjs'] = require.extensions['.js'];
Deprecated In the past, this list has been used to load non-JavaScript modules into Node.js by compiling them on-demand. However, in practice, there are much better ways to do this, such as loading modules via some other Node.js program, or compiling them to JavaScript ahead of time.
Since the module system is locked, this feature will probably never go away. However, it may have subtle bugs and complexities that are best left untouched.
Note that the number of file system operations that the module system has to perform in order to resolve a require(...)statement to a filename scales linearly with the number of registered extensions.
In other words, adding extensions slows down the module loader and should be discouraged.
require.resolve()#
Use the internal require() machinery to look up the location of a module, but rather than loading the module, just return the resolved filename.
The module Object#
In each module, the module free variable is a reference to the object representing the current module. For convenience, module.exports is also accessible via the exports module-global. module is not actually a global but rather local to each module.
module.children#
The module objects required by this one.
module.exports#
The module.exports object is created by the Module system. Sometimes this is not acceptable; many want their module to be an instance of some class. To do this, assign the desired export object to module.exports. Note that assigning the desired object to exports will simply rebind the local exports variable, which is probably not what is desired.
For example suppose we were making a module called a.js
const EventEmitter = require('events');
module.exports = new EventEmitter();
// Do some work, and after some time emit
// the 'ready' event from the module itself.
setTimeout(() => {
module.exports.emit('ready');
}, 1000);
Then in another file we could do
const a = require('./a');
a.on('ready', () => {
console.log('module a is ready');
});
Note that assignment to module.exports must be done immediately. It cannot be done in any callbacks. This does not work:
x.js:
setTimeout(() => {
module.exports = { a: 'hello' };
}, 0);
y.js:
const x = require('./x');
console.log(x.a);
exports shortcut#
The exports variable is available within a module's file-level scope, and is assigned the value of module.exports before the module is evaluated.
It allows a shortcut, so that module.exports.f = ... can be written more succinctly as exports.f = .... However, be aware that like any variable, if a new value is assigned to exports, it is no longer bound to module.exports:
module.exports.hello = true; // Exported from require of module
exports = { hello: false }; // Not exported, only available in the module
When the module.exports property is being completely replaced by a new object, it is common to also reassign exports, for example:
module.exports = exports = function Constructor() {
// ... etc.
};
To illustrate the behavior, imagine this hypothetical implementation of require(), which is quite similar to what is actually done by require():
function require(/* ... */) {
const module = { exports: {} };
((module, exports) => {
// Module code here. In this example, define a function.
function someFunc() {}
exports = someFunc;
// At this point, exports is no longer a shortcut to module.exports, and
// this module will still export an empty default object.
module.exports = someFunc;
// At this point, the module will now export someFunc, instead of the
// default object.
})(module, module.exports);
return module.exports;
}
module.filename#
The fully resolved filename to the module.
module.id#
The identifier for the module. Typically this is the fully resolved filename.
module.loaded#
Whether or not the module is done loading, or is in the process of loading.
module.parent#
- <Object> Module object
The module that first required this one.
module.paths#
The search paths for the module.
module.require(id)#
The module.require method provides a way to load a module as if require() was called from the original module.
Note: In order to do this, it is necessary to get a reference to the module object. Since require() returns the module.exports, and the module is typically only available within a specific module's code, it must be explicitly exported in order to be used.
本文深入探讨了Node.js的模块加载系统,介绍了如何通过require()加载模块,以及模块的缓存机制、循环依赖处理方式等内容。

7147

被折叠的 条评论
为什么被折叠?



