有则笑话,如此讲到:“老丈人爱吃核桃,昨天买了二斤陪妻子送去,老丈人年轻时练过武,用手一拍核桃就碎了,笑着对我说:你还用锤子,你看我用手就成。我嘴一抽,来了句:人和动物最大的区别就是人会使用工具。……”。撇开这样特例场景,这句话还是非常用有道理的;毕竟从远古石器时期或更早,到如今,所言之语,所穿之衣,代步之车,所学的知识,所晓的常识…..皆是工具;可以说绝大部分人之间的差异(天才级除外),仅在于工具使用之优劣罢了。在工具的使用中,很多人极大程度上停留于会用层面,如若不遇到问题,几乎就处于停滞;这本身倒也没有问题,但可能因为没有透彻的了解,而错失了对该物可以拥有的想象力,从而错过了许多本该有的美好,如此的可惜。
坦白说,在从事前端方面工作,蛮长一段时间内,就因缺乏对 Npm 有足够的认知,使得后来对其诸多讯息,颇感「相见恨晚」;在本篇中,将客观陈述 Npm 与 Yarn 的各自功用,以此显出两者间的差异;同时,以比较的形式,列出「常用命令清单」,以方便使用之时,作为参考(将陆续更新以完善);同时也欲借此,再次倡导那经典名言:「工欲善其事,必先利其器」,与诸君共勉。
npm
即:npm package manager ,是一种重用其他开发人员的代码的方法,也是一种与他人共享代码的方式,并且可以很容易地管理不同版本的代码。npm
开始作为 Node 包管理器,所以你会发现很多模块可以在服务器端使用。也有很多的包添加命令供您在命令行中使用。你还会发现可以在前端使用的软件包。
npm
由三个不同的部分组成:网站,注册表和 CLI 。该网站是用户发现软件包的主要工具,注册表是一个关于软件包信息的大型数据库,而 CLI 则是开发者如何在注册表上发布他们的软件包或下载他们希望安装的软件包。更多详细内容,可参见 what-is-npm。
Yarn
对你的代码来说是一个包管理器, 你可以通过它使用全世界开发者的代码,或者分享自己的代码。 Yarn 做这些快捷、安全、可靠,所以你不用担心什么。通过Yarn
,你可以使用其他开发者针对不同问题的解决方案,使自己的开发过程更简单。 使用过程中遇到问题,你可以将其上报或者贡献解决方案。一旦问题被修复,Yarn会更新保持同步。
Yarn
是 Facebook, Google, Exponent 和 Tilde 开发的一款新的 JavaScript 包管理工具。它并没有试图完全取代 npm。Yarn 同样是一个从 npm 注册源获取模块的新的 CLI 客户端。注册的方式不会有任何变化 —— 你同样可以正常获取与发布包。它存在的目的是解决团队在使用 npm
面临的少数问题。当然,在 Node
版本断更替中,Npm 本身也在积极更新。
npm
开始作为 Node 包管理器,所以它的安装是跟 Node.js 捆绑在一起的。至于如何安装 Node.js, Npm 官方,在 Installing Node.js and updating npm 做了阐述。之前在不同平台尝试更新 Node.js 之时,也是遇到过各种问题,有在 NodeJs 升级/安装折腾记 一文做了记载;折腾许久,得出的结论跟官网一致:
如果您使用的是OS X或Windows,安装Node.js的最佳方法是:使用 Node.js下载页面中的一个安装程序。(笔者微注:如是我中国大陆用户,去淘宝 Node.js 镜像下载,会是快速的法子)。
对于如何安装 Yarn
,Yarn 官方给出了很全面的说明,详见 Install Yarn;涵盖 MacOs,Windows,Linux 等平台,并且还给出一些备用安装方式,譬如通过 npm
来安装:
npm install --global yarn
当然,Yarn
官方在 Yarn 备选安装方式有明确讲道:
一般来说, 不推荐通过
npm
安装Yarn
,在用基于 Node 的包管理器安装 Yarn 时,该包未被签名, 并且只通过基本的SHA1
散列进行唯一完整性检查。这在安装系统级应用时有安全风险。因为这些原因,高度推荐用你的操作系统最适合的方式来安装 Yarn。
但在实际使用中,这倒是最为方便的方式之一,迄今倒也没遇到什么问题;当然,最好按照官方推荐的方式;如果你使用并熟悉 Mac 操作系统,用推荐方式安装 Yarn 也是很简单:brew install yarn
(笔者注)。
对于如何更新 Yarn,可以结合安装时候对应命令;如果是 Mac 操作系统,使用 brew
安装,那么如此操作予以更新:
brew upgrade yarn
如果 Yarn
通过 Debian / Ubuntu 包安装,则可以运行如下命令予以更新:
sudo apt-get update && sudo apt-get install yarn
也可以使用 yarn
本身来更新自己:
yarn global add yarn
如果有意了解更多如何更新 yarn
的方法,可参见:yarn self-update should update using the same installation method originally used。
Npm | Yarn | 功能描述 |
---|---|---|
npm install(npm i) | yarn install(yarn) | 根据 package.json 安装所有依赖 |
npm i –save [package] | yarn add [package] | 添加依赖包 |
npm i –save-dev [package] | yarn add [package] –dev | 添加依赖包至 devDependencies |
npm i -g [package] | yarn global add [package] | 进行全局安装依赖包 |
npm update –save | yarn upgrade [package] | 升级依赖包 |
npm uninstall [package] | yarn remove [package] | 移除依赖包 |
Npm | Yarn | 功能描述 |
---|---|---|
npm run | yarn run | 运行 package.json 中预定义的脚本 |
npm config list | yarn config list | 查看配置信息 |
npm config set registry 仓库地址 | yarn config set registry 仓库地址 | 更换仓库地址 |
npm init | yarn init | 互动式创建/更新 package.json 文件 |
npm list | yarn list | 查看当前目录下已安装的node包 |
npm login | yarn login | 保存你的用户名、邮箱 |
npm logout | yarn logout | 删除你的用户名、邮箱 |
npm outdated | yarn outdated | 检查过时的依赖包 |
npm link | yarn link | 开发时链接依赖包,以便在其他项目中使用 |
npm unlink | yarn unlink | 取消链接依赖包 |
npm publish | yarn publish | 将包发布到 npm |
npm test | yarn test | 测试 = yarn run test |
npm bin | yarn bin | 显示 bin 文件所在的安装目录 |
yarn info | yarn info | 显示一个包的信息 |
对于以上还须对于,还须做如下补充性说明:
cnpm
:
alias cnpm="npm --registry=http://registry.cnpmjs.org --cache=$HOME/.npm/.cache/cnpm" npm rebuild node-sass
;当使用 Sass(Scss) 来作样式表预处理器,再打包的时候,你可能会遇见如下错误;而解决此问题,最为简单的方式即使用 rebuild
命令,对 node-sass 进行重建即可。Module build failed: ModuleBuildError: Module build failed: Error: Node Sass does not yet support your current environment: This usually happens because your environment has changed since running npm install. Run npm rebuild node-sass to build the binding for your current environment.
node_modules
目录生成一份yarn.lock
文件;找到合适的软件包可能相当具有挑战性 ——,毕竟有成千上万个模块供你选择。https://npms.io/ ,这个网站的存在,让这项任务轻松很多;它显示了质量,受欢迎程度和维护等指标。这些计算是基于模块是否具有过时的依赖关系,是否配置了linters,是否包含测试或是否进行了最近的提交。
显而易见,经由 npm
或是 yarn
安装,并被放置在 ./node_modules
目录中的包,其二进制可执行文件可访问 ./node_modules/.bin
,那么该如何从项目根目录中调用它呢?以下提供了几种方式,你可以从中任意选择一种,来达到你的目的:
为了方便举例,这里以运行 responsive-email-template(制作更好的响应式邮件模板)作为示例来作说明;其中有用到 mjml 这个库;此库被推荐的方式是在本地安装和使用;所以,要运行对应命令,你可以操作她,使用以下办法:
在你安装 MJML 的文件夹中,你现在可以运行:
./node_modules/.bin/mjml --watch src/index.mjml -o dist/index.html
./node_modules/.bin/
添加至环境变量export PATH="$PATH:./node_modules/.bin"
mjml --watch src/index.mjml -o dist/index.html
npm bin
$(npm bin)/mjml --watch src/index.mjml -o dist/index.html
// 将命令配置在 package.json 文件,使用 scripts 字段定义脚本命令
"scripts": {
"mjml": "mjml --watch src/index.mjml -o dist/index.html"
}
// 使用之时,只需运行如下命令即可:
npm run mjml
注意:npx 包含在
npm > v5.2
,或可以分开安装。
# npm install -g npx
npx mjml --watch src/index.mjml -o dist/index.html
众所周知,Node 版本的迭代速度很快,且开发者也比较能及时使用新版本开发;但,对于维护早期的项目,切换 node 版本就成了一个问题;幸好的是,已经有了比较成熟的几个方案;比如盛名遐迩的 nvm 以及 @tj 大神的 n;相比之下,n
使用与安装都会更简单,这也完全是作者理念的呈现;其区别主要是:
n
是须要基于 node
环境,它是全局安装的一个 npm 包;在安装之时,n
会先将指定版本的 node 存储下来,然后将其复制到我们熟知的路径 /usr/local/bin,非常简单明了。当然由于 n
会操作到非用户目录,所以需要加 sudo 来执行命令;nvm
是一个独立软件包:在安装之时,nvm
将不同的 node 版本存储到 ~/.nvm// 下,然后修改 $PATH
,将指定版本的 node 路径加入,如此我们调用的 node 命令,即是所指定版本的 node;一旦我们选择了我们的模块,我们应该看看文档,并检查开放的问题,以更好地了解我们将要在我们的应用程序中需要什么。不要忘记,您使用的 npm 包越多,存在易受攻击或恶意攻击的风险就越高。
如果你想从cli打开模块的主页,你可以这样做:
npm home axios
要检查未决的问题或公开的路线图(如果有的话),你可以试试这个:
npm bugs axios
另外,如果你只是想检查模块的 git 仓库,请输入:
npm repo axios
npm(Yarn 亦同)允许在 package.json
文件里面,使用 scripts
字段定义脚本命令。它支持通配符、变量、钩子、外部传参、支持并发 & 异步执行等等;所以,完全可以借助 npm script
,打造属于自己的高效工作流。鉴于篇幅,这部分就不在这里多做赘述,具体使用以及运行原理等,可以参见 @阮一峰 所写的文章:npm scripts 使用指南。
具体例子来说,有些时候会有需求要删除 Git 仓库所有提交历史,而保留代码为当前状态;而删除 .git
文件夹可能会导致您的 git 存储库中的问题;所以可以使用另一种更为安全的办法:详见如何删除 Git 仓库所有提交历史记录。但这个操作一套打下来,不免劳神费力。那么借助 npm script
,运行如下命令,一键搞定(当然命令可自行定义);如果你有需求执行更多,也可以在后面追加更多操作。
"scripts": {
"clean-commit": "git checkout --orphan latest_branch && git add -A && git commit -am 'clean past commit history ?' && git branch -D master && git branch -m master && git push -f origin master"
}
// 或者也可以将其作下拆解,譬如像这样:
"scripts": {
"recommit": "git add -A && git commit -am 'clean past commit history ?'",
"repush": "git branch -D master && git branch -m master && git push -f origin master",
"clean-commit": "git checkout --orphan latest_branch && npm run recommit && repush"
}
有时候一个项目周期很长,在不断开发的同时,而依赖的库也会有很大改变;有时候你可能只想运行 npm i
更新没有下载的插件,却不想偶尔会将依赖的一些其他插件更到最新,导致各种奇葩问题;package-lock.json
和 yarn.lock
就是为解决这种问题而设定的存在。
使用 npm
或者 yarn
,都会有 pacakge.json
这个文件,用以标出自己项目对 各库包的依赖。举个例子来说,你的项目中有如下依赖:
"dependencies": {
"jade-package": "^2.3.4"
}
这其中的 ^ 是定义了向后(新)兼容依赖;在 npm<5.0
以前,如果 jade-package 的版本超过2.3.4,并在大版本号(2)上相同,就允许下载最新版本的 jade-package 库包,例如实际上可能运行npm i
时候,下载的具体版本可能是2.5.8。
多数情况下,这种向后兼容依赖下载最新库包,是没有问题的;然而,因为 npm
是开源世界,各库包的版本语义可能并不相同,不是所有开发者都能严格遵守这一原则:相同大版本号的同一个库包,其接口符合兼容要求。而且,不同的库包之间也存在其他依赖。理想状态下使用语义化版本发布补丁不会包含大的变化,但不幸的是这必非真理。npm 的这种策略,有可能导致两台拥有相同 package.json
文件的机子,实际上安装了不同版本的包,这可能导致一些错误。有时候,相同机器稍不留神的一个 npm i
,就可能导致 node_modules 中安装的实际依赖被更新,也就可能导致项目运行呈现,被面目全非。
yarn.lock
就是为解决此问题而衍生的存在;为了跨机器安装得到一致的结果,Yarn 需要比你配置在 package.json 中的依赖列表更多的信息。 Yarn 需要准确存储每个安装的依赖是哪个版本;它类似于 npm 的 npm-shrinkwrap.json,并且无副作用。只是需要注意的是:
yarn.lock
文件是自动产生的,而且应该完全被Yarn
管理。 当你用 Yarn CLI 增加/升级/删除依赖,它将自动更新你的yarn.lock
文件。 不要直接编辑这个文件,那样很容易弄坏某些东西。
当 Node.js
升级之 v8.0 以后,自带的 npm 也升级到了5.0;带来速度上很大提升之外,也带来了其他很大变大;这其中就包括 package-lock.json
:安装模块操作(改变 node_modules 文件夹内容)会生成或更新 package-lock.json 文件;package-lock.json
之于 npm,即是yarn.lcok
之于 yarn 的翻版;更多信息可参见 npm package-lock.json。
另外,值得一提的是,在 Github 上有人专门提供了 Synp 工具,用以:将yarn.lock转换为package-lock.json,反之亦然(Convert yarn.lock to package-lock.json and vice versa)。
相比 Npm 的默认配置,Yarn 获赞颇多。用其可以方便生成锁文件,安装依赖非常迅速,且会自动添加进 package.json,同时安装与使用 Yarn 的成本也极小,这使得 Yarn 可以完美替代 npm。yarn 之于 npm,有点像当年的 io.js 和 node.js,殊途同归,都是为了进一步解放和优善生产力;如今,在 Yarn 的影响下,npm 本身也改善不少(version >= 5.0)。最后要说的是,不管用何种工具,全面了解其全貌,知其优晓其劣,方能更好驾驭它,使之为自己高效、快意的生活增姿添色。
@2017-12-30 于深圳.南山 Last Modify: 2018-01-02