Code |
As we all know, Node.js is a server-side JavaScript runtime environment based on Chrome V8 engine. It uses an event-driven, non-blocking I/O mode, which is both lightweight and efficient in operation. It is true that we can use a single js file to write all the content involved in the application, but this is neither flexible nor modular. The emergence of Node.js makes it very easy to write modular code. Therefore, for the core of Node.js, an important concept we need to understand and master is: dependency management. This article will discuss with you various modes of dependency management and how Nodejs loads dependencies.
Before diving into the details, let us first figure out what a module is. In short, a module is a piece of code. In order to share and reuse, we need to group the code. Through modules, we can decompose complex applications into small pieces of code. At the same time, modules can also help us understand the intent of the program code and find or fix various errors.
Since 2009, CommonJS has implemented Javascript’s modular specification. It standardizes the characteristics of modules and the interdependence between modules. Since each file is treated as a module (usually, the module variable represents the current module), and has its own scope, so the variables, functions, and classes in each file are private, and for other modules Is invisible. The exports attribute of the module is the external interface. Only the attributes exported through exports can be recognized and loaded by other modules. And Node is based on CommonJs specification to realize module synchronization and loading. In other words, we can receive the module’s identity by calling the require() method in the module, and according to the node’s module introduction rules, introduce other modules, and then call the corresponding properties and methods.
Set up the app:
Let’s start with the simplest. Suppose I have created a directory for a certain project. After initializing it by using the npm init command, we will create app.js and appMsg.js, two JavaScript files. The following figure shows the directory structure of this project, and we use it as the starting point for management. If you are interested, you can download the final source code of the project from the git repository link given at the end of the article. By default, these two .js files are empty. Let us update the appMsgs.js file with the following changes.
The above code snippet shows the usage of the module.exports keyword. This syntax is used to expose attributes or objects in a given file (appMsgs.js here) so that they can be used directly in another file (app.js in this example). In this system, every file can access the file named module.exports. Therefore, we have disclosed some items in the appMsgs.js file to facilitate the observation of how app.js uses (require) certain properties.
Obviously, the required keyword can make it easy for us to refer to a file. In other words, when we execute require, it will return an object representing the modular code segment. Therefore, we can assign it to an appMsgs variable, and then simply use the attribute in the console.log statement. When the code is executed, we will see the following output:
The require constructs an object with a certain functional function by executing JavaScript, and returns it. They can be either a class constructor or objects that contain many elements or some simple attributes. For different modes, we can export multiple objects as well as those complex objects. It can be seen that through require and module.exports, we can create modular applications. It is worth noting that the functional functions required by the application will only load the code once. In other words, no matter what code is executed, they will not be executed a second time. Then, if another program also needs to obtain the object through require, it will only be able to obtain the cached version of the object. Next, let’s take a look at how to export.
As shown in the code snippet above, I made changes to the previous code. Now, instead of publishing objects, I exported a function. This code needs to be executed every time the function is called. Below, let’s take a look at how to use it in the app.js file:
In addition to calling a property, we can also execute it like a function. Therefore, the main difference here is that whenever we execute the code, the code inside the function will be re-executed.
Here is the output of our re-running the code snippet:
So far, we have seen the two modes of module.exports and the difference between the two. Another common pattern is to use it as a constructor method. Below, let us look at another example:
The following is the changed app.js file:
Essentially, this is consistent with creating a pseudo-class in JavaScript and creating various instances of it.
Here is the changed output:
Next, let us continue to discuss another example of this type of pattern. As shown in the following code snippet, I created a new file called userRepo.js.
Below is the changed app.js file.
The following figure is the result of this change:
Of course, it is not common to use require for a single file. Next, let us discuss another mode-folder dependency.
Folder dependency:
To understand how Node.js finds dependencies, let’s revisit the JavaScript code in the previous example:
var appMsgs = require(“ ./appMsgs”)
Node will not only look for the appMsgs.js file, but also look for appMsgs as a directory, and retrieve its value. I created a folder named logger and created an index.js file in it, the content of which is shown in the following code snippet:
Below is the app.js file that requires this module:
It can be seen that in this example, we can write JavaScript code like this:
var logger = require(“./logger/index.js”)
The above-mentioned longer path form is definitely correct. However, we actually only need to write the following JavaScript code:
var logger = require(“./logger”)
Since there is no logger.js, but only the logger directory, by default, Node will load index.js as the starting point for logger. We can verify the output result through the following command:
Here, you may have doubts in mind: Why did we so exhausted to create folders and index.js? The reason behind it is: You may put some complex dependencies together, and these dependencies may also There are other dependencies. For callers who need loggers, they don’t need to know the existence of other dependencies. This is a form of encapsulation. We can build more complex code segments in multiple files; from the consumer’s point of view, they only need to use one file. It can be seen that folders are a better way to manage such dependencies.
Node Package Manager (NPM):
The third type of dependency management that is worth discussing is NPM. As the name suggests, NPM is a Node.js package management and distribution tool, which is equivalent to the back-end Maven. It allows Javascript developers to share and share code snippets more easily.
Usually, we can use the following npm command to install dependencies:
npm install underscore;
As shown in the following code snippet, we can also simply require it in app.js:
As you can see, we can use various functions through the underscore software package. In the same way, when we need to use this type of module, we did not specify the path of the file, but just use its name. Node.js will be automatically loaded into its corresponding module from the node_modules folder of your application.