What's the difference between dependencies, de

2018-12-31 06:52发布

This documentation answers my question very poorly. I didn't understand those explanations. Can someone say in simpler words? Maybe with examples if it's hard to choose simple words?

EDIT also added peerDependencies, which is closely related and might cause confusion.

标签: node.js npm
10条回答
骚的不知所云
2楼-- · 2018-12-31 07:15

As an example, mocha would normally be a devDependency, since testing isn't necessary in production, while express would be a dependency.

查看更多
唯独是你
3楼-- · 2018-12-31 07:16

To save a package to package.json as dev dependencies:

npm install "$package" --save-dev

When you run npm install it will install both devDependencies and dependencies. To avoid install devDependencies run:

npm install --production
查看更多
无色无味的生活
4楼-- · 2018-12-31 07:17

When trying to distribute an npm package you should avoid using dependencies. Instead you need to consider adding it into peerDependencies or remove it from dependencies.

查看更多
长期被迫恋爱
5楼-- · 2018-12-31 07:20

peerDependencies didn't quite make sense for me until I read this snippet from a blog post on the topic Ciro mentioned above:

What [plugins] need is a way of expressing these “dependencies” between plugins and their host package. Some way of saying, “I only work when plugged in to version 1.2.x of my host package, so if you install me, be sure that it’s alongside a compatible host.” We call this relationship a peer dependency.

The plugin does expect a specific version of the host...

peerDependencies are for plugins, libraries that require a "host" library to perform their function, but may have been written at a time before the latest version of the host was released.

That is, if I write PluginX v1 for HostLibraryX v3 and walk away, there's no guarantee PluginX v1 will work when HostLibraryX v4 (or even HostLibraryX v3.0.1) is released.

... but the plugin doesn't depend on the host...

From the point of view of the plugin, it only adds functions to the host library. I don't really "need" the host to add a dependency to a plugin, and plugins often don't literally depend on their host. If you don't have the host, the plugin harmlessly does nothing.

This means dependencies isn't really the right concept for plugins.

Even worse, if my host was treated like a dependency, we'd end up in this situation that the same blog post mentions (edited a little to use this answer's made up host & plugin):

But now, [if we treat the contemporary version of HostLibraryX as a dependency for PluginX,] running npm install results in the unexpected dependency graph of

├── HostLibraryX@4.0.0
└─┬ PluginX@1.0.0
  └── HostLibraryX@3.0.0

I’ll leave the subtle failures that come from the plugin using a different [HostLibraryX] API than the main application to your imagination.

... and the host obviously doesn't depend on the plugin...

... that's the whole point of plugins. Now if the host was nice enough to include dependency information for all of its plugins, that'd solve the problem, but that'd also introduce a huge new cultural problem: plugin management!

The whole point of plugins is that they can pair up anonymously. In a perfect world, having the host manage 'em all would be neat & tidy, but we're not going to require libraries herd cats.

If we're not hierarchically dependent, maybe we're intradependent peers...

Instead, we have the concept of being peers. Neither host nor plugin sits in the other's dependency bucket. Both live at the same level of the dependency graph.

... but this is not an automatable relationship.

If I'm PluginX v1 and expect a peer of (that is, have a peerDependency of) HostLibraryX v3, I'll say so. If you've auto-upgraded to the latest HostLibraryX v4 (note that's version 4) AND have Plugin v1 installed, you need to know, right?

npm can't manage this situation for me --

"Hey, I see you're using PluginX v1! I'm automatically downgrading HostLibraryX from v4 to v3, kk?"

... or...

"Hey I see you're using PluginX v1. That expects HostLibraryX v3, which you've left in the dust during your last update. To be safe, I'm automatically uninstalling Plugin v1!!1!

How about no, npm?!

So npm doesn't. It alerts you to the situation, and lets you figure out if HostLibraryX v4 is a suitable peer for Plugin v1.


Coda

Good peerDependency management in plugins will make this concept work more intuitively in practice. From the blog post, yet again...

One piece of advice: peer dependency requirements, unlike those for regular dependencies, should be lenient. You should not lock your peer dependencies down to specific patch versions. It would be really annoying if one Chai plugin peer-depended on Chai 1.4.1, while another depended on Chai 1.5.0, simply because the authors were lazy and didn’t spend the time figuring out the actual minimum version of Chai they are compatible with.

查看更多
回忆,回不去的记忆
6楼-- · 2018-12-31 07:23

There are some modules and packages only necessary for development, which are not needed in production. Like it says it in the documentation:

If someone is planning on downloading and using your module in their program, then they probably don't want or need to download and build the external test or documentation framework that you use. In this case, it's best to list these additional items in a devDependencies hash.

查看更多
皆成旧梦
7楼-- · 2018-12-31 07:24

Summary of important behavior differences:

  • dependencies are installed on both:

    • npm install from a directory that contains package.json
    • npm install $package on any other directory
  • devDependencies are:

    • also installed on npm install on a directory that contains package.json, unless you pass the --production flag (go upvote Gayan Charith's answer).
    • not installed on npm install "$package" on any other directory, unless you give it the --dev option.
    • are not installed transitively.
  • peerDependencies:

    • before 3.0: are always installed if missing, and raise an error if multiple incompatible versions of the dependency would be used by different dependencies.
    • expected to start on 3.0 (untested): give a warning if missing on npm install, and you have to solve the dependency yourself manually. When running, if the dependency is missing, you get an error (mentioned by @nextgentech)
  • Transitivity (mentioned by Ben Hutchison):

    • dependencies are installed transitively: if A requires B, and B requires C, then C gets installed, otherwise, B could not work, and neither would A.

    • devDependencies is not installed transitively. E.g. we don't need to test B to test A, so B's testing dependencies can be left out.

Related options not discussed here:

devDependencies

dependencies are required to run, devDependencies only to develop, e.g.: unit tests, CoffeeScript to JavaScript transpilation, minification, ...

If you are going to develop a package, you download it (e.g. via git clone), go to its root which contains package.json, and run:

npm install

Since you have the actual source, it is clear that you want to develop it, so by default, both dependencies (since you must, of course, run to develop) and devDependency dependencies are also installed.

If however, you are only an end user who just wants to install a package to use it, you will do from any directory:

npm install "$package"

In that case, you normally don't want the development dependencies, so you just get what is needed to use the package: dependencies.

If you really want to install development packages in that case, you can set the dev configuration option to true, possibly from the command line as:

npm install "$package" --dev

The option is false by default since this is a much less common case.

peerDependencies

(Tested before 3.0)

Source: https://nodejs.org/en/blog/npm/peer-dependencies/

With regular dependencies, you can have multiple versions of the dependency: it's simply installed inside the node_modules of the dependency.

E.g. if dependency1 and dependency2 both depend on dependency3 at different versions the project tree will look like:

root/node_modules/
                 |
                 +- dependency1/node_modules/
                 |                          |
                 |                          +- dependency3 v1.0/
                 |
                 |
                 +- dependency2/node_modules/
                                            |
                                            +- dependency3 v2.0/

Plugins, however, are packages that normally don't require the other package, which is called the host in this context. Instead:

  • plugins are required by the host
  • plugins offer a standard interface that the host expects to find
  • only the host will be called directly by the user, so there must be a single version of it.

E.g. if dependency1 and dependency2 peer depend on dependency3, the project tree will look like:

root/node_modules/
                 |
                 +- dependency1/
                 |
                 +- dependency2/
                 |
                 +- dependency3 v1.0/

This happens even though you never mention dependency3 in your package.json file.

I think this is an instance of the Inversion of Control design pattern.

A prototypical example of peer dependencies is Grunt, the host, and its plugins.

For example, on a Grunt plugin like https://github.com/gruntjs/grunt-contrib-uglify, you will see that:

  • grunt is a peer-dependency
  • the only require('grunt') is under tests/: it's not actually used by the program.

Then, when the user will use a plugin, he will implicitly require the plugin from the Gruntfile by adding a grunt.loadNpmTasks('grunt-contrib-uglify') line, but it's grunt that the user will call directly.

This would not work then if each plugin required a different Grunt version.

Manual

I think the documentation answers the question quite well, maybe you are not just familiar enough with node / other package managers. I probably only understand it because I know a bit about Ruby bundler.

The key line is:

These things will be installed when doing npm link or npm install from the root of a package and can be managed like any other npm configuration parameter. See npm-config(7) for more on the topic.

And then under npm-config(7) find dev:

Default: false
Type: Boolean

Install dev-dependencies along with packages.
查看更多
登录 后发表回答