{"action":"created","issue":{"url":"https://api.github.com/repos/weihong1028/blog/issues/5","repository_url":"https://api.github.com/repos/weihong1028/blog","labels_url":"https://api.github.com/repos/weihong1028/blog/issues/5/labels{/name}","comments_url":"https://api.github.com/repos/weihong1028/blog/issues/5/comments","events_url":"https://api.github.com/repos/weihong1028/blog/issues/5/events","html_url":"https://github.com/weihong1028/blog/issues/5","id":316899099,"number":5,"title":"npm vs cnpm","user":{"login":"weihong1028","id":7336582,"avatar_url":"https://avatars3.githubusercontent.com/u/7336582?v=4","gravatar_id":"","url":"https://api.github.com/users/weihong1028","html_url":"https://github.com/weihong1028","followers_url":"https://api.github.com/users/weihong1028/followers","following_url":"https://api.github.com/users/weihong1028/following{/other_user}","gists_url":"https://api.github.com/users/weihong1028/gists{/gist_id}","starred_url":"https://api.github.com/users/weihong1028/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weihong1028/subscriptions","organizations_url":"https://api.github.com/users/weihong1028/orgs","repos_url":"https://api.github.com/users/weihong1028/repos","events_url":"https://api.github.com/users/weihong1028/events{/privacy}","received_events_url":"https://api.github.com/users/weihong1028/received_events","type":"User","site_admin":false},"labels":[{"id":895801213,"url":"https://api.github.com/repos/weihong1028/blog/labels/Script","name":"Script","color":"7057ff","default":false}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":1,"created_at":"2018-04-23T17:00:46Z","updated_at":"2018-04-23T17:01:33Z","closed_at":null,"author_association":"OWNER","body":"## 前言\r\n\r\n在公司项目中，由于比较多地采用了 cnpm 来替换 npm 安装依赖包。使用过程中难免会出现一些关于 cnpm 和 npm 这两者依赖管理这一块各有什么不同的疑问。虽然很多时候并不需要我们去关心这个过程，因为我们一般的使用基本为 `npm install [xxx]` 这样的安装命令。但事实上，有时候出现的一些依赖包的混乱问题，如果我们不能很清楚地认识到这两者各自的安装原理是什么，势必会造成很多麻烦。"},"comment":{"url":"https://api.github.com/repos/weihong1028/blog/issues/comments/383648206","html_url":"https://github.com/weihong1028/blog/issues/5#issuecomment-383648206","issue_url":"https://api.github.com/repos/weihong1028/blog/issues/5","id":383648206,"user":{"login":"weihong1028","id":7336582,"avatar_url":"https://avatars3.githubusercontent.com/u/7336582?v=4","gravatar_id":"","url":"https://api.github.com/users/weihong1028","html_url":"https://github.com/weihong1028","followers_url":"https://api.github.com/users/weihong1028/followers","following_url":"https://api.github.com/users/weihong1028/following{/other_user}","gists_url":"https://api.github.com/users/weihong1028/gists{/gist_id}","starred_url":"https://api.github.com/users/weihong1028/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/weihong1028/subscriptions","organizations_url":"https://api.github.com/users/weihong1028/orgs","repos_url":"https://api.github.com/users/weihong1028/repos","events_url":"https://api.github.com/users/weihong1028/events{/privacy}","received_events_url":"https://api.github.com/users/weihong1028/received_events","type":"User","site_admin":false},"created_at":"2018-04-23T17:01:33Z","updated_at":"2018-04-23T17:01:33Z","author_association":"OWNER","body":"## cnpm\r\n\r\n[cnpm](https://npm.taobao.org) 的来源还得从淘宝的提供的 npm 镜像说起，根本问题是国内网络直连 npm 源不畅，所以淘宝通过其内部的网络来同步国际 npm 镜像并提供给外网使用。本来呢，我们要使用淘宝提供的 npm 镜像并不需要直接使用 cnpm 的，我们可以通过 `npm config set registry url` 来更换源，也可以通过 nrm 这样 npm 源管理器切换源。但是，cnpm 却不只是将 npm 的源更换成淘宝源这么简单，更准确地说，cnpm 是一个拥有另一套工作原理的 npm 包管理工具。\r\n\r\n众所周之，cnpm 的主要特点是快。那么 cnpm 是如何工作的，我们从官方 github 上可以得知，cnpm 使用的是 [npminstall](https://github.com/cnpm/npminstall) 来使得 `npm install` 变得更快和更简单的。\r\n\r\n**目录生成规则**\r\n\r\n从 [npminstall](https://github.com/cnpm/npminstall) 的 README 中可以了解到 cnpm 生成的 node_modules 文件目录结构将遵循以下原则：\r\n\r\n2. 同名依赖存在多个版本，以 package.json 指定版本的为准（例子二：koa），如果没有则以更新版本为准。（例子一：ms）\r\n3. 默认情况下，npminstall 总会尝试安装符合语义版本控制的最新的那个版本的依赖包。（例子一：ms、二：koa）\r\n4. 依赖的子依赖将被以软链的形式链接在依赖的 `node_modules/${子依赖名}` 文件夹下，如果 package.json 中没有声明到这些依赖，使用 cnpm ls 查看结构时，这些包会带上 `extraneous` 标志。（例子一：ms）\r\n5. 所有的依赖以及子依赖都会被以隐藏文件夹的形式安装在项目根目录下的 `node_modules` 文件夹下，文件夹名格式为 `.{version}@{name}`，eg：`.2.2.0@debug`。（例子一）\r\n6. `--flatten` 模式下（需手动开启），npminstall 将尝试使用祖先的依赖关系来最小化依赖树。即不同版本如果能互相兼容则不安装其它非必要版本，减小 node_modules 的体积。（例子三）\r\n\r\n**例子一：base structure**\r\n\r\n- app: `{ \"dependencies\": { \"debug\": \"2.2.0\", \"psleep\": \"2.2.0\" } }` (root)\r\n- debug@2.2.0: `{ \"dependencies\": { \"ms\": \"0.7.1\" } }`\r\n- psleep@2.0.0: `{ \"dependencies\": { \"ms\": \"^0.7.1\" } }`\r\n\r\n```bash\r\napp/\r\n├── package.json\r\n└── node_modules\r\n    ├── .0.7.1@ms # 包实体\r\n    ├── .0.7.3@ms # 包实体\r\n    ├── .2.2.0@debug # 包实体\r\n    │   ├── node_modules\r\n    │   │   └── ms -> ../../.0.7.1@ms # 软链\r\n    ├── .2.0.0@psleep # 包实体\r\n    │   ├── node_modules\r\n    │   │   └── ms -> ../../.0.7.3@ms # 软链\r\n    ├── debug -> .2.2.0@debug # 软链\r\n    ├── psleep -> .2.0.0@psleep # 软链\r\n    └── ms -> .0.7.3@ms # extraneous 软链，存在多个版本且无指定版本，以最靠前的版本号的为准\r\n```\r\n\r\n**例子二：latest install**\r\n\r\n- app: `{ \"dependencies\": { \"koa\": \"1.1.0\", \"mod\": \"1.1.0\" } }` (root)\r\n- mod@1.1.0: `{ \"dependencies\": { \"koa\": \"~1.1.0\" } }`\r\n\r\n```bash\r\napp/\r\n├── package.json\r\n└── node_modules\r\n    ├── .1.1.0@koa # 包实体\r\n    ├── .1.1.2@koa # 包实体\r\n    ├── .1.1.0@mod # 包实体\r\n    │   ├── node_modules\r\n    │   │   └── koa -> ../../.1.1.2@koa # 软链\r\n    ├── koa -> .1.1.0@koa # 软链，存在多个版本以 package.json 指定版本的为准\r\n    └── mod -> .1.1.0@mod # 软链\r\n# 由于 npminstall 总会尝试安装符合要求的最新版本\r\n# 那么 koa: ~1.1.0 意味着最终会安装成 koa@1.1.2\r\n# 所以最终这个依赖关系会生成两个不同版本的 koa 包\r\n```\r\n\r\n**例子三：flatten mode**\r\n\r\n- app: `{ \"dependencies\": { \"koa\": \"1.1.0\", \"mod\": \"1.1.0\" } }` (root)\r\n- mod@1.1.0: `{ \"dependencies\": { \"koa\": \"~1.1.0\" } }`\r\n\r\n```bash\r\napp/\r\n├── package.json\r\n└── node_modules\r\n    ├── .1.1.0@koa # 包实体\r\n    ├── .1.1.0@mod # 包实体\r\n    │   ├── node_modules\r\n    │   │   └── koa -> ../../.1.1.0@koa # 软链\r\n    ├── koa -> .1.1.0@koa # 软链\r\n    └── mod -> .1.1.0@mod # 软链\r\n# 开启 flatten mode 后，由于祖先中己有 koa@1.1.0\r\n# koa: ~1.1.0 将直接采用祖先中兼容的版本 koa@1.1.0\r\n# 所以最终这个依赖关系只会生成 koa@1.1.0 一个版本\r\n```"}}
{"actor":{"display_login":"weihong1028"}}