
Front-end (vue) entry-to-mastery course: Entering into learning
Nowadays, front-end development students cannot do without npm , a package management tool. Its excellent package version management mechanism carries the entire prosperous NodeJS community. It is very useful to understand its internal mechanism. It helps to deepen our understanding of module development and various front-end engineering configurations to speed up our troubleshooting (I believe many students have been troubled by various dependency issues).
This article conducts a detailed analysis of npm 's package management mechanism from three perspectives: package.json , version management, dependency installation, and specific examples.

In Node.js , a module is a library or framework and also a Node.js project. The Node.js project follows a modular architecture. When we create a Node.js project, it means creating a module. This module must have a description file, namely package.json . It is our most common configuration file, but have you really understood the configuration in it in detail? Configuring a reasonable package.json file directly determines the quality of our project, so first we will analyze the detailed configuration of package.json .
There are many attributes in package.json , of which only two must be filled in: name and version . These two attributes form the unique identifier of an npm module.
name is the module name. When naming, you need to follow some official specifications and recommendations:
the package name will become a parameter in the module url , the command line, or a folder name. Any non- url safe characters are in the package name. Neither can be used. You can use validate-npm-package-name package to check whether the package name is legal.
Semantic package names can help developers find the required packages faster and avoid accidentally obtaining the wrong package.
If there are some symbols in the package name, the symbols must not be repeated with the existing package name after removing them.
For example: since react-native already exists, react.native and reactnative cannot be created again.
For example: the username is conard , then the scope is @conard , and the published package can be @conard/react .
name is the unique identifier of a package and must not be repeated with other package names. We can execute npm view packageName to see whether the package is occupied, and we can view some basic information about it:

If the package name has never been used, a 404 error will be thrown:

In addition, you can also go to https://www.npmjs.com/ for more detailed package information.
{
"description": "An enterprise-class UI design language and React components implementation",
"keywords": [
"ant",
"component",
"components",
"design",
"framework",
"frontend",
"react",
"react-component",
"ui"
]
} description is used to add module description information to facilitate others to understand your module.
keywords is used to add keywords to your module.
Of course, they also play a very important role, which is to facilitate module retrieval. When you use npm search to retrieve a module, it will match description and keywords . Writing a good description and keywords will help your module get more and more accurate exposure:

to describe developers: author and contributors . author refers to the main author of the package, and one author corresponds to one person. contributors refers to contributor information. One contributors corresponds to multiple contributors. The value is an array. The description of the person can be a string or the following structure:
{
"name" : "ConardLi",
"email" : "[email protected]",
"url" : "https://github.com/ConardLi"
} {
"homepage": "http://ant.design/",
"bugs": {
"url": "https://github.com/ant-design/ant-design/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/ant-design/ant-design"
},
} homepage is used to specify the homepage of this module.
repository is used to specify the code repository of the module.

bugs specifies an address or an email where people who have questions about your module can go here to raise questions.
Our project may depend on one or more external dependency packages. According to the different uses of the dependency packages, we configure them under the following attributes: dependencies、devDependencies、peerDependencies、bundledDependencies、optionalDependencies .
Before introducing several dependency configurations, first let's take a look at the dependency configuration rules. The dependency package configuration you see may be as follows:
"dependencies": {
"antd": "ant-design/ant-design#4.0.0-alpha.8",
"axios": "^1.2.0",
"test-js": "file:../test",
"test2-js": "http://cdn.com/test2-js.tar.gz",
"core-js": "^1.1.5",
} Dependency configuration follows the following configuration rules:
依赖包名称:VERSION VERSION is a version number configuration that follows SemVer specification. When npm install it will go to the npm server to download packages that meet the specified version range.依赖包名称:DWONLOAD_URL DWONLOAD_URL is a downloadable tarball compressed package address. When the module is installed, this .tar will be downloaded and installed locally.依赖包名称:LOCAL_PATH LOCAL_PATH is a local dependency package path, such as file:../pacakges/pkgName . Suitable for you to test an npm package locally, this method should not be applied online.依赖包名称:GITHUB_URL GITHUB_URL is the writing method of github 's username/modulename , for example: ant-design/ant-design . You can also specify tag and commit id later.依赖包名称:GIT_URL GIT_URL is git url of our usual clone code base, which follows the following form:<protocol>://[<user>[:<password>]@]<hostname>[:<port>][: ][/]<path>[#<commit-ish> | #semver:<semver>]
protocal can be in the following forms:
git://github.com/user/project.git#commit-ishgit+ssh://user@hostname:project.git#commit-ishgit+ssh://user@hostname/project.git#commit-ishgit+http://user@hostname/project/blah.git#commit-ishgit+https://user@hostname/project/blah.git#commit-ishdependencies specify the modules on which the project depends. Both the development environment and the production environment dependency modules can be configured here, such as
"dependencies": {
"lodash": "^4.17.13",
"moment": "^2.24.0",
} There are some packages in that you may only use in the development environment, such as eslint for checking code specifications and jest for testing. When users use your package, it can run normally even without installing these dependencies. On the contrary Installing them will take more time and resources, so you can add these dependencies to devDependencies . These dependencies will still be installed and managed when you perform npm install locally, but will not be installed into the production environment:
"devDependencies" : {
"jest": "^24.3.1",
"eslint": "^6.1.0",
} peerDependencies are used to specify the version on which the module you are developing depends on and the compatibility of the dependent package version installed by the user.
The above statement may be a bit too abstract. Let's take ant-design as an example. package.json of ant-design has the following configuration:
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
} When you are developing a system and using ant-design , you definitely need to rely on React . At the same time, ant-design also needs to rely on React . React version it needs to maintain stable operation is 16.0.0 , and the React version you rely on when developing is 15.x :
At this time, ant-design needs to use React , and Import it:
import * as React from 'react'; import * as ReactDOM from 'react-dom';
What you get at this time is the host environment, which is the React version in your environment, which may cause some problems. In npm2 , specifying peerDependencies above will mean forcing the host environment to install versions of react@>=16.0.0和react-dom@>=16.0.0 .
In the future, npm3 will no longer require dependency packages specified by peerDependencies to be forcibly installed. On the contrary npm3 will check whether the installation is correct after the installation is completed. If it is incorrect, a warning will be printed to the user.
"dependencies": {
"react": "15.6.0",
"antd": "^3.22.0"
} For example, I rely on the latest version of antd in the project, and then rely on the 15.6.0 version of react . The following warning will be given when installing the dependency:

In some scenarios, the dependent package may not be a strong dependency. The function of this dependent package is dispensable. When this dependent package cannot be obtained, you want npm install to continue running without causing failure. You can This dependency is placed in optionalDependencies . Note that the configuration in optionalDependencies will override dependencies so it only needs to be configured in one place.
Of course, when referencing dependencies installed in optionalDependencies , exception handling must be done, otherwise an error will be reported when the module cannot be obtained.
is different from the above. The value of bundledDependencies is an array. Some modules can be specified in the array. These modules will be packaged together when this package is released.
"bundledDependencies": ["package1" , "package2"]
{
"license": "MIT"
} The license field is used to specify the open source agreement of the software. The open source agreement details the rights that others have after obtaining your code, what operations they can perform on your code, and what operations are prohibited. There are many variants of the same agreement. An agreement that is too loose will cause the author to lose many rights to the work, while an agreement that is too strict will not be convenient for users to use and the dissemination of the work. Therefore, open source authors must consider which rights they want to retain and which restrictions they want to loosen. .
Software agreements can be divided into two categories: open source and commercial. For commercial agreements, also called legal statements and license agreements, each software will have its own set of text, written by the software author or a specialized lawyer. For most people, there is no need to do it yourself. Spend time and effort writing lengthy license agreements. Choosing a widely circulated open source license is a good choice.
The following are several mainstream open source protocols:

MIT : As long as users include a copyright notice and a license notice in their copies of the project, they can do whatever they want with your code without any responsibility on your part.Apache : Similar to MIT , but also includes terms related to patent licensing provided by contributors to users.GPL : Users who modify the project code must publish their relevant modifications when redistributing source code or binary code.If you have more detailed requirements for the open source agreement, you can go to choosealicense.com/ for a more detailed description of the open source agreement.

{
"main": "lib/index.js",
} The main attribute can specify the main entry file of the program. For example, the module entry lib/index.js specified by antd above. When we introduce antd in the code: import { notification } from 'antd'; in fact, what is introduced is lib/index.js Modules exposed in lib/index.js .

When your module is a command line tool, you need to specify an entry for the command line tool, that is, specify the correspondence between your command name and the local specifiable file. If installed globally, npm will use a symbolic link to link the executable file to /usr/local/bin . If installed locally, it will link to ./node_modules/.bin/ .
{
"bin": {
"conard": "./bin/index.js"
}
} For example, the above configuration: When your package is installed globally: npm will create a soft link named conard under /usr/local/bin , pointing to "./bin/index.js" under the globally installed conard package. "./bin/index.js" . At this time, if you execute conard on the command line, the linked js file will be called.
I won’t go into too much detail here, more content will be explained in detail in my subsequent command line tool articles.
{
"files": [
"dist",
"lib",
"es"
]
} The files attribute is used to describe the list of files that you push to the npm server after npm publish . If you specify a folder, all the contents in the folder will be included. We can see that the downloaded package has the following directory structure:

In addition, you can also configure a
.npmignorefile to exclude some files to prevent a large number of junk files from being pushed tonpm. The rules are the same as.gitignoreyou use..gitignorefiles can also act as.npmignorefiles.
The man command is a help command under Linux . Through the man command, you can view command help, configuration file help, programming help and other information in Linux .
If your node.js module is a global command line tool, you can specify the document address searched by the man command through the man attribute in package.json .
man files must end with a number or, if compressed, .gz . The number indicates which part of man the file will be installed into. If the man file name does not start with the module name, the module name will be prefixed during installation.
For example, the following configuration:
{
"man" : [
"/Users/isaacs/dev/npm/cli/man/man1/npm-access.1",
"/Users/isaacs/dev/npm/cli/man/man1/npm-audit.1"
]
} Enter man npm-audit on the command line:

A node.js module is implemented based on CommonJS modular specification. In strict accordance with the CommonJS specification, in addition to the package description file package.json , the module directory also needs to contain the following directories:
bin : where executable binary files are stored. Directorylib : Directory for storing js codedoc : Directory for storing documentstest : Directory for storing unit test case codeIn the module directory, you may not strictly follow the above structure to organize or name it. You can specify directories in package.json Properties to specify how your directory structure corresponds to the canonical structure above. Apart from this, the directories attribute has no other applications for the time being.
{
"directories": {
"lib": "src/lib/",
"bin": "src/bin/",
"man": "src/man/",
"doc": "src/doc/",
"example": "src/example/"
}
} However, the official document states that although this attribute currently has no important role, some tricks may be developed in the future. For example, markdown files stored in doc and example files stored in example may be displayed in a friendly manner.
{
"scripts": {
"test": "jest --config .jest.js --no-cache",
"dist": "antd-tools run dist",
"compile": "antd-tools run compile",
"build": "npm run compile && npm run dist"
}
} scripts is used to configure the abbreviations of some script commands. Each script can be used in combination with each other. These scripts can cover the entire project life cycle. After configuration, they can be called using npm run command . If it is an npm keyword, it can be called directly. For example, the above configuration specifies the following commands: npm run test , npm run dist , npm run compile , npm run build .
The config field is used to configure the environment variables used in the script. For example, the following configuration can be obtained using process.env.npm_package_config_port in the script.
{
"config" : { "port" : "8080" }
} If your node.js module is mainly used to install global command line tools, then this value is set to true , and users will get a warning when they install the module locally. This configuration will not prevent users from installing, but will prompt users to prevent incorrect use that may cause some problems.
If the private attribute is set to true , npm will refuse to publish it. This is to prevent a private module from being published accidentally.

"publishConfig": {
"registry": "https://registry.npmjs.org/"
}, more detailed configuration when publishing the module, for example, you can configure to publish only a certain tag , configure the private npm source to publish to.
more detailed configuration, please refer to npm-config
If you develop a module that can only run under the darwin system, you need to ensure that windows users will not install your module to avoid unnecessary errors.
Using the os attribute can help you accomplish the above requirements. You can specify that your module can only be installed on certain systems, or specify a blacklist of systems that cannot be installed:
"os" : [ "darwin", "linux" ] "os" : [ "!win32" ]
For example, I assign a test module to a system blacklist: "os" : [ "!darwin" ] . When I install it under this system, the following error will pop up:

In the node environment, you can use process.platform to determine the operating system.
is similar to os above. We can use the cpu attribute to more accurately limit the user's installation environment:
"cpu" : [ "x64", "ia32" ] "cpu" : [ "!arm", "!mips" ]
In the node environment, you can use process.arch to determine the CPU architecture.
The success of Nodejs is inseparable from npm ’s excellent dependency management system. Before introducing the entire dependency system, you must understand how npm manages the versions of dependent packages. This chapter will introduce the version release specifications of npm包, how to manage the versions of various dependent packages, and some best practices regarding package versions.

You can execute npm view package version to view the latest version of a package .
Execute npm view conard versions to view all published versions of a package on the npm server.

Execute npm ls to view the version information of all packages in the current warehouse dependency tree.

Module versions in npm包need to follow SemVer specification - a guiding, unified version number representation rule drafted by Github . It is actually the abbreviation of Semantic Version .
SemVer specification official website: https://semver.org/Standard
The standard version number of SemVer specification adopts the format of XYZ , where X, Y and Z are non-negative integers, and zero padding in front of the numbers is prohibited. X is the major version number, Y is the minor version number, and Z is the revision number. Each element must be numerically incremented.
major ): When you make incompatible API modificationsminor ): When you make backward compatible functionalities Newpatch ): When you make backward compatibility issues Correction.For example: 1.9.1 -> 1.10.0 -> 1.11.0
When a version has major changes, is not stable, and may not meet expected compatibility requirements, you may want to release an advance version first.
The leading version number can be added to the end of "major version number. minor version number. revision number". First add a connection number and then a series of identifiers and version compilation information separated by periods.
alpha ):beta ):rc : Release candiateLet’s take a look at the historical versions of React :

It can be seen that the version is released strictly in accordance with SemVer specification:
主版本号.次版本号.修订号The naming16.8.0 -> 16.8.1 -> 16.8.2alpha , beta , rc and other advanced versions. After modifying certain functions of npm package, it is usually necessary to release a new version. Our usual approach is to directly modify package.json to the specified version. If the operation is incorrect, it is easy to cause confusion in the version number. We can use commands that comply with Semver specification to complete this operation:
npm version patch : upgrade the revision numbernpm version minor : upgrade the minor version numbernpm version major : upgrade the major version numberin development is definitely indispensable for the operation of some version numbers. If these version numbers comply with the SemVer specification, we can use the npm package semver for operating versions to help us compare version sizes, extract version information, and other operations.
Npm also uses this tool to handle versioning work.
npm install semver
semver.gt('1.2.3', '9.8.7') // false
semver.lt('1.2.3', '9.8.7') // true semver.valid('1.2.3') // '1.2.3'
semver.valid('abc') // null semver.valid(semver.coerce('v2')) // '2.0.0'
semver.valid(semver.coerce('42.6.7.9.3-alpha')) // semver.clean(' =v1.2.3 ') // '1.2.3'
semver.satisfies('1.2.3', '1.x || >=2.5.0 || 5.0.0 - 7.2.3') // true
semver.minVersion('>=1.0.0') // '1.0.0' The above are the most common uses of semver. For more details, please view the semver documentation: https://github.com/npm/node-semver
We often see different ways of writing various dependencies in package.json :
"dependencies": {
"signale": "1.4.0",
"figlet": "*",
"react": "16.x",
"table": "~5.4.6",
"yargs": "^14.0.0"
} The first three are easy to understand:
"signale": "1.4.0" : Fixed version number"figlet": "*" : Any version ( >=0.0.0 )"react": "16.x" : Match main Version ( >=16.0.0 <17.0.0 )"react": "16.3.x" : Match major version and minor version ( >=16.3.0 <16.4.0 )Let's take a look at the last two, the version number The ~ and ^ symbols are quoted:
~ : When a new version is obtained when installing dependencies, install the latest version of z in xyz . That is, while keeping the major version number and minor version number unchanged, the latest revision number is maintained.^ : When a new version is obtained when installing dependencies, both y and z in xyz installed are the latest versions. That is, while keeping the major version number unchanged, keep the minor version number and revision number as the latest version.The most common dependency in the package.json file should be "yargs": "^14.0.0" , because when we use npm install package to install the package, npm installs the latest version by default, and then installs it in the Add a ^ sign before the version number.
Note that when the major version number is 0 , it will be considered an unstable version. The situation is different from the above:
0 : ^0.0.z and ~0.0.z are both regarded as fixed versions. , no changes will occur when installing dependencies.0 : ^0.yz behaves the same as ~0.yz , only the revision number is kept as the latest version.The version number 1.0.0 is used to define the public API. When your software is released to the official environment, or has a stable API, you can release version 1.0.0. So, when you decide to release an official version of an npm package to the outside world, mark its version as 1.0.0.
In actual development, strange problems often occur due to inconsistencies in various dependencies, or in some scenarios, we do not want dependencies to be updated. It is recommended to use package-lock.json during development.
Locking the dependency version means that unless we perform updates manually, the fixed version will be installed every time we install the dependency. Ensure that the entire team uses dependencies with consistent version numbers.
Each time you install a fixed version, there is no need to calculate the dependency version range, which can greatly speed up the dependency installation time in most scenarios.
When using package-lock.json, make sure that the npm version is above 5.6, because between 5.0 and 5.6, the processing logic of package-lock.json was updated several times, and the post-processing logic of version 5.6 gradually stabilized.
We will analyze the detailed structure of package-lock.json in later chapters.
Our purpose of
In actual development scenarios, although we do not need to install a new version every time, we still need to upgrade dependency versions regularly so that we can enjoy the problem fixes, performance improvements, and new feature updates brought about by dependency package upgrades.

Using npm outdated can help us list the dependencies that have not been upgraded to the latest version:
and execute npm update . Upgrade all red dependencies.
1.0.0 .主版本号.次版本号.修订号alpha、beta、rc and other advanced versions first.npm packages developed by team members. At this time, it is recommended to change the version prefix ~ . If it is locked, the dependencies of the main project must be upgraded every time the sub-dependencies are updated, which is very cumbersome. If the sub-dependencies are completely Trust, open directly ^ Upgrade to the latest version every time.docker line, and sub-dependencies are still being developed and upgraded locally. Before the docker version is released, all dependency versions must be locked to ensure that there will be no problems online after the local sub-dependencies are released.npm version is above 5.6 , and ensure that the package-lock.json file is enabled by default.npm inatall is executed by the initialization member, package-lock.json is submitted to the remote warehouse. Do not submit node_modules directly to the remote repository.npm update to upgrade dependencies, and submit the lock file to ensure that other members update their dependencies simultaneously. Do not change the lock file manually.package.json file and execute npm installnpm install package@version (changing package.json will not downgrade the dependencies).lock file after changing the dependencies.
npm install will probably go through the above processes. This chapter will talk about the implementation details, development and why of each process.
We all know that after executing npm install , dependent packages are installed into node_modules . Let’s take a closer look at the specific mechanism by which npm installs dependent packages into node_modules .
In the early versions of npm , npm way of handling dependencies was simple and crude. It installed dependencies into their respective node_modules in a recursive manner and strictly according to the package.json structure and the package.json structure of sub-dependency packages. Until there are sub-dependent packages that no longer depend on other modules.
For example, our module my-app now depends on two modules: buffer and ignore :
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
}
} ignore is a pure JS module that does not depend on any other modules, and buffer depends on the following two modules: base64-js and ieee754 .
{
"name": "buffer",
"dependencies": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
}
} Then, after executing npm install , the module directory structure in node_modules obtained is as follows:

The advantages of this approach are obvious. The structure of node_modules corresponds to the structure of package.json one-to-one, the hierarchical structure is obvious, and the directory structure of each installation is guaranteed to be the same.
However, just imagine, if you depend on a lot of modules, your node_modules will be very large and the nesting level will be very deep:

Windows systems, the maximum file path length is 260 characters, and too deep a nesting level may cause unpredictable problems.In order to solve the above problems, NPM made a major update in version 3.x It changes the early nested structure to a flat structure:
node_modules root directory.Still with the above dependency structure, we will get the following directory structure after executing npm install :


At this time, if we rely on the [email protected] version in the module:
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
"base64-js": "1.0.1",
}
} node_modules of the current module.At this point, we will get the following directory structure after executing npm install :


Correspondingly, if we reference a module in the project code, the module search process is as follows:
node_modulesnode_modules module.node_modulesdepends on a package buffer2@^5.4.3 , and it depends on the package [email protected] , the installation structure at this time is as follows:

Therefore, npm 3.x version does not completely solve the module redundancy problem of the old version, and may even bring new problems.
Imagine that your APP does not depend on [email protected] version, but you also depend on buffer and buffer2 that depend on different base64-js versions. Since when executing npm install , the dependencies in package.json are parsed in order, the order in which buffer and buffer2 are placed in package.json determines the dependency structure of node_modules :
depend on buffer2 first:

Depend on buffer first:

In addition, in order to allow developers to use the latest dependency packages under the premise of safety, we usually only lock the large version in package.json , which means that after the minor version of some dependency packages is updated, the dependency structure may also be changed. Uncertainty in dependency structures may cause unpredictable problems for programs.
In order to solve the uncertainty problem of npm install , the package-lock.json file was added in npm 5.x version, and the installation method also follows the flat method of npm 3.x
The function of package-lock.json is to lock the dependency structure. That is, as long as there is a package-lock.json file in your directory, the node_modules directory structure generated after each execution of npm install must be exactly the same.
For example, we have the following dependency structure:
{
"name": "my-app",
"dependencies": {
"buffer": "^5.4.3",
"ignore": "^5.1.4",
"base64-js": "1.0.1",
}
} package-lock.json generated after executing npm install is as follows:
{
"name": "my-app",
"version": "1.0.0",
"dependencies": {
"base64-js": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.0.1.tgz",
"integrity": "sha1-aSbRsZT7xze47tUTdW3i/Np+pAg="
},
"buffer": {
"version": "5.4.3",
"resolved": "https://registry.npmjs.org/buffer/-/buffer-5.4.3.tgz",
"integrity": "sha512-zvj65TkFeIt3i6aj5bIvJDzjjQQGs4o/sNoezg1F1kYap9Nu2jcUdpwzRSJTHMMzG0H7bZkn4rNQpImhuxWX2A==",
"requires": {
"base64-js": "^1.0.2",
"ieee754": "^1.1.4"
},
"dependencies": {
"base64-js": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz",
"integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g=="
}
}
},
"ieee754": {
"version": "1.1.13",
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz",
"integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg=="
},
"ignore": {
"version": "5.1.4",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.4.tgz",
"integrity": "sha512-MzbUSahkTW1u7JpKKjY7LCARd1fU5W2rLdxlM4kdkayuCwZImjkpluF9CM1aLewYJguPDqewLam18Y6AU69A8A=="
}
}
} Let’s take a closer look at the above structure:

The two outermost attributes name and version are the same as name and version in package.json , and are used to describe the current package name and version.
dependencies is an object, which corresponds to the package structure in node_modules . key of the object is the package name, and the value is some description information of the package:
version : package version - the version of this package currently installed in node_modulesresolved : package specific Installation sourceintegrity : package hash value, based on Subresource Integrity to verify whether the installed software package has been modified or invalid.requires : the dependency corresponding to the sub-dependency, which is the same as dependencies in the sub-dependency package.json .dependencies : The structure is the same as the outer dependencies structure, and stores the dependency packages installed in the sub-dependency node_modules .Note here that not all sub-dependencies have the dependencies attribute. This attribute will only appear after the dependencies of the sub-dependencies conflict with the dependencies currently installed in node_modules in the root directory.
For example, review the dependencies above:

The [email protected] version we rely on in my-app conflicts with base64-js@^1.0.2 we rely on in buffer , so [email protected] needs to be installed in node_modules of the buffer package, corresponding to The dependencies attribute of buffer in package-lock.json has been changed. This also corresponds to npm 's flat approach to dependencies.
Therefore, according to the above analysis, package-lock.json file and node_modules directory structure are in one-to-one correspondence. That is, the existence of package-lock.json in the project directory can keep the dependency directory structure generated by each installation the same.
In addition, the use of package-lock.json in the project can significantly speed up the dependency installation time.
We use the npm i --timing=true --loglevel=verbose command to see the complete process of npm install . Let's compare the difference between using a lock file and not using a lock file. Clean the npm cache before comparing.
Without using lock file:

Use lock file:

It can be seen that the specific version and download link of each package have been cached in package-lock.json . There is no need to go to the remote warehouse to query, and then directly enter the file integrity verification process, which reduces a large number of network requests.
to develop system applications, it is recommended to submit the package-lock.json file to the code version repository to ensure that the dependency versions installed by all team developers and CI links are consistent when executing npm install .
When developing a npm package, your npm package needs to be dependent on other warehouses. Since the flat installation mechanism we mentioned above, if you lock the dependent package version, your dependency package cannot share the same semver as other dependent packages with other dependent packages. The dependency package within the range will cause unnecessary redundancy. So we should not publish the package-lock.json file ( npm will not publish package-lock.json file).
After the execution of npm install or npm update command download dependencies, in addition to installing the dependent package in the node_modules directory, it will also cache a local cache directory.
You can query through npm config get cache command: the .npm/_cacache directory under Linux or Mac default.
There are two directory in this directory: content-v2 , index-v5 , content-v2 directory is used to store the cache of tar package, and index-v5 directory is used to store hash of tar package.
When the NPM is installed, you can generate a unique key record in index-v5 Integrity, Version, tar Name hash in hash integrity、version、name stored in package-lock.json The cache tar package is directly used.
We can find a bag in the cache directory and search for testing, search for package path in index-v5 :
GREP "https://registry.npmjs.org/base64-js//base64-js-1.0.tgz" "" "" "" "" "" "" "" -R index-v5

Then we formatted JSON:
{
"Key": "Pacote: Version-Manifest: https: //regotion.npmjs.org/base64-js/-/base64-js-1.0.tgz: Sha1-sbrSZT7TUTDW3I/NP+Pag =",
"Integrity": "SHA512-C2Ekhxwxvlsbrucjtrs3XFHV7MF/Y9KLMKDXPTE8YEVCOH5H8AE69Y+/LP+AHPW91 CRNZGO78E6APJFIQ ==",,
"time": 15755554308857,
"size": 1,
"Metadata": {
"id": "[email protected]",,
"manifest": {
"name": "base64-js",
"Version": "1.0.1",
"ENGINES": {
"Node": "> = 0.4"
},
"Dependencies": {},
"OptionalDependencies": {},
"devDependencies": {
"Standard": "^5.2.2",,
"TAPE": "4.x"
},
"bundledependencies": false,
"peerDependencies": {},
"deprecated": false, false,
"_Resolved": "https://registry.npmjs.org/base64-js//base64-js-1.0.1.tgz",,
"_INTEGRITY": "SHA1-ASBRSZT7XZE47TUTDW3I/NP+Pag =",
"_shasum": "6926D1B194FBC737B8ED513756DE2FCDA7EA408",,
"_Shrinkwrap": null,
"bin": null,
"_id": "[email protected]"
},
"Type": "Finalized-Manifest"
}
} The _shasum attributes above 6926d1b194fbc737b8eed513756de2fcda7ea408 is hash of tar package. The first few 6926 of hash is the first two layers of cache.

The above cache strategy starts with the NPM V5 version. Before the NPM V5 version, the module of each cache is directly stored in the form of a module name in the ~/.npm folder. The storage structure is {cache}/{name}/ {Version}.
npm provides several commands to manage the cache data:
npm cache add : The official explanation said that this command is mainly used in npm , but it can also be used to manually add cache to a specified Package.npm cache clean : Delete all the data in the cache directory. In order to ensure the integrity of the cache data, you need to add the --force parameter.npm cache verify : Verify the effectiveness and integrity of the cache data, and clean up the spam data.Based on the cache data, the NPM provides offline installation mode, which are the following:
--prefer-offline : Priority to use cache data. If there is no matched cache data, download from the remote warehouse.--prefer-online : Using network data first. If the network data request fails, and then request the cache data, this mode can get the latest module in time.--offline : Do not request the network, use the cache data directly. Once the cache data does not exist, the installation fails.We mentioned the completeness of the file many times. So what is the file integrity verification?
Before downloading the dependency package, we can generally hash hash value calculated by npm on the dependent package. For example, we execute the npm info command and follow shasum tarball (download link).

After the user download dependency package is located, it is necessary to determine that there is no error during the download process, so after the download is completed, you need to calculate hash value of the file locally. If the two hash values are the same, it is ensured that the dependency of downloading is complete If different, download it again.
is good, let's summarize the process above:
Check the .npmrc file: priority is: project -level .npmrc file> User -level .npmrc file> Global .npmrc file> NPM built -in .npmrc
There is no lock file in the
file
No lock file:
npm remote warehousepackage.json , and build a process: node_modules root directory.node_modules of the current module.npm Remote warehouse download packagenpm cache directory tonode_modulesnode_modulesnode_moduleslock filewith lock file:
package.json is conflict with the dependencies in package-lock.json .
The above process briefly describes the approximate process of npm install . This process also contains some other operations, such as performing some life cycle functions you defined. You can execute npm install package --timing=true --loglevel=verbose to view view Specific installation process and details of a bag.

yarn was released in 2016 At that time, npm was still in V3 period. At that time, there was no package-lock.json file, just like the disadvantages we mentioned above: instability, slow installation speed and other shortcomings were often subject to major developers. Make complaints. At this time, yarn was born:

The above is the advantages of yarn mentioned on the official website, which was very attractive at that time. Of course, npm later realized its own problems and made many optimizations. In the later optimization ( lock files, cache, default-s ...), we can see the shadow of yarn yarn The design is still very good.
yarn also uses the flat structure of npm v3 to manage dependencies. After the installation dependencies, a yarn.lock file will be generated by default, or the above dependencies will be generated. Let's take a look at the structure of yarn.lock :
# this is an autogenerated file. Do not Edit this file directly. # Yarn Lockfile V1 [email protected]: version "1.0.1" resolution "https://registry.yarnpkg.com/base64-js/-/base64-js-1.0.1.tgz 6926d1bc737513756DE2FCDA7EA408" " Integrity Sha1-AsbrSZT7XZE47TUTDW3I/NP+Pag = base64-js@^ 1.0.2: version "1.3.1" reSolved "https://registry.yarnpkg.com/base64-js//base64-js-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JS-JTG Integrity Sha512-MLQ4I2QO1YTVGWFWMCNGKO // jxaquezvwektjgqfm4jik0ku+YTMFPLL8J+N5MSPOFJHWOAG+9yhb7bwahm36g == buffer@^5.4.3: version "5.4.3" reSolved "https://registry.yarnpkg.com/buffer/-/buffer-5.4.3.tgz 3fbc9c69eb713d323fc1a8eee072115" "" "" "" "" "" "" "" "" Integrity Sha512-ZVJ65TKFEIT3I6AJ5bivjdzjjqqqgs4O/SNOEZG1F1KYAP9NU2JCUDPWZRSJTHMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMZG m nknqknwhwn == dependencies: base64-js "^1.0.2" ieee754 "^1.1.4" ieee754@^ 1.1.4: version "1.1.13" reSolved "https://registry.yarnpkg.com/ieee754//ieee754-1.13.tgz #EC168558aa187d37d37C32bcb6708b84" "" Integrity Sha512-4VF7I2LYV/Hawerso3xmlmkp5ez83i+/CDLUXI/IGTS/O1SejbnhttnxzfvouqjqhketVPGSFDLWZTG == ignore@^5.1.4: version "5.1.4" reSolved "https://registry.yarnpkg.com/ignore/-/ignore-5.1.4.tgz 94b7b3dbe64552b6eca99f6743d97adf" " Integrity Sha512-MZBUSAHKTW1U7JPKKJY7LCARD1FU5W2RLDXLM4KDKAMJKPLUF9CM1ALEWYJGUPDQEWLAM18y69a8a ==
The on file is relatively similar. There are some differences package-lock.json
package-lock.json uses json format, yarn.lock uses a custom formatyarn.lock The version number that yarn.lock is dependent on is not yarn.lock , which means that node_modules directory structure cannot be determined alone, and it needs to be cooperated with the package.json file. And package-lock.json only needs one file to determine.yarn 's slowdown strategy looks very similar to before npm v5 . Each cache module is stored in an independent folder. The folder name contains information such as module names and version numbers. Use command yarn cache dir to view the catalog data directory:

yarnuses theprefer-onlinemode by default, that is, the use of network data priority. If the network data request fails, then ask the caching data.
I hope that after reading this article, I can help you as follows:
pacakge.json , so as to have further insights to the project engineering configuration,npm version management mechanism, and can reasonably configure the dependent version tonpm install Installation principle, can be used reasonably, can be reasonably used npm cache, package-lock.json