This time I will bring you a detailed explanation of commonJS. What are the precautions when using commonJS. The following is a practical case, let’s take a look.
commonJS Overview
All code runs in the module scope and will not pollute the global scope.
The module can be loaded multiple times, but it will only be run once when it is loaded for the first time, and then the running results will be cached. When loaded later, the cached results will be read directly. For the module to work again, the cache must be cleared.
The order in which modules are loaded is based on the order in which they appear in the code.
2.Module object
There is a module object inside each module, representing the current module. It has the following properties.
console.log(module.id) //模块的识别符,通常是带有绝对路径的模块文件名。console.log(module.filename) //模块的文件名,带有绝对路径。console.log(module.loaded) //返回一个布尔值,表示模块是否已经完成加载。console.log(module.parent) //返回一个对象,表示调用该模块的模块。console.log(module.children) //返回一个数组,表示该模块要用到的其他模块。console.log(module.exports) //表示模块对外输出的值。
2.1 module.exports attribute
The module.exports attribute indicates the external output interface of the current module. When other files load the module, they actually read the module.exports variable.
2.2 exports variable
For convenience, Node provides an exports variable for each module, pointing to module.exports. This is equivalent to having a line like this at the head of each module:
var exports = module.exports;(commonJS隐式做了这个赋值)
The advantage of this is that when exporting the module interface, you can add methods to the exports object to expose them.
So if you change module.exports, but still want to use export.xxx to expose something, then we have to write exports = module.exports;
李:
module.exports = songthing;//接下来把exports指回来exports = module.exports;//常见写法是:exports = module.exports = something;
3 .Compatibility of AMD specification and CommonJS specification
CommonJS specification loading module is synchronous. Only after the loading is completed, subsequent operations can be performed. The AMD specification is asynchronous loading of modules, allowing the specification of callback functions. Since Node.js is mainly used for server programming, module files generally already exist on the local hard disk, so they can be loaded quickly. There is no need to consider asynchronous loading, so the CommonJS specification is more applicable. However, if it is a browser environment and the module is to be loaded from the server side, the asynchronous mode must be used, so the browser side generally uses the AMD specification.
define(['package/lib'], function(lib){ function foo(){ lib.log('hello world!'); } return { foo: foo }; });
AMD specification allows the output module to be compatible with CommonJS specification. In this case, the define method needs to be written as follows:
define(function (require, exports, module){ var someModule = require("someModule"); var anotherModule = require("anotherModule"); someModule.doTehAwesome(); anotherModule.doMoarAwesome(); exports.asplode = function (){ someModule.doTehAwesome(); anotherModule.doMoarAwesome(); }; });
4.require command
4.1 Basic usage
Node uses the CommonJS module specification, and the built-in require command is used to load module files. The basic function of the
require command is to read and execute a JavaScript file, and then return the exports object of the module. If the specified module is not found, an error will be reported. (To put it bluntly, the values exposed in another file are referenced into this file.)
// example.jsvar invisible = function () { console.log("invisible"); } exports.message = "hi"; exports.say = function () { console.log(message); }
Run the following command to output the exports object.
//someelse.jsvar example = require('./example.js');console.log(example);// {// message: "hi",// say: [Function]// }
If the module outputs a function, it cannot be defined on the exports object, but must be defined on the module.exports variable.
//example2.jsmodule.exports = function () { console.log("hello world") }require('./example2.js')()
In the above code, the require command calls itself, which is equivalent to executing module.exports, so hello world will be output.
4.2 Loading Rules
The require command is used to load files, and the suffix defaults to .js.
var foo = require('foo');// 等同于var foo = require('foo.js');
According to different formats of parameters, the require command searches for module files in different paths:
(1) If the parameter string starts with "/", it means loading is a module file located at an absolute path. For example, require('/home/marco/foo.js') will load /home/marco/foo.js.
(2) If the parameter string starts with "./", it means that a module file located in a relative path (compared to the current location of the script execution) is loaded. For example, require('./circle') will load circle.js in the same directory as the current script.
(3) If the parameter string does not start with "./" or "/", it means that a core module provided by default (located in the system installation directory of Node) or a module located in the node_modules directory at all levels is loaded. of installed modules (globally or locally).
For example, if the script /home/user/projects/foo.js executes the require('bar.js') command, Node will search for the following files in sequence.
/usr/local/lib/node/bar.js /home/user/projects/node_modules/bar.js /home/user/node_modules/bar.js /home/node_modules/bar.js /node_modules/bar.js
The purpose of this design is to enable different modules to localize the modules they depend on.
(4) If the parameter string does not start with "./" or "/" and is a path, such as require('example-module/path/to/file'), example-module will be found first position, and then use it as a parameter to find the subsequent path.
(5) If the specified module file is not found, Node will try to add .js, .json, and .node to the file name before searching. .js files will be parsed as text-formatted JavaScript script files, .json files will be parsed as JSON-formatted text files, and .node files will be parsed as compiled binary files.
(6) If you want to get the exact file name loaded by the require command, use the require.resolve() method.
4.3 Directory loading rules
通常,我们会把相关的文件会放在一个目录里面,便于组织。这时,最好为该目录设置一个入口文件,让require方法可以通过这个入口文件,加载整个目录。
在目录中放置一个package.json文件,并且将入口文件写入main字段。下面是一个例子。
// package.json{ "name" : "some-library", "main" : "./lib/some-library.js" } require发现参数字符串指向一个目录以后,会自动查看该目录的package.json文件,然后加载main字段指定的入口文件。如果package.json文件没有main字段,或者根本就没有package.json文件,则会加载该目录下的index.js文件或index.node文件。
4.4模块的缓存
第一次加载某个模块时,Node会缓存该模块。以后再加载该模块,就直接从缓存取出该模块的module.exports属性。
require('./example.js');require('./example.js').message = "hello";require('./example.js').message// "hello"
上面代码中,连续三次使用require命令,加载同一个模块。第二次加载的时候,为输出的对象添加了一个message属性。但是第三次加载的时候,这个message属性依然存在,这就证明require命令并没有重新加载模块文件,而是输出了缓存。
如果想要多次执行某个模块,可以让该模块输出一个函数,然后每次require这个模块的时候,重新执行一下输出的函数。
所有缓存的模块保存在require.cache之中,如果想删除模块的缓存,可以像下面这样写。
// 删除指定模块的缓存delete require.cache[moduleName];// 删除所有模块的缓存Object.keys(require.cache).forEach(function(key) { delete require.cache[key]; })
注意,缓存是根据绝对路径识别模块的,如果同样的模块名,但是保存在不同的路径,require命令还是会重新加载该模块。
4.5环境变量NODE_PATH
Node执行一个脚本时,会先查看环境变量NODE_PATH。它是一组以冒号分隔的绝对路径。在其他位置找不到指定模块时,Node会去这些路径查找。
可以将NODE_PATH添加到.bashrc。 export NODE_PATH="/usr/local/lib/node"
所以,如果遇到复杂的相对路径,比如下面这样:
var myModule = require('../../../../lib/myModule');
有两种解决方法,一是将该文件加入node_modules目录,二是修改NODE_PATH环境变量,package.json文件可以采用下面的写法。
{ "name": "node_path", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "NODE_PATH=lib node index.js" }, "author": "", "license": "ISC"}
NODE_PATH是历史遗留下来的一个路径解决方案,通常不应该使用,而应该使用node_modules目录机制。
4.6模块的循环加载
如果发生模块的循环加载,即A加载B,B又加载A,则B将加载A的不完整版本。
// a.jsexports.x = 'a1';console.log('a.js ', require('./b.js').x); exports.x = 'a2';// b.jsexports.x = 'b1';console.log('b.js ', require('./a.js').x); exports.x = 'b2';// main.jsconsole.log('main.js ', require('./a.js').x);console.log('main.js ', require('./b.js').x);
上面代码是三个JavaScript文件。其中,a.js加载了b.js,而b.js又加载a.js。这时,Node返回a.js的不完整版本,所以执行结果如下。(也就是说,虽然这样去require看似会造成a.js和b.js循环引用,但commonJS会在将循环的点剪断循环,并对剪断处所在的a.js终止执行,b得到了第一个x值。)
$ node main.js b.js a1 a.js b2 main.js a2 main.js b2
修改main.js,再次加载a.js和b.js。
// main.jsconsole.log('main.js ', require('./a.js').x);console.log('main.js ', require('./b.js').x);console.log('main.js ', require('./a.js').x);console.log('main.js ', require('./b.js').x);
执行上面代码,结果如下。
$ node main.js b.js a1 a.js b2 main.js a2 main.js b2 main.js a2 main.js b2
上面代码中,第二次加载a.js和b.js时,会直接从缓存读取exports属性,所以a.js和b.js内部的console.log语句都不会执行了。
4.7 require.main
require方法有一个main属性,可以用来判断模块是直接执行,还是被调用执行。
直接执行的时候(node module.js),require.main属性指向模块本身。
require.main === module// true
调用执行的时候(通过require加载该脚本执行),上面的表达式返回false。
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
相关阅读:
The above is the detailed content of commonJS detailed explanation. For more information, please follow other related articles on the PHP Chinese website!