Home >Web Front-end >JS Tutorial >Understanding the module system in Node.js

Understanding the module system in Node.js

青灯夜游
青灯夜游forward
2020-11-24 17:58:153288browse

Understanding the module system in Node.js

Related recommendations: "node js tutorial"

Node.js module

JavaScript as a A simple scripting language to add interactive functions to web pages came out. It did not include a module system at the beginning. As JavaScript solved more and more complex problems, writing all codes in one file and using function to distinguish functional units can no longer support the development of complex applications. Yes, ES6 brings classes and modules that are common in most high-level languages, making it easier for developers to organize code

import _ from 'lodash';

class Fun {}

export default Fun;

The above three lines of code show the two most important elements of a module system, import and export

  • export is used to specify the external interface of the module

  • import is used to import the functions provided by other modules

Before ES6, many module loading solutions appeared in the community, the most important ones were CommonJS and AMD. Node.js was born earlier than ES6, and the module system used something similar to CommonJS The implementation follows several principles

  • A file is a module, and the scope of variables in the file is within the module

  • Usemodule.exports Object export module external interface

  • Use require to introduce other modules

circle.js

const { PI } = Math;

module.exports = function area(r) {
  PI * r ** 2;
};

The above code implements a module of Node.js. The module does not depend on other modules and exports the methodarea to calculate the area of ​​a circle

test.js

const area = require('./circle.js');
console.log(`半径为 4 的圆的面积是 ${area(4)}`);

The module relies on circle.js and uses its exposed area method to calculate the area of ​​the circle

module.exports

The external exposed interface of the module uses module.exports. There are two common usages: adding attributes to it or assigning values ​​to new objects.
test.js

// 添加属性
module.exports.prop1 = xxx;
module.exports.funA = xxx;
module.exports.funB = xxx;

// 赋值到全新对象
module.exports = {
  prop1,
	funA,
  funB,
};

The two writing methods are equal to Price, there is no difference when using

const mod = require('./test.js');

console.log(mod.prop1);
console.log(mod.funA());

There is another way to directly use the exports object, but you can only add attributes to it and cannot assign it to a new object. The reason will be introduced later.

// 正确的写法:添加属性
exports.prop1 = xxx;
exports.funA = xxx;
exports.funB = xxx;

// 赋值到全新对象
module.exports = {
  prop1,
	funA,
  funB,
};

require('id')

Module type

require usage is relatively simple, id supports two types: module name and file path

Module name

const fs = require('fs');
const _ = require('lodash');

The fs and lodash in the example are both module names. fs is the built-in core module of Node.js, and lodash is a third-party module installed through npm under node_modules. If there are duplicate names , give priority to using the system’s built-in modules

Because a project may contain multiple node_modules folders (Node.js’ relatively failed design), the third-party module search process will follow the principle of proximity and go up layer by layer (you can find it in the program Print module.paths to view the specific search path) until the file system root directory is found according to the NODE_PATH environment variable. For the specific process, please refer to the official documentation

In addition, Node .js also searches the following global directory listing:

  • $HOME/.node_modules
  • $HOME/.node_libraries
  • $PREFIX/lib/node

$HOME is the user's home directory, $PREFIX is the node_prefix configured in Node.js. It is strongly recommended to place all dependencies in the local node_modules directory, which will load faster and be more reliable

File path

Modules can also be loaded using file paths. This is the project A common loading method for custom modules within the directory. The path extension can be omitted. Modules prefixed with

'/'
    will be tried in the order of .js, .json, and .node.
  • Is the absolute path of the file. Find the module according to the system path.
  • Modules prefixed with './' are relative to the file currently calling the require method, and are not affected by where subsequent modules are used. To affect

Single loading & circular dependency

The module will be cached to Module._cache after the first load, if it is called every time ## If #require('foo') is parsed to the same file, the same object will be returned. Calling require(foo) multiple times at the same time will not cause the module's code to be executed multiple times. Node.js caches modules based on their actual file names, so the same module is not loaded twice when referenced from different levels of directories.

The understood module single loading mechanism facilitates our understanding of the phenomenon of module circular dependencies


a.js

console.log('a 开始');
exports.done = false;
const b = require('./b.js');
console.log('在 a 中,b.done = %j', b.done);
exports.done = true;
console.log('a 结束');

b.js

console.log('b 开始');
exports.done = false;
const a = require('./a.js');
console.log('在 b 中,a.done = %j', a.done);
exports.done = true;
console.log('b 结束');

main.js:

console.log('main 开始');
const a = require('./a.js');
const b = require('./b.js');
console.log('在 main 中,a.done=%j,b.done=%j', a.done, b.done);
When main.js loads a.js, a.js loads b.js. At this time, b.js will Try to load a.js

In order to prevent an infinite loop, an

unfinished copy of the exports object of a.js will be returned to the b.js module, and then b.js will complete the loading. And provide the exports object to the a.js module

so the output of the example is

main 开始
a 开始
b 开始
在 b 中,a.done = false
b 结束
在 a 中,b.done = true
a 结束
在 main 中,a.done=true,b.done=true

看不懂上面的过程也没关系,日常工作根本用不到,即使看懂了也不要在项目中使用循环依赖!

工作原理

Node.js 每个文件都是一个模块,模块内的变量都是局部变量,不会污染全局变量,在执行模块代码之前,Node.js 会使用一个如下的函数封装器将模块封装

(function(exports, require, module, __filename, __dirname) {
	// 模块的代码实际上在这里
});
  • __filename:当前模块文件的绝对路径
  • __dirname:当前模块文件据所在目录的绝对路径
  • module:当前的模块实例
  • require:加载其它模块的方法,module.require 的快捷方式
  • exports:导出模块接口的对象,module.exports 的快捷方式

回头看看最开始的问题,为什么 exports 对象不支持赋值为其它对象?把上面函数添加一句 exports 对象来源就很简单了

const exports = module.exports;
(function(exports, require, module, __filename, __dirname) {
	// 模块的代码实际上在这里
});

其它模块 require 到的肯定是模块的 module.exports 对象,如果吧 exports 对象赋值给其它对象,就和 module.exports 对象断开了连接,自然就没用了

在 Node.js 中使用 ES Module

随着 ES6 使用越来越广泛,Node.js 也支持了 ES6 Module,有几种方法

babel 构建

使用 babel 构建是在 v12 之前版本最简单、通用的方式,具体配置参考 @babel/preset-env(https://babeljs.io/docs/en/babel-preset-env)

.babelrc

{
  "presets": [
    ["@babel/preset-env", {
      "targets": {
        "node": "8.9.0",
        "esmodules": true
      }      
    }]
  ]
}

原生支持

在 v12 后可以使用原生方式支持 ES Module

  • 开启 --experimental-modules

  • 模块名修改为 .mjs (强烈不推荐使用)或者 package.json 中设置 "type": module

这样 Node.js 会把 js 文件都当做 ES Module 来处理,更多详情参考官方文档(https://nodejs.org/dist/latest-v13.x/docs/api/esm.html)

更多编程相关知识,请访问:编程视频!!

The above is the detailed content of Understanding the module system in Node.js. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:cnblogs.com. If there is any infringement, please contact admin@php.cn delete