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-ish
git+ssh://user@hostname:project.git#commit-ish
git+ssh://user@hostname/project.git#commit-ish
git+http://user@hostname/project/blah.git#commit-ish
git+https://user@hostname/project/blah.git#commit-ish
dependencies
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
.npmignore
file to exclude some files to prevent a large number of junk files from being pushed tonpm
. The rules are the same as.gitignore
you use..gitignore
files can also act as.npmignore
files.
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 candiate
Let’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.2
alpha
, 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 install
npm 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_modules
node_modules
module.node_modules
depends 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_modules
resolved
: 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_modules
node_modules
node_modules
lock
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:
yarn
uses theprefer-online
mode 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