npm依赖应该放在哪一个字段?

在一个前端项目中,我们把依赖通过特有的pacakge.json,其中的dependencies字段提供给npm来安装依赖。自从npm升级之后,就增加了依赖的控制,例如增加package-lock.json来锁定开发时使用的依赖版本。而我们的项目,很多会上到CI系统去构建,然后通过CD系统来部署到我们的生产环境中。这就会带来一个问题,我们在开发环境中使用的依赖,应不应该在CI系统中安装,因为大部分CI系统都不具备开发环境那么宽松的编译环境。那么到底要怎么来安排我们的开发环境依赖、CI环境依赖和生产环境依赖?

首先理解三种依赖:

dependencies

本包的依赖。即当另外一个包/项目依赖你这个包的时候,你的包必须依赖的其他包。npm在安装一个包的时候,它不单单只安装了这个包本身,同时还安装了dependencies里面指定的依赖。例如:

npm i databaxe

你会发现,不单单databaxe这个包被安装了,databaxe这个包的package.json里面的dependencies字段里面的包也被安装了(devDependencies里面的包没有被安装,optionalDependencies 也会被安装,但是 optionalDependencies 有一点不同,如果里面的包在安装过程中失败了,那么不会影响安装过程,这个包因为安装失败而被忽略,具体阅读这里)。

由于我们大部分前端项目,在现阶段还是需要通过构建工具构建的,因此,对于一个完整的上线项目而言,dependecies里面只需要放置node端的modules即可,例如express等服务器依赖包,而不需要把jquery等前端代码需要的包放在 dependencies 里面,因为,前端代码的依赖包会被webpack等工具打包进去。这些在构建阶段用到的包,可以放在 devDependencies 里面。

但是,对于一个组件,或者一个第三方前端包,你需要考虑的是,你的包被其他项目引用时,会由其他项目去自己实现构建过程。你的代码里面的依赖可以和该项目其他依赖的依赖共用,例如前面提到的jquery,有可能你的包和别人的包都用到了,是一个公用的依赖,因此,你不应该把jquery的代码打包到你自己发布的包代码中,而是作为依赖的形式,添加到dependencies中去。这样,当别人的项目在构建时,会同时使用你的包、别人的包、jquery去进行构建。这样保证了代码的复用和代码量的减少。

devDependencies

本包的开发依赖。当你在开发一个包的时候,你可能需要用到一些用于开发的依赖,例如webpack,babel等。这些依赖只会在开发构建阶段(也包括你上到CI系统进行构建时)用到,而不会在生产环境用到。npm在安装一个包时,这个包pacakge.json里面的devDependencies里面的包不会被同时安装。但你在把这个包的源码克隆到本地,并准备开发时,你在项目目录下执行

npm i

你就会发现,这些包被安装了。而且,这些包里面的package.json里面的规定,也完全和本文所讲的规则一致。也就是说,虽然这些包是放在devDependencies里面,但是,它们被安装时,它们自己package.json里面的devDependencies里面的包不会被安装,因为,这些是它们自己的开发依赖。当它们被安装时,是被当作一个正式的第三方产品来使用。

optionalDependencies

本包的可选依赖。当你在开发一个包的时候,你可能希望有些包是可选的,比如,一个包由AB两位同学在开发。其中A开发时由于自己的习惯,使用到了一个包,但是B同学没有这个习惯,他不希望自己在开发时这个多余的包被下载浪费时间,这个时候,这个包应该被放在optionalDependencies里面。npm在安装一个包的时候,该包的optionalDependencies里面的依赖也不会被安装。但是,在开发时,你执行

npm i

这些包还是会被安装。你可能会问,那它和devDependencies有啥区别?区别在于,你可以使用参数让它们不被安装:

npm i --no-optional

通过这个参数,在执行npm i的时候,optionalDependencies就不会被安装了。

不同的开发环境如何正确选择一个包应该放在哪里?

  • 但凡用于构建的、测试的、质量检查的,都放到devDependencies里面
  • 但凡仅用于本地开发,例如预览等,都放到optionalDependencies里面

例如,我们经常会在本地开发时,起一个devserver来对开发结果进行预览,但是在上到CI系统时,这个功能根本不会用到,所以,我们把这些依赖放到optionalDependencies里面,而在CI系统构建脚本中,一定要加上--no-optional参数。

所以,最终总结下来,我们大致可以这么认为:dependencies是给生产环境用的,devDependencies是给CI构建用的,optionalDependencies是给本地开发用的。虽然这个理解有偏差,但对于对这个知识点刚刚了解的同学来说,非常有用。