NPM continues to iterate, and this article will continue to be updated. You can like it at 👍 and follow it at 💖

NPM provides a very complete automated toolchain in the front-end development process and has become a necessary tool for every front-end developer, but also because of its power many front-end developers will simply use it. This article will summarize the NPM knowledge needed in daily development, so that developers can better use NPM in practical development.

1. NPM install mechanism

As we all know, NPM Install is used to install dependencies in projects, but how it is installed is often overlooked. Because many students only pay attention to the function provided by the dependency package in the actual development, but do not pay attention to the hierarchy of the dependency package. Dependency package installation is complicated, which also leads to many students at a loss when there is a problem with the installation package. Therefore, it is important to understand the NPM install mechanism so that we can be more comfortable with the installation issues.

Assume that the project App has the following three dependencies:

"dependencies": {
    A: "1.0.0",
    B: "1.0.0",
    C: "1.0.0"
}
Copy the code

Modules A, B and C have the following dependencies:

[email protected] - > [email protected] [email protected] - > [email protected] [email protected] - > [email protected]Copy the code

Nested installation

In the NPM 2.x era, installing dependencies was straightforward, and the local directory structure was populated recursively with a tree of package dependencies, that is, each package would install its dependencies into the node_modules directory where the current package resides.

After NPM install, node_modules for the project App will change to the following directory structure:

├ ─ ─ node_modules │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ └ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected]Copy the code

Obviously, such a dependency structure has the following advantages:

  • Obvious hierarchy
  • Simple implementation of multi-version compatibility
  • This ensures uniform behavior and structure for both installation and removal of dependencies

But the disadvantages are just as obvious:

  • May cause a large number of redundancy problems in the same module
  • The directory structure may be deeply nested

Flat installation

Since NPM 3.x, modules have been installed in a flat structure, which iterates through all the nodes in the dependency tree, placing modules one by one in level node_modules. When duplicate modules are found, they are discarded.

Using the project App as an example, node_modules will change to the following directory structure after NPM install:

├ ─ ─ node_modules │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ └ ─ ─ [email protected]Copy the code

As you can see, the [email protected] module is installed in tier 1 node_modules, while [email protected] is still installed in [email protected]. Therefore, it can be concluded that when NPM install is implemented, if the same dependent package is encountered, the higher version (large version) package will be placed in the first level node_modules, and the lower version of the package will be hung in the dependent package node_modules according to NPM 2.x.

After installing the module [email protected] (dependent on the module [email protected]) in the project, the directory structure becomes:

├ ─ ─ node_modules │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ├ ─ [email protected]Copy the code

As you can see, the module [email protected] will still be installed under module E. So it can be concluded that if a dependency package already exists in node_moudles, the newly installed dependency package will still be installed in node_modules if there is a version conflict.

After installing the module [email protected] (dependent on the module [email protected]) in the project, the directory structure becomes:

├ ─ ─ node_modules │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ │ └ ─ ─ node_modules │ │ │ └ ─ ─ [email protected] │ └ ─ ─ [email protected]Copy the code

As you can see, only module F will be installed. Therefore, it can be concluded that in cases where dependencies already exist in tier 1 Node_moudles, the installation of newly installed dependencies will be ignored if there are no version conflicts.

As you can see from the above examples, NPM 6.x does not solve the problems in NPM 2.x perfectly and even degrades to the behavior of NPM 2.x.

To solve the problem of having many copies in the directory, we can use the NPM dedupe command to redirect all level 2 dependent modules [email protected] to the level 1 directory (provided that the dependent modules [email protected] are upgraded to [email protected]) :

├ ─ ─ node_modules │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ ├ ─ ─ [email protected] │ └ ─ ─ [email protected]Copy the code

Node_modules Path lookup mechanism: When a module is looking for a dependency package, nodeJS will try to load the module in its node_modules folder starting from the current module directory. If it does not find one, it will move up one level to node_modules in the global installation path.

package-lock.json

Starting with NPM 5.x, a package-lock.json file is automatically generated when NPM install is executed.

NPM uses package.json to lock the latest version of the dependency package so that the latest version of the dependency package is pulled every time NPM installs. One of the biggest disadvantages of this mechanism is that when there are minor updates to dependencies, there can be inconsistencies between co-developer dependencies.

The package-lock.json file describes exactly the tree dependencies of all packages in node_modules, and the version number of each package is completely exact. For example, sas-loader in package-lock.json:

"dependencies": {
  "sass-loader": {
    "version": "7.1.0"."resolved": "http://registry.npm.taobao.org/sass-loader/download/sass-loader-7.1.0.tgz"."integrity": "sha1-Fv1ROMuLQkv4p1lSihly1yqtBp0="."dev": true."requires": {
      "clone-deep": "^ 2.0.1." "."loader-utils": "^" 1.0.1."lodash.tail": "^ 4.4.1"."neo-async": "^ 2.5.0"."pify": "^ 3.0.0"."semver": "^ 5.5.0"
    },
    "dependencies": {
      "pify": {
        "version": "3.0.0"."resolved": "http://registry.npm.taobao.org/pify/download/pify-3.0.0.tgz"."integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="."dev": true}}}}Copy the code

Package-lock. json is described in the following fields: Version, resolved, integrity, dev, requires, and dependencies:

  • version: Unique package version number
  • resolvedSource: installation
  • integrity: Hash value to indicate package integrity (to verify whether the package is invalid)
  • dev: If true, the dependency is only a development dependency for the top-level module or a transitive dependency
  • requires: Indicates all dependencies required by the dependency packagepackage.jsonIn thedependenciesDependencies in
  • dependencies: depend on the packagenode_modulesDependency packages, and top-level packagesdependenciesSame structure

As you can see in the package-lock.json file above, the Pify dependency exists in requires and dependencies. So let’s go to node_modules:

  1. Open the root directorynode_modulesYou’ll find it installedsass-loaderAll dependencies required, except those in thepifyExcept, all dependent packages with large version numberssass-loaderThe consistency required.
  2. To the root directorynode_modulesfindpifyDependent package, the discovery version is4.0.1.
  3. findsass-loaderOpen the project dependency packagenode_modulesTurns out there’s one of thempifyDependent package, but version is3.0.0. This version ofsass-loaderIt really depends on this versionpify.

The package-lock.json file corresponds to the node_modules directory structure. The presence of package-lock.json in the project directory keeps the dependent directory structure the same for each installation.

When developing an application, it is recommended to submit the package-lock.json file to the code repository so that your team members, operations deployable personnel, or CI systems can install the same dependent versions when NPM install is executed.

But when developing a library, package-lock.json files should not be published to the repository. In fact, NPM does not publish package-lock.json files by default. The reason for this is that library projects are typically dependent on other projects and can reuse packages already loaded by the main project without dying, whereas libraries that rely on exact version numbers can cause package redundancy.

2. Dependency packages in NPM

Dependent package classification

There are actually five dependencies in Node:

  • Dependencies – Business dependencies

  • DevDependencies – Develop dependencies

  • PeerDependencies – peerDependencies

  • BundledDependencies/bundleDependencies – Package dependencies

  • OptionalDependencies – optionalDependencies

As NPM consumers, the dependencies we use are Dependencies and devDependencies, and the remaining three dependencies are fields used by package publishers.

dependencies

This dependency is required when the project finally goes live or when an NPM package is released, meaning that the dependencies in it should be part of the online code. Dependencies such as framework Vue, third-party component library Element-UI, etc. must be included in this option for production use.

Package this dependency with the command NPM install/ I packageName -s /–save. If you write a package name without specifying a version, the latest version of the package in the current NPM repository will be installed. If you want to specify a version, you can write the version number after the package name, for example, NPM I [email protected] -s.

Starting with NPM 5.x, you can add dependencies to dependencies by executing NPM I packageName instead of manually adding -s /–save.

devDependencies

Such dependencies are only needed at project development time, that is, they should not be part of the online code. For example, build tools webpack, gulp, preprocessor Babel-Loader, SCSS-Loader, test tools E2E, CHAI, etc., are all toolkits for development and do not need to be used in the production environment.

Install the package as a development dependency by using the command NPM install/ I -d /–save-dev. If you want to reduce the installation package, you can use the command NPM I –production to ignore development dependencies and install only base dependencies, which is usually used online on the machine (or QA environment).

Don’t think only independenciesAre packaged together, while indevDependenciesNot in! Whether a module can be packaged depends on whether it has been introduced into the project!

In a business project, there is no essential difference between dependencies and devDependencies. When implementing NPM I, both dependencies are downloaded. When an NPM package is distributed, the Dependencies dependency in the package is downloaded together when the package is installed, whereas the devDependencies dependency is not.

peerDependencies

This dependency prompts the host environment to install the package specified in peerDependencies. The package that the plug-in depends on is always the NPM package installed by the host environment, and finally resolves the problem that the plug-in does not depend on the package.

This sentence may sound like a mouthful, but let me give you an example. [email protected] only provides a library of VUe-based UI components, but it requires the hosting environment to install the specified VUE version, so you can see a configuration in package.json in the Element project:

"peerDependencies": {
    "vue": "^ 2.5.16"
}
Copy the code

It requires the host environment to install version 3.0.0 > Vue @ >= 2.5.16, which means that element-UI runs on vUE dependencies in that range provided by the host environment.

When installing the plugin, peerDependencies behave differently in NPM 2.x and NPM 3.x:

In NPM 2.x, the dependencies specified in the installation package peerDependencies are forcibly installed along with NPM install packageName, and the dependencies specified in peerDependencies are installed in the host environment. So there is no need to specify a dependency on the content of peerDependencies in the installed package in the package.json file of the host environment.

NPM 3.x does not require the dependencies specified in peerDependencies to be installed. NPM 3.x only checks the installation after the installation, and if the installation is not correct, it will print a warning to the user, for example, some packages must be installed or some packages are of different versions.

In plain English: If you install me, then you’d better install A, B, and C as I request.

bundledDependencies / bundleDependencies

This dependency is related to the NPM pack command. Assume that package.json has the following configuration:

{
  "name": "font-end"."version": "1.0.0"."dependencies": {
    "fe1": "^" 0.3.2. },"devDependencies": {..."fe2": "^ 1.0.0"
  },
  "bundledDependencies": [
    "fe1"."fe2"]}Copy the code

NPM install front-end-1.0.0. TGZ generates front-end-1.0.0. TGZ contains fe1 and fe2 installation packages. NPM install front-end-1.0.0.

The dependencies specified in bundledDependencies must be declared in Dependencies and devDependencies, otherwise an error will be reported in packaging.

optionalDependencies

The dependency in this dependency does not affect the entire installation process even if the installation fails. Note that if a dependency appears in both dependencies and optionalDependencies, optionalDependencies will have higher priority and may have unintended effects, so try to avoid this.

In a real project, if a package has expired, we usually look for a replacement, or a different implementation. Uncertain dependencies can make code judgment and testing harder, so this dependency is best avoided.

Dependent package version number

NPM adopts the Semver specification as a dependency versioning scheme.

According to Semver’s convention, the version format of an NPM dependency package is: major version number. Revision number (X.Y.Z), each of which is as follows:

  • Major version number (also called major version, major version)

    A change to a larger version is likely to be a disruptive change, meaning there may be an API or usage that is incompatible with the older version (e.g. Vue 2 -> 3).

  • Minor version number (also called minor version, minor version)

    Changes made in smaller versions should be compatible with apis and usages in the same larger version, and therefore should be insensitive to developers. So we usually just say the big version number, and rarely get down to the small version number.

    If the major version number is 0, it indicates that the software is in the initial stage of development, everything can change at any time, and there may be incompatibilities between minor versions. So when choosing dependencies, try to avoid packages with large version numbers of 0.

  • Revision number (also called patch, patch)

    Generally used to fix bugs or minor changes, but also to maintain forward compatibility.

The common versions are as follows:

  • “1.2.3”

    Indicates the exact version number. Any other version numbers do not match. This approach is recommended for important online projects.

  • ^ “1.2.3”

    Indicates the version number of compatible patches and minor version updates. The earliest Allows changes that do not modify the left-most non-zero digit in the [major, minor, Patch] a tuple). This sentence is a mouthful. Here are a few examples:

    ^ "1.2.3"Is equivalent to"> = < 2.0.0 1.2.3". Just the leftmost one"1"Nothing changes. Everything else can change. so"1."."1.3.0"All compatible."^ 0.2.3"Is equivalent to"> = 0.2.3 < 0.3.0". Because the leftmost one is"0", so only the second"2"Constant. Everything else is compatible, like"0.2.4""0.2.99"."^ 0.0.3"Is equivalent to"> = 0.0.3 < 0.0.4". Both the major and minor version numbers are"0"So it's equivalent to exact"0.0.3".Copy the code

    As you can see from these examples, ^ is both an updated and secure version, but there are different mechanisms for handling large versions of 1 and 0.

  • “~ 1.2.3”

    Indicates that only the patch version is compatible. The definition of ~ is divided into two parts: if the minor version number (second) is listed, only patch (third) changes are compatible; If no minor version number is listed, second – and third-bit changes are compatible. Let’s understand this definition in two ways:

    "~1.2.3" lists the minor version number "2" and is therefore only compatible with third-bit changes, equivalent to ">= 1.2.3 < 1.3.0". "~1.2" also lists the minor version number "2", so it is compatible with the third bit as above, equivalent to ">= 1.2.0 < 1.3.0". "~1" does not list minor version numbers and is compatible with second and third bit changes, so it is equivalent to ">= 1.0.0 < 2.0.0"Copy the code

    As you can see from these examples, ~ is a more cautious and secure way of writing than ^, and ~ does not discriminate between larger versions 0 or 1, so “~0.2.3” is the same algorithm as “~1.2.3”. The two are equivalent when the first digit is 0 and the second digit is listed, for example, “~0.2.3” and “^0.2.3”.

  • “1.X”, “1.X”, “1”, “*”

    Indicates the wildcard version number. X, x, *, and (empty) have the same meaning, indicating that any content can be matched. To be specific:

    "*", "x", or (empty) indicates that any version can be matched. "1 x", "1. *" and "1" means to match all versions of the major version number for "1", so is equivalent to "> = 1.0.0 < 2.0.0". "1.2 x", "1.2. *" and "1.2" means to match the version number begin with "1.2" all versions, so is equivalent to "> = 1.2.0 < 1.3.0".Copy the code
  • “1.2.3-alpha.1”, “1.2.3-beta.1”, “1.2.3- RC.1”

    Version number with pre-release keywords. Here are some definitions of pre-release keywords:

    Alpha: Preview, or internal beta; Generally not to external release, there will be a lot of bugs; Generally only used by testers. Beta: beta, or public beta; This phase of the release will always add new features; It will be released after alpha. Rc (release candidate) : final test version; A potential release candidate for the final product, or for release if there are no problems.Copy the code

    Consider this from a package developer’s point of view: if the current online version is “1.2.3”, if I make some changes and need to release version “1.2.4”, but I don’t want to go live directly (because users using “~1.2.3” or “^1.2.3” will update silently), then I need to use pre-release. So I might publish “1.2.4-alpha.1” or “1.2.4-beta.1”, etc.

    ">1.2.4-alpha.1" accepts all pre-releases greater than 1 under "1.2.4-alpha". Therefore, "1.2.4-alpha.7" is acceptable, but "1.2.4-beta.1" and "1.2.5-alpha.2" are not. In addition, if the version is official (without pre-release keywords), as long as the version number meets the requirements, do not check the pre-release version number, such as "1.2.5", "1.3.0" are accepted. "~1.2.4-alpha.1" means ">=1.2.4-alpha.1 < 1.3.0". Such "1.2.5 pathogen - alpha.", "2" are eligible, and 1.2.5 - alpha. "1", "1.3.0 does not comply with the". "^1.2.4-alpha.1" means ">=1.2.4-alpha.1 < 2.0.0". Such "1.2.5 pathogen - alpha.", "2", "1.3.0" meet the condition, and 1.2.5 - alpha. "1", "2.0.0 does not comply with the".Copy the code

Version number and more writing, such as range (a – b), (> =) number greater than or equal to, less than or equal to number (< =), or (| |), etc., because use much, here no longer. For detailed documentation, see Semver. It is also an NPM package that can be used to compare the size of two version numbers, whether they meet requirements, etc.

Dependency package version management

NPM 2.x/3.x is a thing of the past. In NPM 5.x and above (preferably above 5.6, because the package-lock.json processing logic has been updated for several versions between 5.0 and 5.6, and stabilizes after 5.6), You should know how to manage the version of the dependencies in your project (for example, the ^ version) :

  1. Under the same premise of large version, if a module inpackage.jsonThe smaller version inIs greater thanpackage-lock.jsonA smaller version of thenpm install, the module will be updated to the latest version under the larger version and the version number will be updated topackage-lock.json. ifLess than, ispackage-lock.jsonVersion lock in.
// package-lock.json original version
"clipboard": {
  "version": "1.5.10",},"vue": {
  "version": "2.6.10",}// Package. json modified version
"dependencies": {
  "clipboard": "^ 1.5.12"."vue": "^ 2.5.6". }// After NPM install is executed, package-lock.json is displayed
"clipboard": {
  "version": "1.7.1".// Update to the latest version under the larger version
},
"vue": {
  "version": "2.6.10".// The version has not changed
}
Copy the code
  1. If a module is inpackage.jsonandpackage-lock.jsonIf the larger version innpm installWill be based onpackage.jsonUpdate the latest version under the medium version and update the version number topackage-lock.json.
// package-lock.json original version
"clipboard": {
  "version": "2.0.4",}// Package. json modified version
"dependencies": {
  "clipboard": "^ 1.6.1." ",}// After NPM install is executed, package-lock.json is displayed
// 
"clipboard": {
  "version": "1.7.1".// Update to the latest version under the larger version
}
Copy the code
  1. If a module is recorded in package.json but not in package-lock.json, a detailed record of the module will be generated in package-lock.json after NPM install is executed. Similarly, a module that has no record in package.json but has records in package-lock.json will be deleted in package-lock.json after NPM install is executed.

  2. To update the latest version (minor version) of a module from a larger version, run the following command:

npm update packageName
Copy the code
  1. To update to the specified version (upgrade to a larger version), run the following command:
npm install [email protected]
Copy the code
  1. To uninstall a module, run the following command:
npm uninstall packageName
Copy the code
  1. Exact version of the installed module:
npm install packageName -D/S --save-exact  # Install version number will be accurate, version number will not appear before the ^~ character
Copy the code

Json and package-lock.json version numbers will be updated accordingly.

When upgrading/uninstalling dependent packages, we try to use commands to achieve this, so as not to manually modify the version number in package.json, especially not to manually modify package-lock.json.

3. NPM scripts

The scripts field in package.json can be used with custom script commands, and each of its properties corresponds to a script. Take vuE-CLI3 as an example:

"scripts": {
  "serve": "vue-cli-service serve". }Copy the code

Instead of the vue-cli-service serve script, the NPM run serve script can be used to start the project without having to type a long script every time.

NPM run is short for NPM run-script, and the former is usually used, but the latter better reflects the nature of the command.

The working principle of

The bin field in package.json

The field bin in package.json represents a mapping from an executable file to a specified file source. The NPM bin command displays the path to the bin directory of the current project. For example, in package.json in @vue/cli:

"bin": {
  "vue": "bin/vue.js"
}
Copy the code

If @vue/cli is installed globally, the @vue/ CLI source files will be installed in the global source file installation directory (/user/local/lib/node_modules). NPM creates a soft link named vue to the /usr/local/lib/node_modules/ @vue.js file in the global executable bin installation directory (/usr/local/bin). This allows you to execute commands directly from the terminal by typing vUE. As shown below:

If @vue/cli is installed locally, NPM will create a soft link named vue in the local project./node_modules/. Bin directory pointing to./node_moudles/@vue/cli/bin/vue. /node_modules/.bin/vue (you can also use the NPX vue command, which is used to call the modules installed inside the project).

A soft link (symbolic link) is a special type of executable that contains a reference to another file or directory as an absolute or relative path. Run ll in the bin directory to view specific soft links. When a link file is read or written, the system automatically changes the operation to the source file. However, when a link file is deleted, the system only deletes the link file, not the source file.

PATH environment variable

When executing a command in Terminal, the command looks for an executable with the same name in the PATH contained in the environment variable PATH. /node_modules/.bin registers their executables and is not included in the PATH environment variable, so entering commands in terminal will cause an error that cannot be found.

Why is it possible to execute a partially installed command line package through NPM run?

This is because every time NPM run is executed, a new Shell is automatically created, which adds the absolute PATH of the current project node_modules/.bin to the environment variable PATH. After execution, the environment variable PATH is restored to its original state.

Let’s test that out. The PATH environment variable is:

PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
Copy the code

NPM run env = NPM run env = PATH

PATH=/usr/local/lib/node_modules/npm/node_modules/npm-lifecycle/node-gyp-bin:/Users/mac/Vue-projects/hao-cli/node_modules/.bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
Copy the code

You can see that the runtime PATH environment variable has two additional paths: the NPM directive PATH and the absolute PATH to node_modules/.bin in the project.

So NPM run lets you access the executable files in the node_modules/.bin directory of the current project without adding a path prefix.

The PATH environment variable tells the system, when asked to run a program without telling it the full PATH of the program, which directories to look for in addition to the current directory.

Usage guidelines

Incoming parameters

A few more words here about the parameters in scripts. There are a lot of inaccurate statements on the web. After a lot of trial and error, Node handles scripts very easily, such as:

"scripts": {
  "serve": "vue-cli-service serve"."serve1": "vue-cli-service --serve1"."serve2": "vue-cli-service -serve2"."serve3": "vue-cli-service serve --mode=dev --mobile -config build/example.js"
}
Copy the code

With the exception of the first executable command, any whitespace delimited string (except for some shell syntax) is an argument and is accessible through the process.argv attribute.

The process.argv property returns an array containing the command line arguments that started the Node process. The first element is the absolute pathname of the executable file that started the Node process, process.execPath, and the second element is the path of the currently executing JavaScript file. The remaining elements are other command line arguments.

For example, to execute the NPM run serve3 command, process.argv contains the following contents:

[ '/ usr/local/Cellar/node / 7.7.1 _1 / bin/node'.'/Users/mac/Vue-projects/hao-cli/node_modules/.bin/vue-cli-service'.'serve'.'--mode=dev'.'--mobile'.'-config'.'build/example.js']
Copy the code

Many command line packages rely on parsers such as Minimist or Yargs to parse command-line arguments.

Take minimist to vuE-cli-service serve –mode=dev — mobile-config build/example.js parsing as an example, parsing the result is:

{ _: [ 'serve'].mode: 'dev'.mobile: true.config: 'build/example.js'.'$0': '/Users/mac/Vue-projects/hao-cli/node_modules/.bin/vue-cli-service'}
Copy the code

You can see minimist’s handling of command-line arguments in the./node_modules/.bin/vue-cli-service file:

const rawArgv = process.argv.slice(2)
const args = require('minimist')(rawArgv, {
  boolean: [
    // build
    'modern'.'report'.'report-json'.'watch'.// serve
    'open'.'copy'.'https'.// inspect
    'verbose']})const command = args._[0]
service.run(command, args, rawArgv).catch(err= > {
  error(err)
  process.exit(1)})Copy the code

We can also pass arguments on the command line:

NPM run serve --params // The params argument will be converted to process.env.npM_config_params =trueNPM run serve --params=123 // Params will be converted to process.env.npM_config_params =123 NPM run serve-params // equivalent to --params parameter NPM Run serve -- --params // Add the --params parameter to process.env.argv array NPM run serve params // add the params parameter to process.env.argv array NPM Run serve -- params // Add the params argument to the process.env.argv arrayCopy the code

Multi-command operation

Some projects may need to execute multiple tasks simultaneously at startup, and the order in which these tasks are executed determines the project’s performance.

The serial execution

Serial execution requires that the next task can be executed only after the previous task has been successfully executed, using && symbols to connect.

npm run script1 && npm run script2
Copy the code

During serial command execution, if one command fails to be executed, the entire script is terminated.

Parallel execution

Parallel execution, where multiple commands can be executed in parallel at the same time, using the ampersand symbol.

npm run script1 & npm run script2
Copy the code

These two symbols are built into Bash. In addition, third-party task manager modules can be used: script-runner, NPm-run-all, redrun.

Env environment variable

When the NPM run script is executed, NPM sets some special env environment variables. All fields in package.json are set to environment variables starting with npM_package_. For example, package.json contains the following fields:

{
  "name": "sh"."version": 1.1.1 ""."description": "shenhao"."main": "index.js"."repository": {
    "type": "git"."url": "git+ssh://[email protected]/xxxx/sh.git"}}Copy the code

Process.env.npm_package_name specifies the name field sh in package.json. Git can also be repository_type for the nested property type by going to process.env.npm_package_repository_type.

All configurations related to NPM are also set to environment variables starting with NPM_config_.

In addition, a special environment variable, nPM_lifecyCLE_event, is set to represent the name of the script being run. For example, when NPM run serve is executed, the value of process.env.npm_lifecycle_event is serve. By checking this variable, one script can be used in different NPM scripts.

These environment variables are only available in the script execution environment of NPM run, not in the node script that normally executes. Env $npM_package_name cannot be accessed directly, but scripts can be accessed by defining the script “scripts”: {“bundle”: “echo $npm_package_name”}.

Instruction hooks

When executing NPM scripts (either custom or built-in), you go through the pre and POST hooks, where you can define the commands before and after a command.

For example, when executing the NPM run serve command, NPM run serve, NPM run serve, NPM run postserve are executed in sequence, so you can customize some actions in the two hooks:

"scripts": {
  "preserve": "xxxxx"."serve": "vue-cli-service serve"."postserve": "xxxxxx"
}
Copy the code

Of course, if you do not specify preserve or postserve, you will skip it silently. If you want to specify hooks, you must add them strictly with the pre and POST prefixes.

As mentioned above, an environment variable process.env.npM_lifecycle_event can be used with hooks:

const event = process.env.npm_lifecycle_event

if (event === 'preserve') {
    console.log('Running the preserve task! ')}else if (_event === 'serve') {
    console.log('Running the serve task! ')}Copy the code

4. NPM configuration

The configuration actions of the NPM help us to pre-define the behavior of the NPM for the project and also allow us to pre-define configuration items for use in the project. Therefore, it is necessary to understand the configuration mechanism of NPM.

priority

NPM can obtain its configuration values from various sources, in order of priority from highest to lowest:

The command line

npm run serve --params=123
Copy the code

When the above command is executed, the value of the configuration item params is set to 123, which can be accessed through process.env.npM_config_params. The Params configuration value at this time will override all existing Params configuration values from other sources.

Env environment variable

If the env environment variable has an environment variable prefixed with nPM_config_, it is recognized as a configuration property of NPM. For example, set the npM_config_Package_lock variable in the env environment variable:

export npm_config_package_lock=false// Modifies variables in memory, which only apply to the current terminalCopy the code

When NPM install is executed, NPM reads the configuration item from the environment variable without generating the package-lock.json file.

Check an environment variable: echo $NODE_ENV

To delete an environment variable: unset NODE_ENV

NPMRC file

You can modify the configuration directly by modifying the NPMRC file. Multiple NPMRC files exist in the system. The access priorities of these NPMRC files are as follows:

  • Project-level.npMRc files

    Only applicable under this project. In other projects, these configurations do not take effect. By creating this.npMRc file, you can unify the team’s NPM configuration specification.

  • User-level.npmrc files

    The MAC address is ~/.npmrc. (NPM config get userconfig)

  • Global level NPMRC file

    The MAC address is $PREFIX/etc/npmrc. NPM config get globalconfig

  • NPM built-in NPMRC file

    This is an immutable built-in configuration file that overwrites the default configuration in a standard and consistent manner for maintainers. The MAC address is /path/to/ NPM/NPMRC.

. NPMRC is written in NPM/INI format.

The default configuration

Run the NPM config ls -l command to view the default internal configuration parameters of the NPM. If no parameter is configured on the command line, environment variables, or all configuration files, the default parameter value is used.

NPM config instruction

NPM provides several NPM config directives for user – and global-level configuration:

set

npm config set <key> <value> [-g|--global]
npm config set registry <url>  # designated download source of NPM package defaults to https://registry.npmjs.org/, you can specify the private sources

npm config set prefix <path>  The # prefix argument specifies the root directory for the global installation
When the package is installed globally, the package will be installed in the following location:
# Mac system: {prefix}/lib/node_modules
# Windows: {prefix}/node_modules
Link the executable file to the following location:
# Mac system: {prefix}/bin
# Windows: {prefix}
Copy the code

Use – g – | the global flag changes or new global level configuration, do not use the modified or new user level configuration (level NPMRC files will update).

If the key does not exist, it is added to the configuration. If value is omitted, the key is set to true.

You can also override the value of the config field in package.json:

// package.json
{
  "name" : "foo"."config" : { "port" : "8080" },
  "scripts" : { "start" : "node server.js"}}Copy the code
// server.js
console.log(process.env.npm_package_config_port)  / / print 8080
Copy the code

Execute instructions:

npm config set foo:port 8000
Copy the code
// server.js
console.log(process.env.npm_package_config_port)  / / print 8000
Copy the code

get

npm config get <key>
npm config get prefix  Get the global installation path of NPM
Copy the code

Gets the value of the specified configuration item based on the configuration priority.

delete

npm config delete <key>
Copy the code

The NPM website says that you can delete all configuration items specified in the configuration file, but the experiment cannot delete the configuration items specified in the project level. NPMRC file.

list

npm config list [-l] [--json]
Copy the code

Add -l or –json to view all configuration items, including the default ones. Otherwise, you cannot view the default configuration items.

edit

npm config edit [-g|--global]
Copy the code

Open the configuration file in the editor. Use – g | – global symbol editing global level configuration and the default configuration, do not use the edit user level configuration and the default configuration.

See NPM config for more default configurations.

5. NPM project management

Project version number management

The version field in package.json represents the version number of the project. Each time a new version of the project is released, the Version field needs to be updated accordingly for later maintenance. You can modify the vsersion field manually, but in order to automate the release process, try to use the NPM version directive to automatically update version:

NPM version 1.2.3 (v)Set the version number to 1.2.3
npm version major  The larger version number is incremented by 1, and the remaining version numbers are returned to 0
npm version minor  If the minor version number is increased by 1, the revision number is returned to 0
npm version patch  # Revision number + 1
Copy the code

The version number displayed must conform to the Semver specification, allowing a V identifier before the version number.

If you don’t want the update to be officially released, you can also create a pre-release version:

The current version is 1.2.3
npm version prepatch  The version number is changed to 1.2.4-0, which is the first pre-release version of 1.2.4
npm version preminor  The version number changes to 1.3.0-0, which is the first pre-release of version 1.3.0
npm version premajor  The version number is changed to 2.0.0-0, which is the first pre-release of version 2.0.0
npm version prerelease  # change the version number to 2.0.0-1, that is, increase the pre-release version number by one
Copy the code

Git add->git commit->git tag

Commit message default is automatically modified version number, can be customized by adding -m/–message option commit message:

npm version xxx -m "upgrade to %s for reasons"  # %s is automatically replaced with the new version number
Copy the code

For example, NPM version minor -m “feat(version): upgrade to %s for reasons”

If there are uncommitted changes in git workspace, NPM version will fail to execute and can be enforced with the -f/–force suffix.

If you don’t want the NPM version directive to affect your Git repository, you can use the –no-git-tag-version parameter in the directive:

npm --no-git-tag-version version xxx
Copy the code

If you want to leave your Git repository unaffected by default, you can disable it in your NPM Settings:

NPM config set git-tag-version false # Do not automatically tag NPM config set commit-hooks false # Do not automatically commitCopy the code

Module Tag Management

Students who do not publish packages often may not be very familiar with the concept of module tag. Take VUE as an example, run NPM dist-tag ls vue to check vUE package tag:

3 CSP: 1.0.28- CSP Latest: 2.6.10Copy the code

The beta, CSP, and latest listed above are tags. Each tag corresponds to a version.

So what does a tag do anyway? Tag is similar to the concept of branching in Git, where a publisher can publish a version on a given tag and a consumer can install a package on a given tag. Versions under different labels do not affect each other, which does not affect the official version when publishers release pre-release packages and users try pre-release packages.

NPM publish –tag latest is published by default when the package is published. NPM install XXX will download the latest version under the latest tag by default. NPM install xxx@latest is actually executed. Of course, we can also customize the tag:

The current version is 1.0.1
npm version prerelease  # 1.0.2-0
npm publish --tag beta
npm dist-tag ls xxx  # # beta: 1.0.2-0
npm install xxx@beta  Download beta version 1.0.2-0
Copy the code

When the PRERelease version is stable, you can set the prerelease version to stable:

NPM dist-tag add [email protected] Latest NPM dist-tag ls XXX# latest: 1.0.2-0
Copy the code

Domain level package management

A careful student will notice that dependencies in package.json come in two forms:

"devDependencies": {
  "@commitlint/cli": "^ 7.2.1"."commitizen": "^ 3.0.4"
}
Copy the code

Scoped package is a scoped package. This scoped package concentrates packages in a namespace for centralized management and prevents naming conflicts with other packages.

To publish domain-level packages, first add scope-related declarations to the name property of your project’s package.json, via the directive:

npm init --scope=scopeName -y
Copy the code

Package. Json into:

{
  "name": "@scopeName/package"
}
Copy the code

You can use a user name as a domain name or an organization name as a domain name.

By default, NPM considers the package private because it is declared with @, and there is a charge to host private packages on NPM. To avoid publishing private packages, add –accss=public to inform NPM that it is not a private package:

npm publish --access=public
Copy the code

A domain-level package is not necessarily a private package, but a private package is definitely a domain-level package.

In addition, you need to install the domain level package according to the full name of the domain level package:

npm install @scopeName/package
Copy the code

6. Several practical tips for NPM

Default initial configuration

Initializing a new project with NPM init will prompt you to fill in some project description. If filling in this information is a bit cumbersome, you can use the -y flag to accept some of the default values in package.json:

npm init -y
Copy the code

You can also set the default value for initialization:

npm config set init-author-name <name> -g
npm config set init-author-email <email> -g
Copy the code

The above two directives set the default author name and mailbox for your NPM. When NPM init-y is executed, the author name and mailbox fields in package.json are automatically written to the default values.

Viewing Script Commands

The most direct way to see all the NPM script commands for the current project is to open the package.json file in the project and examine the scripts field. We can also see this using the NPM run command with no arguments:

npm run
Copy the code

Viewing environment Variables

Env to view all of the current environment variables, while viewing all of the runtime environment variables can do:

npm run env
Copy the code

Check the environment

Multiple checks can be run in our environment through the NPM doctor command. For example, check whether our current environment can connect to the NPM service, check node and NPM versions, check NPM sources, check cache file permissions, and so on:

npm doctor
Copy the code

Module management

Check all modules that the current project depends on, including submodules and submodules of submodules:

npm list/ls
Copy the code

If you also want to see some description of the module (as in description in package.json) :

NPM la/ll // equivalent to NPM ls --longCopy the code

A project often depends on many modules, and can be viewed by limiting the level of output modules:

NPM list/ls --depth=0Copy the code

Check the current version information of a module that the project depends on:

npm list/ls <packageName>
Copy the code

View the version information of a module package:

NPM view/info <packageName> version // Latest version information about the module (excluding pre-released versions) NPM view/info <packageName> versions // NPM view/info <packageName> <package.json key value > // You can also view the corresponding values of fields in package.jsonCopy the code

Check whether a module is installed because of who, if empty, the module is a built-in module or does not exist:

npm ll <packageName>
Copy the code

View all information about a module, including its dependencies, keywords, update dates, contributors, repository addresses, licenses, and so on:

npm view/info <packageName>
Copy the code

View the upgradable modules in the current project:

npm outdated
Copy the code

Sort out irrelevant modules in the project:

npm prune
Copy the code

View module documentation

Open the module’s home page:

npm home <packageName> 
Copy the code

Open the code repository for the module:

npm repo <packageName> 
Copy the code

Open the document address of the module:

npm docs <packageName>
Copy the code

Open the module’s issues address:

npm bugs <packageName>
Copy the code

Run the script in a different directory

You have a lot of apps in your folder, and when you want to launch an app, you have to CD step by step into the directory of the app you want to launch, and then execute the launch command. NPM provides –prefix to specify the boot directory:

npm run dev --prefix /path/to/your/folder
Copy the code

Local development module debugging

  1. Suppose you are developing a global command line moduleAThat’s when you need to debug it locally, not every timenpm publishModule, and then install and debug. At this point you can execute in the module’s directory:
npm link
Copy the code

The above commands make NPM module commands globally executable anywhere by linking directories and executable files. After executing the above command, the A command can be invoked globally.

NPM Link does two things:

  1. For the targetnpmThe module creates a soft link to link it globallynodeModule Installation Path/usr/local/lib/node_modules/.
  2. For the targetnpmExecutable of a modulebinFile creates a soft link to link it globallynodeCommand Installation Path/usr/local/bin/.
  1. Suppose you are developing a functional moduleBAnd then you’re in the projectCOf course, you can copy the module code to the project you needCDebug, but this is not ideal, you can do the following:
# project B
npm link

# project C
npm link B
Copy the code

The above command creates a global soft link for function module B and then links it to the installation path of project module C. /node_modules/ is equivalent to generating symbolic links for local modules. We can then load the module in project C:

// In project C
const B = require('B')
Copy the code

All changes to module B are directly reflected in project C. When your project no longer needs this module, you need to remove the soft connection, otherwise you will have an error when installing packages on NPM in your project:

# project C
npm unlink B

# project B
npm unlink
Copy the code

Security vulnerability check

Check whether there are dependency packages with security vulnerabilities in the project, and if so, generate their vulnerability reports and display them in the console:

NPM audit [--json] # add --json to generate vulnerability report in JSON formatCopy the code

After the NPM is upgraded to 6.x, the NPM audit command is automatically executed after the project is updated or a new dependency package is downloaded to check the security of the project dependency package. If any security vulnerability exists, the vulnerability report will be generated and displayed in the console.

Fix dependency packages with security vulnerabilities (automatically updated to a compatible security version) :

npm audit fix
Copy the code

NPM Audit fix can be used to fix most of the dependencies that have security vulnerabilities. For some dependencies that fail to be automatically repaired, SERVER Warnings are generated (mainly when the dependencies change incompatible apis or upgrade large versions). This means that the recommended fix version may still have problems, in which case you can run the following command to fix these dependencies:

npm audit fix --force
Copy the code

— Force will upgrade the dependency package version number to the latest large version, not the compatible security version. Older upgrades may have incompatible uses, so avoid using –force.

If the NPM audit fix –force command is executed, you can manually run the NPM audit command to display details about the dependent packages with vulnerabilities. The More info link may provide a solution.

If you want to know how the Audit Fix will handle dependencies in your project, you can check in advance:

npm audit fix --dry-run --json
Copy the code

If you only want to fix dependencies in production (only update dependencies in devDependencies, not devDependencies) :

npm audit --only=prod
Copy the code

If you don’t want to fix dependencies, just modify the package-lock.json file:

npm audit fix --package-lock-only
Copy the code

If you want to install a package without checking for security vulnerabilities:

npm install packageName --no-audit
Copy the code

To install all packages without checking for security vulnerabilities, you can modify the NPM configuration:

npm config set audit false
Copy the code

Depend on the lock

When NPM installs modules by default, it qualifies the major version number of the installed modules with a stripper ^. NPM can be configured to qualify the installed module version with the tilde ~ :

npm config set save-prefix="~"
Copy the code

You can also configure NPM to only install modules with exact version numbers:

npm config set save-exact true
Copy the code

Install packages in a continuous integration environment

NPM CI is recommended instead of NPM install in continuous integration environments:

npm ci
Copy the code

The main differences between it and NPM Install are:

  • It must be present in the projectpackage-lock.jsonornpm-shrinkwrap.json, otherwise executenpm ciAn error will be reported.
  • If you detectpackage.jsonandpackage-lock.jsonIf the dependencies in thenpm ciWill exit with an error instead of updating the version numbers in both files.
  • npm ciOnly according to thepackage-lock.jsonTo install the package, whilenpm installWill be combined during installationpackage.jsonandpackage-lock.jsonTo calculate the dependency package version difference problem. So by comparisonnpm install.npm ciThis improves package installation speed and avoids inconsistent package versions in production environments.
  • If the project already existsnode_modules.npm ciIt will be removed first and then installed.
  • npm ciYou can only install dependencies for an entire project at once, not a single dependency for a project.

7. Release the NPM package

The preparatory work

After you set up a development build environment for your package (which I won’t go into detail here), there are two things to note:

  1. inpackage.jsonOf the filemainThe import file of the imported package is configured in the field. Generally, the path of the packaged file is configured.
{
    "main": "./lib/index.js",}Copy the code
  1. Create a new one in the root directory of the package.npmignoreFile to specify which folders of the package do not need to be published.
.idea
node_modules
src
test
.eslintignore
.eslintrc
Copy the code

Release process

  1. If there is nonpmFirst account,registeredAn account;
  2. Go to the root directory of the project and executenpm loginAnd then enter the user name and password as prompted.
  3. performnpm publishPublish, publish successfully, yournpmThe bag can be found innpmRetrieved from the official website;
  4. To withdraw a published version, you can performnpm unpublish [email protected].

Publish common errors

  • 400: Version error, modifiedpackage.jsontheversionCan;
  • 401:npmThis occurs when the source is set to a third party source, such as we often set the mirror source to Taobao source. Just change the mirror source back to the default;
  • 403: The package name is the same. You can change the package name to advertise the package again.

Publish a package that supports the Tree Shaking mechanism

Tree Shaking relies on the modular nature of the ES Module. Because the dependencies of ES Module modules are determined at compile time (independent of runtime) and cannot be changed later, it is possible to do reliable static analysis based on this feature.

The most common NPM package we develop is Babel, which includes a plugin for ES2015 modules to CommonJS Transform, for better compatibility with browsers, and the most frequently used @babel/preset-env. The purpose of this plugin is to transform ES Module syntax into CommonJS syntax, so you lose the ES Module features and cannot tree shaking.

Why not point the main field in package.json directly to the ES6 syntax file? This leads to two problems:

  1. babelIgnore by defaultnode_modulesFiles to improve compilation speed if you want to put yourES6Syntax for package backward compatibility, then need to be given in the projectbabelConfigure complex masking rules to whitelist your packages.
  2. If thenodejsUse your package in the environment, and happen tonodejsNot supported in the environmentES ModuleSpecification, then causes code to report errors.

To solve this problem, we need to add two configuration items to package.json.

module

This field points to a source file that conforms to the ES Module specification but uses ES5 syntax. The goal is to start tree shaking while avoiding code compatibility issues.

{
    "main": "./lib/index.js".// Point to the CommonJS module specification code entry file
    "module": "./lib/index.es.js" // Point to the ES Module specification code entry file
}
Copy the code

The above configuration requires that you ship versions of both module specifications in your package. If your NPM environment supports the Module field, the ES Module module specification entry file will be used preferentially, if not, the CommonJS module specification entry file will be used.

sideEffects

This field indicates whether your NPM package has side effects. When this field is set to false, it indicates that the package has no side effects. If it is an array, each entry in the array represents a file that has side effects, these files will not be tree shaking.

{
  "sideEffects": [
    "dist/*"."es/components/**/style/*"."lib/components/**/style/*"."*.less"]}Copy the code

To publish a package that supports Tree Shaking, you need to build a source file that meets the Module field requirements. That is, a source file that complies with the ES Module specification but also uses ES5 syntax.

Rollup can directly build files that conform to the ES Module specification, but WebPack cannot. So we just need to use the build capability provided by rollup and set the output format to ES in the configuration file:

// rollup.config.js
export default{... .output: {
    file: 'bundle.es.js'.format: 'es'}}Copy the code

To better enable Tree Shaking using the ES Module Module specification, rollup is preferred for NPM package development.

8. The last

The knowledge points about NPM are summarized here for the time being, and there may be many aspects that have not been summarized in place. If there are any new knowledge points in the future, we will update them at any time, and welcome you to supplement them at any time and make progress together!