The introduction

Xiaoming receives a demand. The requirement is: “Upgrade T (SDK) to be compatible with P project (H5) in Q (App)”. The requirements are simple, in theory you just need to upgrade A. But the actual process is not easy. Give a case

The error report shocked, Xiao Ming began to investigate. In the console error details, Ming found a crucial piece of information that the error message came from T/buff.js. Ming discovered that buff.js was actually integrated with a feature. This function is used by mounting the Window globally. One line of code that looks like this


  init(){
    if(window.buff){
      console.error('buff has been initialized')
      return
    }
    window.buff = () = >{}}Copy the code

The source of the error has been found. The reason is that window.buff initialization triggered repeated judgment logic causing an error. So what’s the reason for the repeated initialization? Xiao Ming then continued to investigate. What’s going to cause this to be reinitialized. Looking for xiao Ming suddenly found a clue. In the P project. T is not only referenced in projects but also in other internals such as E. Their relationship in node_modules in the project looks like this

It suddenly dawned on me that this nested relationship had two T’s of code executing. Duplicate errors are caused. But where does this nesting come from?

First, let’s look at some concepts

What is the node

Node.js is an open source and cross-platform JavaScript runtime environment. Node is understandably similar to a browser. Provides low-level support for JS running. Based on the cross-platform feature. Node.js has become an essential infrastructure for Web front-end development. Also enable React/vue/webpack outbreaks.

What is the NPM

NPM is the Node.js standard package manager. The original purpose was to manage dependencies in node.js projects. Subsequently, it gradually became an integral part of front-end engineering projects. It is also the world’s largest software registry. Open source developers on every continent use NPM to share and borrow packages, and many organizations use NPM to manage proprietary development.

What is the node_modules

Node_modules is itself a directory. All NPM packages or dependency packages used in the project are summarized in the directory. Its generation is controlled by NPM.

Node_modules can be global and local

For example, we install LoDash:

npm install lodash // NPM will install lodash into the local node_modules file for us.
Copy the code

With -g we can install packages globally:

npm install lodash -g // NPM will install LoDash globally for us.
Copy the code

You can run the NPM root -g command to check the installation location of the global package

The calibration file generated by node_modules is the package.json file in the project.

package.json

Package. json is a dependency calibration file within the project, or understood as a manifest. In this file, all dependencies in the current project are covered.

options paraphrase
dependencies Dependencies are required in production
devDependencies DevDependencies are something you need in a development environment
peerDependencies Development plug-ins are packages on which plug-ins depend
name Project named
version version
main Project Entrance (default ES5)
module Es6 Module code entry (tree shaking mechanism)
author The author
scripts Write executable script commands

version

  1. The common library acts as a member of the NPM. Always follow the release iteration constraints provided by NPM. Version number format: Major version number. Second version. Revision number.
  • Major version number: Incompatible changes or subversive rewrites
  • Minor version number: Added functionality for backward compatibility
  • Revision number: Backward compatible problem fix

  // Common commands to manage version numbers
  major: Major version numberpremajor: Prepare the major versionminor: Indicates the second version numberpreminor: Prepare the second versionpatch: revision numberprepatch: Preliminary revisionprerelease: Pre-release versionCopy the code

We can synchronize the commands provided above to update the version in the project. For example,

// ./mySdk/package.json
"version": "0.0.1",

npm version prerelease / / version = > 0.0.1-0
npm version prepatch / / version = > hundreds
npm version preminor / / version = > while
npm version premajor / / version = > 1.0.1
Copy the code

Versioning can also be managed through modifiers. For example,

  • ^: for instance,^ 1.2.5Represents the version range1. *. *The latest
  • ~: for instance,~ 1.2.5Represents the version range1.2. *The latest

Global version first

One question is why node_modules has so many files when there doesn’t seem to be a lot of dependencies in package.json. We know that the NPM plug-in package will also have its own dependencies. So their dependency might look something like this.

In the figure above, there is A component C that is dependent not only on P but also on A. This dependency requires C to support multi-version coexistence. This scene is Xiao Ming’s problem. T happens to be involved in global operations. The T version does not want multiple versions to coexist. What is the reason for this multi-version coexistence? Let’s look at the anSI-Regex plug-in dependency diagram from a demo project

The directory structure in node_modules looks like this:

The higher version of ansi-align dependency, ansi-regex, is repeatedly dependent on the node_modules of ansi-align. The lower version of the HAS-ANSI dependency is not relied on repeatedly. So we can dialyze NPM’s versioning strategy:

  • Plugin dependent version > global version (plugin node_modules is independently dependent)

  • Plug-in dependent version <= global version (plug-in version will depend on global)

Is there any way to avoid multiple versions co-existing?

1> peerDependencies

Json provides the option peerDependencies. The purpose of peerDependencies is to prompt the host environment to install the packages that meet the dependencies specified by the plugin peerDependencies. It will be the version of the NPM package that references the unified installation of the host environment. This prevents multiple versions from co-existing

2> interval dependencies (avoid write dead dependencies)

NPM introduces some operators based on Node-semver to increase the flexibility of version resolution. These operators allow us to specify a range of dependency library versions. Common ones are: <, >, <=, >=, =. The default value is =.


"vue": "> 0.0.8" Vue must be greater than 0.0.8
"vue": "1.1.2-1.3.1" // VUE must satisfy the range 1.1.2 to 1.3.1

Copy the code

Structure flattening

When there is such a deep nesting dependency, will the corresponding node_modules be nested like this?

Let’s take the example of the globally dependent ANSI-align plug-in from the demo project

In the demo project package-lock.json their dependencies look like this:

The essential dependencies are ansi-align -> string-width -> strip- ANSI -> ansi-regex. But in package-lock.json they have sibling dependencies where the topology is shown

So it’s not hard to see NPM’s handling of deep nesting flattening it in the node_modules of the current environment. That is, flattening deep dependencies.

package-lock.json

This file is intended to keep track of the exact version of each package installed so that the product can be copied 100% in the same way (even if the maintainer of the package updates the package —- citationnodejs.cn/

We need to understand some concepts first

  • ^: for instance,^ 1.2.5Represents the version range1. *. *The latest
  • ~: for instance,~ 1.2.5Represents the version range1.2. *The latest
  • 1.2.5Represents the fixed version1.2.5
  • Use prefixes by default~:npm config set save-prefix '~'
  • Save the exact version without prefix:npm config set save-exact true

NPM will update the updatable plug-in version for us during NPM I without version locking. Engineering projects don’t always involve just one person. So there will be scenarios where A is dependent on B 1.2.0 and there is no problem. However, after student C cloned the project and B upgraded to 1.2.1 after NPM install, a bug appeared.

This is the problem with version dependency. The version can be fixed with 1.2.0. However, there is no control over the dependency of the B plug-in, which is fixed. Package-lock is designed to address this phenomenon. He describes the dependencies in the project in a freeze frame. To ensure that everyone’s dependencies are the same.

The last

Node_modules is in our daily development. It’s actually a black box itself. In general, we don’t need to worry about installing the plug-in package. Many teams serve the business with common business and logic as NPM plug-ins. The other is when the project gets bigger and bigger. Dependency packages also become more complex. Versioning in a project becomes especially important.