In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-26 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article focuses on "how to understand Node.js modularization", interested friends may wish to take a look. The method introduced in this paper is simple, fast and practical. Now let the editor take you to learn "how to understand Node.js modularization"!
Text
In Node.js, there are two modules built in for modular management, which are also two keywords that we are very familiar with: require and module. The built-in means that we can use these two modules globally without having to refer to them before using them, as other modules do.
No need for require ('require') or require (' module') to copy the code
It's not hard to reference a module in Node.js, it's simple:
Const config = require ('/ path/to/file') copy the code
But in fact, this simple code performs a total of five steps:
Understanding these five steps will help us understand the fundamentals of Node.js modularization, and it will also allow us to identify some pitfalls and briefly summarize what we have done with these five steps:
Resolving: find the target module to be referenced and generate an absolute path.
Loading: determine the type of module content to be referenced. It may be a .json file, a .js file, or a .node file.
Wrapping: as the name implies, wraps the referenced module. Through packaging, the module has a private scope.
Evaluating: the loaded module is actually parsed and processed.
Caching: cache module, which eliminates the need to repeat the above steps when introducing the same module.
After reading these five steps, some students may already know very well that they are familiar with these principles, and some students may have more doubts in their minds. In any case, the following content will analyze the above implementation steps in detail, hoping to help you answer questions, or, consolidate knowledge, check gaps and make up for gaps.
By the way, if necessary, like me, can build an experimental directory and experiment with Demo.
What is a module?
To understand modularity, you need to take a visual look at what the module is.
We know that in Node.js, files are modules. We just mentioned that modules can be .js, .json, or .node files. By referencing them, you can get utility functions, variables, configuration, and so on, but what is its specific structure? Simply execute the following command on the command line to see the module, that is, the structure of the module object:
~ / learn-node $node > module Module {id:', exports: {}, parent: undefined, filename: null, loaded: false, children: [], paths: [...]} copy the code
You can see that the module is an ordinary object, but there are several special attribute values in the structure that we need to understand one by one. Some attributes, such as id, parent, filename, children, even need not be explained, can be understood literally.
The following content will help you understand the meaning and function of these fields.
Resolving
Now that we have an overview of what a module is, we start with the first step, Resolving, to understand the principle of modularization, that is, how Node.js finds the target module and generates the absolute path of the target module.
So what do we just want to print the module object first, let you understand the structure of module? Because there are two field values id, paths, and Resolving that are closely related. Let's take a look.
The first is the id property:
Each module has an id attribute, which is usually the full path of the module, through which the Node.js can identify and locate the location of the module. But there is no specific module here, we just output the structure of module on the command line, so it is the default value (repl stands for interactive interpreter).
The second is the paths attribute:
What is the purpose of this paths attribute? Node.js allows us to reference modules in a variety of ways, such as relative paths, absolute paths, and preset paths (explained in a moment). Suppose we need to reference a module called find-me, how can require help us find this module?
Require ('find-me') copy the code
Let's first print to see what's in the paths:
~ / learn-node $node > module.paths ['/ Users/samer/learn-node/repl/node_modules','/ Users/samer/learn-node/node_modules','/ Users/samer/node_modules','/ Users/node_modules','/ node_modules','/ Users/samer/.node_modules','/ Users/samer/.node_libraries' '/ usr/local/Cellar/node/7.7.1/lib/node'] copy the code
Ok is actually a bunch of system absolute paths that represent the possible locations of all target modules, and they are ordered, which means that Node.js looks for all paths listed in paths in order, and if it finds this module, it outputs the absolute paths of the module for later use.
Now we know that Node.js will look for module in this pile of directories and try to execute require ('find-me') to find the find-me module. Because we have not placed the find-me module in any directory, Node.js cannot find the target module after traversing all the directories, so you may often see the error Cannot find module' find-me',:
~ / learn-node $node > require ('find-me') Error: Cannot find module' find-me' at Function.Module._resolveFilename (module.js:470:15) at Function.Module._load (module.js:418:25) at Module.require (module.js:498:17) at require (internal/module.js:20:19) at repl:1:1 at ContextifyScript.Script.runInThisContext (vm.js:23 33) at REPLServer.defaultEval (repl.js:336:29) at bound (domain.js:280:14) at REPLServer.runBound [as eval] (domain.js:293:12) at REPLServer.onLine (repl.js:533:10) copy code
Now, you can try to place the find-me module that needs to be referenced in any of the above directories, where we create a node_modules directory and create a find-me.js file so that Node.js can find it:
~ / learn-node $mkdir node_modules ~ / learn-node $echo "console.log ('I am not lost');" > node_modules/find-me.js ~ / learn-node $node > require ('find-me'); I am not lost {} > copy code
After manually creating the find-me.js file, Node.js did find the target module. Of course, when the find-me module is found in Node.js 's local node_modules directory, it will not continue to look in subsequent directories.
Students with Node.js development experience will find that when referencing a module, you do not have to specify an accurate file, but you can also reference the target module by referencing the directory, for example:
~ / learn-node $mkdir-p node_modules/find-me ~ / learn-node $echo "console.log ('Found again.');" > node_modules/find-me/index.js ~ / learn-node $node > require (' find-me'); Found again. {} > copy the code
The index.js file in the find-me directory will be imported automatically.
Of course, there are rules, and Node.js is able to find index.js files in the find-me directory because the default module introduction rule is to look for index.js files when a specific file name is missing. We can also change the incoming rule (by modifying package.json), such as index\-> main:
~ / learn-node $echo "console.log ('I rule');" > node_modules/find-me/main.js ~ / learn-node $echo'{"name": "find-me-folder", "main": "main.js"}'> node_modules/find-me/package.json ~ / learn-node $node > require ('find-me'); I rule {} > copy code
Require.resolve
If you only want to introduce a module into your project and do not want to execute it immediately, you can use the require.resolve method, which is similar to the require method, but does not execute the introduced module method:
> require.resolve ('find-me');' / Users/samer/learn-node/node_modules/find-me/start.js' > require.resolve ('not-there') Error: Cannot find module 'not-there' at Function.Module._resolveFilename (module.js:470:15) at Function.resolve (internal/module.js:27:19) at repl:1:9 at ContextifyScript.Script.runInThisContext (vm.js:23:33) at REPLServer.defaultEval (repl.js:336:29) at bound (domain.js:280:14) at REPLServer.runBound [as eval] (domain.js : 293 at emitOne 12) at REPLServer.onLine (repl.js:533:10) at emitOne (events.js:101:20) at REPLServer.emit (events.js:191:7) > copy code
As you can see, if the module is found, Node.js prints the full path of the module, and if it is not found, it reports an error.
Now that you've seen how Node.js looks for modules, take a look at how Node.js loads modules.
Father-son dependency between modules
We represent the reference relationship between modules as a parent-son dependency relationship.
Simply create a lib/util.js file and add a line of console.log statements to identify that this is a referenced submodule.
~ / learn-node $mkdir lib ~ / learn-node $echo "console.log ('In util');" > lib/util.js copy code
Also enter a line of console.log statement in index.js to identify that this is a parent module and reference the lib/util.js you just created as a child module.
~ / learn-node $echo "require ('. / lib/util'); console.log ('In index, parent', module);" > index.js copy code
Execute index.js to see the dependencies between them:
~ / learn-node $node index.js In util In index Module {id:'.', path:'/ Users/samer/', exports: {}, parent: null, filename:'/ Users/samer/index.js', loaded: false, children: [Module {id:'/ Users/samer/lib/util.js', path:'/ Users/samer/lib', exports: {} Parent: [Circular * 1], filename:'/ Users/samer/lib/util.js', loaded: true, children: [], paths: [Array]}], paths: [...]} copy the code
Here we focus on two attributes related to dependencies: children and parent.
In the printed result, the children field contains the introduced util.js module, indicating that util.js is a submodule on which index.js depends.
But take a closer look at the parent property of the util.js module, we find that the value Circular appears here, because when we print the module information, we have a circular dependency, print the parent module information in the child module information, and print the child module information in the parent module information, so Node.js simply marks its processing as Circular.
Why do you need to understand the father-son dependency? Because this concerns how Node.js handles circular dependencies, it will be described in more detail later.
Before we look at the handling of circular dependencies, we need to understand two key concepts: exports and module.exports.
Exports, module.exports
Exports:
Exports is a special object that can be used as a global variable without declaration in Node.js. It is actually a reference to module.exports, and you can modify the module.exports by modifying the exports.
Exports is also an attribute value in the module structure that you just printed, but the values you just printed are empty objects because we didn't manipulate it in the file, so now we can try to simply assign a value to it:
/ / add a new line at the beginning of lib/util.js exports.id = 'lib/util'; / / add a line at the beginning of index.js exports.id =' index'; copy code
Perform index.js:
~ / learn-node $node index.js In index Module {id:'.', exports: {id: 'index'}, loaded: false,...} In util Module {id:' / Users/samer/learn-node/lib/util.js', exports: {id: 'lib/util'}, parent: Module {id:'., exports: {id: 'index'} Loaded: false,...}, loaded: false,...} copy the code
You can see that the two id attributes you just added were successfully added to the exports object. We can also add any attribute other than id, just as we do with ordinary objects, or we can turn exports into a function, such as:
Exports = function () {} copy the code
Module.exports:
The module.exports object is actually what we end up getting through require. When we write a module, we end up assigning a value to module.exports that others can get when they reference the module. For example, combined with the operation you just did on lib/util:
Const util = require ('. / lib/util'); console.log ('UTIL:', util); / / output result UTIL: {id:' lib/util'} copy code
Since we have just assigned {id: 'lib/util'} to module.exports through the exports object, the result of require has changed accordingly.
Now we have a general idea of what exports and module.exports are, but one small detail to note is that module loading of Node.js is a synchronous process.
Let's go back to the loaded property in the module structure, which identifies whether the module has been loaded or not, which can be used to simply verify the synchronization of the Node.js module load.
When the module is loaded, the loaded value should be true. But so far every time we print module, its state is false, which is actually because in Node.js, module loading is synchronous, when we have not finished loading action (loading action includes tagging module, including marking the loaded attribute), so the printed result is the default loaded: false.
We use setImmediate to help us verify this information:
/ / In index.js setImmediate (() = > {console.log ('The index.js module object is now loadedcards, module)}); copy the code The index.js module object is now loaded! Module {id:'.', exports: [Function], parent: null, filename:'/ Users/samer/learn-node/index.js', loaded: true, children: [Module {id:'/ Users/samer/learn-node/lib/util.js', exports: [Object], parent: [Circular], filename:'/ Users/samer/learn-node/lib/util.js' Loaded: true, children: [], paths: [Object]}], paths: ['/ Users/samer/learn-node/node_modules','/ Users/samer/node_modules','/ Users/node_modules','/ node_modules']} copy the code
Ok, because the console.log is postpositioned after the load is complete (marked), the load state is now loaded: true. This fully verifies that Node.js module loading is a synchronous process.
Now that you understand exports, module.exports, and the synchronization of module loading, take a look at how Node.js handles module cyclic dependencies.
Module circular dependence
In the above, we learned that there is a parent-child dependency between modules, so what will Node.js do if there is a circular dependency between modules? Suppose there are two modules, module1.js and modole2.js, and they refer to each other, as follows:
/ / lib/module1.js exports.a = 1; require ('. / module2'); / / reference here exports.b = 2; exports.c = 3; / / lib/module2.js const Module1 = require ('. / module1'); console.log ('Module1 is partially loaded here', Module1); / / reference module1 and print it to copy the code
Try running module1.js, and you can see the output:
~ / learn-node $node lib/module1.js Module1 is partially loaded here {a: 1} copy the code
As a result, only {a: 1} is output, while {b: 2, c: 3} is missing. Taking a closer look at module1.js, we see that we have added a reference to module2.js in the middle of the module1.js, where it was before exports.b = 2 and exports.c = 3 were executed. If we call this location the location where the circular dependency occurs, then the result we get is the property that is exported before the circular dependency occurs, which is based on our conclusion that the module loading of Node.js is a synchronous process.
This is how Node.js simply handles circular dependencies. During the process of loading the module, the exports object is built step by step to assign a value to exports. If we refer to the module before it is fully loaded, we can only get some of the exports object properties.
.json and .node
In Node.js, we can use require to reference not only JavaScript files, but also JSON or C++ plug-ins (.json and .node files). We don't even need to explicitly declare the corresponding file suffix.
You can also see the file types supported by require on the command line:
~% node > require.extensions [Object: null prototype] {'.js': [Function (anonymous)], '.json': [Function (anonymous)], '.node': [Function (anonymous)]} copy the code
When we use require to reference a module, first Node.js will match whether there is a .js file, if not, then match the .json file, and if not, finally try to match the .node file. In general, however, to avoid confusion and ambiguity of referencing intent, you can follow the practice of explicitly specifying suffixes when referencing .json or .node files, and omitting suffixes (optional, or both) when referencing .js.
.json file:
Referencing .json files is very common, such as static configurations in some projects. Using .json files for storage is more manageable, for example:
{"host": "localhost", "port": 8080} copy code
It's easy to reference it or use it:
Const {host, port} = require ('. / config'); console.log (`Server will run at http://${host}:${port}`) copy code
The output is as follows:
Server will run at http://localhost:8080 copy code
.node file:
The .node file is converted from a C++ file, and the official website provides a simple hello plug-in implemented by C++, which exposes a hello () method that outputs the string world. If necessary, you can jump to the link to learn more and experiment.
We can compile and build .cc files into .node files through node-gyp, and the process is as simple as configuring a binding.gyp file. I'm not going to elaborate here, just know that after you generate the .node file, you can reference the file normally and use the methods in it.
For example, after converting hello () into an addon.node file, reference and use it:
Const addon = require ('. / addon'); console.log (addon.hello ()); copy code
Wrapping
In fact, in the above content, we described the first two steps of referencing a module in Node.js, Resolving and Loading, which solve the problem of module path and loading, respectively. Let's see what Wrapping has done.
Wrapping is the wrapper, and the object of the wrapper is all the code we write in the module. That is, when we quote the module, we actually experience a layer of "transparent" packaging.
To understand this packaging process, you must first understand the difference between exports and module.exports.
Exports is a reference to module.exports, and we can use exports to export properties in the module, but we cannot replace it directly. For example:
Exports.id = 42; / / ok, where exports points to module.exports, which is equivalent to modifying module.exports. Exports = {id: 42}; / / is useless, it just points to the {id: 42} object, and it doesn't actually change the module.exports. Module.exports = {id: 42}; / / ok, directly operate module.exports. Copy the code
You may wonder why this exports object seems to be a global object for each module, but it can tell which module the exported object is from and how it is done.
Before we look at the Wrapping process, let's take a look at a small example:
/ / In a.js var value = 'global' / / In b.js console.log (value) / / output: global / / In c.js console.log (value) / / output: global / / In index.html. Copy the code
When we define a value value in the a.js script, the value is globally visible and can be accessed by both b.js and c.js introduced later. However, this is not the case in the Node.js module. Variables defined in one module have private scope and cannot be accessed directly in other modules. How did this private scope come into being?
The answer is simple, because before compiling the module, Node.js wraps the contents of the module in a function and implements a private scope through the function scope.
The wrapper property can be printed out through require ('module'). Wrapper:
~ $node > require ('module'). Wrapper [' (function (exports, require, module, _ _ filename, _ _ dirname) {','\ n});'] > copy the code
Node.js does not execute any code in the file directly, but it executes the code through the wrapped function, which gives each of our modules a private scope and does not affect each other.
The wrapper function takes five parameters: exports, require, module, _ _ filename, _ _ dirname. We can access and print these parameters directly through the arguments parameter:
~ / learn-node $echo "console.log (arguments)" > index.js ~ / learn-node $node index.js {'0mm: {},' 1seam: {[Function: require] resolve: [Function: resolve], main: Module {id:'., exports: {}, parent: null, filename:'/ Users/samer/index.js' Loaded: false, children: [], paths: [Object]}, extensions: {...}, cache: {'/ Users/samer/index.js': [Object]}}, '2employees: Module {id:'., exports: {}, parent: null, filename:'/ Users/samer/index.js' Loaded: false, children: [], paths: [...]}, '3percent:' / Users/samer/index.js', '4percent:' / Users/samer'} copy the code
Take a brief look at these parameters. The first parameter, exports, is initially empty (no value is assigned). The second and third parameters, require and module, are related to the module we refer to, and they are not global. The fourth and fifth parameters _ _ filename and _ _ dirname represent the file path and directory, respectively.
What the entire wrapped function does is approximately equal to:
Function (require, module, _ _ filename, _ _ dirname) {let exports = module.exports; / / Your Code... Return module.exports;} copy the code
All in all, wrapping is to privatize our module scope, exposing variables or methods for use with module.exports as the return value.
Cache
Caching is easy to understand, so take a look at it through a case study:
Echo 'console.log (`log something.`)' > index.js / / In node repl > require ('. / index.js') log something. {} > require ('. / index.js') {} > copy the code
As you can see, the same module is referenced twice and the information is printed only once, because the second reference is cached and there is no need to reload the module.
Print require.cache to see the current cache information:
> require.cache [Object: null prototype] {'/ Users/samer/index.js': Module {id:'/ Users/samer/index.js', path:'/ Users/samer/', exports: {}, parent: Module {id:', path:'., exports: {}, parent: undefined, filename: null, loaded: false Children: [Array], paths: [Array]}, filename:'/ Users/samer/index.js', loaded: true, children: [], paths: ['/ Users/samer/learn-node/repl/node_modules','/ Users/samer/learn-node/node_modules','/ Users/samer/node_modules' '/ Users/node_modules',' / node_modules','/ Users/samer/.node_modules','/ Users/samer/.node_libraries','/ usr/local/Cellar/node/7.7.1/lib/node']} copy the code
You can see that the index.js file you just referenced is in the cache, so the module will not be reloaded. Of course, we can also clear the cache content by deleting require.cache to achieve the purpose of reloading, which will not be demonstrated here.
At this point, I believe you have a deeper understanding of "how to understand Node.js modularization". You might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.