Liaoct / blog

在这里记录一些个人经验与思考
22 stars 2 forks source link

使用yarn代替npm #7

Open ghost opened 7 years ago

ghost commented 7 years ago

使用yarn代替npm

首先说一下我为什么使用yarn代替npm。

最近在做赢财富的开发,我遇到了一个很麻烦的问题,这个问题也排除了很久才搞定。

我将赢财富源码从svn迁移到git时,顺便也把项目依赖进行了升级,比如升级到webpack 2,babel 6,react 15.x等。在我本地调试通过后,我把package.json及相关配置项推送到了服务端,按理说另外一个同事检出项目后,执行npm install命令进行依赖安装,最后会得到跟我一样的环境并且项目应该良好运行。然而,事情并不是这样。

比如,我其中两个依赖如下:

"webpack": "^2.1.0-beta.19",
"webpack-dev-server": "^2.1.0-beta.0"

同事检出项目package.json文件,安装依赖之后,webpack变成了2.2.0版本,webpack-dev-server变成了2.1.0-beta.25版本,从而导致项目不能运行。

造成这种情况的原因与npm有关,npm install采用的是模糊安装,也就是它会自动寻找与package.json中指定的版本相近的稳定版本,npm的这种看似聪明的做法,实际上破坏了各个依赖之间的对等关系,从而导致虽然都是同一份配置文件,但是不同的人可能得到的安装结果不一样。

yarn发布最初的目的就是解决npm的缺陷问题。yarn与npm类似,是一款js包管理cli工具,甚至它的许多用法与npm极为相似,但是yarn更加高效、安全和可靠。

高效:yarn安装依赖项更加快速,具体的数据对比请自己体验。在我实际的使用中,比npm明显快几乎一半的时间。并且它使用了缓存机制,避免重复下载。

安全:下载之前会检查签名及包的完整性。

可靠:它利用一个yarn.lock文件来保证各个平台下得到的依赖具有一直性。

仅仅以上三点,就让我有理由使用yarn。下面我先说,如何从npm迁移到yarn,这也算是yarn和npm的对比。

安装yarn

安装yarn非常简单,下载yarn,然后一直下一步就行。

在现在的项目中使用yarn

从npm迁移到yarn并不需要做太多的工作。

yarn与npm一样都是通过package.json文件来描述项目依赖,所以如果项目中有了npm的package.json文件,你只需要执行一个命令即可。

yarn //或者yarn install

这个命令会自动分析package.json文件,并且根据它自己的算法,重组node_modules文件夹,生成一个yarn.lock文件,然后项目之间的依赖关系就被yarn管理起来了。

然后,我们应该将package.json 和 yarn.lock文件都推送到服务端,以保证其他同事得到相同的依赖关系。

因此,更加平凡的情况是,从服务端检出项目,项目中应该包含package.json文件和yarn.lock文件。然后执行命令:

yarn //或者yarn install

yarn命令会自动检查package.json和yarn.lock文件,并且安装这两个文件规定的依赖,依赖文件依旧安装在node_modules文件夹中。

安装完依赖之后,项目结构跟之前使用npm并没有什么不同。只不过你在安装的过程中可能已经发现了,yarn更加友好,下载速度更加快,依赖关系更加准确。

依赖安装完成之后,可以使用同npm start等一样的命令来启动项目:

yarn start //或者yarn run start ,yarn run [script]

更加详细的从npm迁移到yarn的介绍请查看这里

yarn与npm共存

实际上,yarn并不强制每一个人都必须使用yarn。上面已经提到过,yarn和npm都使用package.json来描述依赖关系,因此,yarn和npm可以共存。也就是说,其他同事仍然可以保留他的npm,并按照npm的使用方式来运行。

但是,我强烈建议大家应该都使用yarn,因为它能解决依赖不对等带来的麻烦。而它的好处正如官方所言:

what are u waiting for?

yarn的使用方法

yarn与npm的使用方式相似,而且只需要记住几个命令即可。

yarn init

yarn init 与 npm init一样,都是用来初始化一个项目,生成package.json文件。

可以看到这个命令跟npm init几乎一样。

yarn / yarn install

直接执行yarn,其默认会执行yarn install命令。因此,这两种写法的执行结果是一样的。

yarn [install] //同 npm install一样

yarn install与直接执行npm install的效果是一样的,都是安装所有的package.json文件指定的依赖文件。

不同的是yarn install会生成一个yarn.lock文件,或者根据yarn.loack文件(yarn.loack文件已经存在)加载更加确定可靠的依赖。并且package.json和yarn.lock会保持同步更新,新加到package.json中的依赖也会同步更新到yarn.lock。

关于yarn.lock文件后面再解释,在这里就把它当做一个锁文件,这个锁保证加载到的依赖都是确定可靠的。

yarn add

yarn add用来添加一个新依赖。于npm不同的是,npm添加一个新依赖依旧使用npm install xxx.name命令。

安装新依赖时,同npm一样可以指定版本号,选择安装到全局环境或者本地环境,作为项目运行依赖或者开发依赖。

yarn add [package] //和npm install --save [package]一样

yarn add [package]会添加并安装指定的依赖,并且默认加到package.json文件中,同npm install --save [package]的效果一样。

在安装依赖时,仍然可以指定版本号:

yarn add [package@version]

甚至可以使用git上的tag作为版本号:

yarn add [package@tag]//这个tag是指推送到了git,但是还没有发布的版本

yarn的全局安装需要添加global前缀:

yarn global add [package]//和npm install --global [package]一样

与npm的全局安装不同的是,npm的全局安装是使用-g或者--global参数,而yarn是则是在命令前加前缀,而且globa只能加载add前面。

还可以将依赖项安装为开发依赖,从而避免打包工具打包到生产环境,比如mocha、babel等工具就应该被安装为开发依赖。

yarn add [package] [--dev/-D] //和npm install --save-dev [package]一样

yarn新增了一个对等依赖选项,它可以新增一个包并将包声明为对等依赖,表明该项目需要这个对应的依赖包才能正常运行。

yarn add [package] [--peer/-P]

这个命令会在package.json文件中,生成一个peerDependencies属性。比如:

"peerDependencies": {
    "babel": "^6.5.2"
 }

这样就表明了该项目需要对等的babel@^6.5.2才能运行,这与npm的peerDependencies一样,只不过yarn使用明确的参数来声明。

对等依赖在执行yarn时并不会自动安装。也就是另外的同事在检出package.json文件时,如果包含对等依赖,那么这些依赖不会自动安装,yarn只会提示你需要安装这些依赖,所以你需要手动安装这些依赖。

yarn remove

移除一个依赖使用如下命令:

yarn remove [package]

与npm不同的是,npm使用npm uninstall命令,并且npm uninstall可以具有--save、--save-dev等参数。而yarn移除依赖,均只使用yarn remove,package.json文件也会自动被更新。

yarn run

使用yarn run运行package script命令。同npm run 命令一样。

yarn run [script] //npm run [script]

这里yarn start 依然是yarn run start的简写形式。

yarn upgrade
yarn upgrade

这个命令会升级package.json中的所有依赖包,但是,它只会升级到package.json指定的版本区间的最新版本。并且lock文件也会被同步更新。

这种情况通常出现在别人升级了项目依赖,你获取package.json文件后,使用这个命令来更新依赖,以便跟他同步,跟npm update类似。

yarn upgrade [package]

指定包名的情况就比较有意思了,这会更新你指定的某个包。但是,它会更新到包仓库中现在最新的稳定版本,也就是指定包名,它不一定会升级依赖的版本,还可能会降低版本。

比如,我刚才安装了两个依赖:

"dependencies": {
    "webpack": "2.1.0-beta.19",
    "webpack-dev-server": "2.1.0-beta.0"
 }

然后指定包名,升级webpack:

yarn upgrade webpack 

查看package.json文件:

"dependencies": {
    "webpack": "^1.14.0",
    "webpack-dev-server": "2.1.0-beta.0"
 }

webpack的版本没有增加,反而降低了。这是因为,它默认使用了包仓库中的稳定版本。我们一样可以指定版本,来更新到特定的版本:

yarn upgrade [package@version/tag]
yarn cache

这个命令通常用来清空缓存:

yarn cache clean //npm cache clean
yarn publish
yarn publish

用于将代码发布到npm仓库,具体的发布过程这里不再阐述。

掌握以上命令和使用方法,已经足够我们日常开发了。如果需要了解它的更多用法,请移步官网

yarn.lock文件

前面一直提到,yarn可以保证依赖的可靠性,这种可靠性就是通过yarn.lock文件来完成的。

yarn推出之处,正如官方所言,它是为了弥补npm的缺陷而出现的:

yarn既然只是为了弥补npm的缺陷,那么它就并不会完全取代npm,它只是在npm的基础上进行了完善和优化,因此,yarn拉取的package依旧来自npm仓库。

既然拉取的包依然来自npm仓库,仓库本身不会变,所以获取的的模板包的版本就应该一样,那么它是如何保证在不同机器环境下获得一致的版本的呢?

这就有了yarn.lock这个文件。

我们已经知道了yarn和npm一样都是使用package.json来描述项目依赖,但是package.json中的版本号往往不会写的太确切,通常是一个版本区间。这个版本区间规定了大版本号和小版本号,从npm仓库拉取依赖时,会根据这个版本区间来选择一个最新的已经修复了BUG的稳定版本。

这种看似聪明的做法,实际上会为项目带来BUG,甚至完全不能运行。为了防止拉取到不同的版本,yarn使用了lock文件来进一步描述这种依赖关系,yarn.lock文件可以看做是package.json文件的补充。

每新增或更新一个依赖时,yarn就会创建(更新)lock文件,并把这次安装的确定版本号记录在lock文件中。当我们把package.json文件和yarn.lock文件交给其他人时,他通过执行yarn命令,就会得到跟你一模一样的模块版本。

总结

yarn比npm更好用:具有锁文件,保证依赖一直性;并行下载,更快的安装速度;离线下载模式进一步提升速度;自检查保证包完整性;并且从npm转到yarn几乎是零成本。