@冯博
2019.12.5
聊聊前端包管理器
Table of Content
- history
- npm
- yarn
- npm vs yarn
- package.json
- extension
- question
History
startup
https://github.com/osuripple/old-frontend



老的项目创建步骤
- 去 jQuery 官网下载 jQuery
- 去 BooStrap 官网下载 BootStrap
- 去 Underscore 官网下载 Underscore
- ......
- 把他们 copy/paste 到 lib 文件夹下
- 在 html 里面通过 <script> 把它引进来

https://github.com/isaacs

NPM
Node Package Manager
NPM 实现思路
- 买个服务器作为代码仓库(registry),在里面放所有需要的共享的代码
- 发邮件通知 jQuery、Bootstrap、Underscore 作者使用 npm publish 把代码提交到 registry 上,分别取名 jquery、bootstrap 和 underscore
- 社区里的其他人如果想使用这些代码,就把 jquery、bootstrap 和 underscore 写到 package.json 里,然后运行 npm install ,npm 就会帮他们下载代码
- 下载完的代码出现在 node_modules 目录里,可以随意使用了
想法是美好的,但怎么说服人家用呢?

@Ryan Dahl
代码共享
NPM 槽点
- 下载速度慢
- 安装版本不一致
- 日志杂乱多
Q: 下载速度慢
A: 1️⃣ 设置国内镜像源
npm config set registry https://registry.npm.taobao.org --global2️⃣ 搭梯子
3️⃣ 安装 nrm
Q: 安装版本不一致
A: 在 npm 5.0 以后,npm 默认增加了 package.lock.json文件
Q: 打印的日志杂乱多
A: 无解




Yarn
Yarn是由Facebook在 2016 年推出了一个新的 JS 包管理工具
https://code.fb.com/web/yarn-a-new-package-manager-for-javascript/

@arcanis
Yarn 的优点
- 速度快(并行安装、离线模式)
- 安装版本统一(会在项目下生成 yarn.lock)
- 更简洁的日志输出
- 更好的语义化
npm 和 yarn 命令的变化
npm install === yarn
npm install react --save === yarn add react
npm uninstall react --save === yarn remove react
npm install react --save-dev === yarn add react --dev
npm update --save === yarn upgradepackage.json
1. private
如果这个属性被设置为true,npm将拒绝发布它,这是为了防止一个私有模块被无意间发布出去。如果你只想让模块被发布到一个特定的npm仓库,如一个内部的仓库,可与在publishConfig中配置仓库参数或者在发布命令后添加参数 --access=public
2. name
define a package name
e.g: react(?[1])
e.g: @wyny/metro(?[2])
[2]https://docs.npmjs.com/getting-started/scoped-packages
[1]https://zhuanlan.zhihu.com/p/31289463
1. name必须小于等于214个字节
2. name 不能以 “_” 或者 “.” 开头
3. 不能含有大写字母
4. name 会成为 url 的一部分,所以 url 中不能包含 url 非法集
3. version
version必须可以被npm依赖的一个node-semver模块解析
e.g: 0.0.1
4. description
一个描述,方便别人了解你的模块作用,搜索的时候也有用。
5. keywords
一个字符串数组,方便别人搜索到本模块
6. repository
指定仓库地址,方便需要贡献代码的开发者找到仓库存放地址
"repository": {
"type": "git",
"url": "https://cd.i.strikingly.com/strikingly/Bobcat"
},https://www.npmjs.com/package/react
7. engines
你可以指定项目运行的node版本范围,如下:{ “engines” : { “node” : “>=0.10.3 <0.12” } }和dependencies一样,如果你不指定版本范围或者指定为*,任何版本的node都可以。也可以指定一些npm版本可以正确的安装你的模块,例如:{ “engines” : { “npm” : “~1.0.20” } }要注意的是,除非你设置了engine-strict属性,engines属性是仅供参考的
"engines": {
"node": "^6.9"
},8. scripts
scripts属性是一个对象,里边指定了项目的生命周期个各个环节需要执行的命令。key是生命周期中的事件,value是要执行的命令。具体的内容有 install start stop 等
"scripts": {
"start": "apollo dev && cross-env API_ENV=dev node scripts/bootstrap dev",
"start:mock": "cross-env API_ENV=mock node scripts/bootstrap dev",
"start:vtest": "cross-env API_ENV=vtest node scripts/bootstrap dev",
"build:dev": "apollo dev && cross-env API_ENV=dev node scripts/bootstrap build",
"build:vtest": "cross-env API_ENV=vtest node scripts/bootstrap build",
"build:test": "apollo test && cross-env API_ENV=prod node scripts/bootstrap build",
"build:prod": "apollo prod && cross-env API_ENV=prod node scripts/bootstrap build",
}9. bin
很多模块有一个或多个可执行文件需要配置到PATH路径下, 在安装的时候会放到 /node_modules/.bin 目录下,这样就可以在项目中直接使用这些命令
"bin": {
"apollo": "./lib/cli/index.js"
}10. lint-staged
eslint 特殊配置文件,每次 commit 的时候只对 stage 中的文件进行检查
https://zhuanlan.zhihu.com/p/27094880
"lint-staged": {
"*.{js,jsx,es6}": [
"npm run lint:error --",
"git add"
]
},11. main
指定模块的入口,在 import/require 一个包名的时候默认会去读这个字段指定的文件
"main": "lib/index.js",12. files
指定哪些文件或者文件夹需要在执行 npm publish 的时候发布到 npm 上去, 配置上和 .npmignore 恰恰相反,但功能是一样的
"files": [
"lib",
"master.js",
"slave.js",
"index.js"
],13. devDependencies
项目依赖包,主要针对项目中在开发阶段需要用到的工具和库,等到了生产环境并不需要这些同样能正常 run 起来,一般我们把它放在 devDenpendencies,比如像 webpack、TypeScript、prettier 等
npm install babel gulp --save-dev
// 或者
npm i babel gulp -D
// 亦或
yarn add babel gulp --dev14. dependencies
项目依赖包,主要记录在线上环境必须依赖的包,如果不安装这个包项目就跑步起来的包,一般放在这个里面
npm install react react-dom --save
// 或者
npm i react react-dom -S
// 亦或
yarn add react react-dom15. peerDependencies
https://github.com/mui-org/material-ui/blob/v0.x/package.json#L46
这个配置的目的是让npm知道,如果要使用此插件模块,请确保安装了兼容版本的宿主模块。比如我们在使用 antd 或者 material-ui 的时候,他们都是 基于 react 开的,那么我们需要用它的时候就必须得安装 react
"peerDependencies": {
"react": ">=16.0.0",
"react-dom": ">=16.0.0"
},yarn add xx --peer16. optionDependencies
如果一个依赖模块可以被使用, 同时你也希望在该模块找不到或无法获取时npm继续运行,你可以把这个模块依赖放到optionalDependencies配置中。这个配置的写法和dependencies的写法一样,不同的是这里边写的模块安装失败不会导致npm install失败,optionalDependencies 中的配置会覆盖dependencies中的配置,最好只在一个地方写
"optionalDependencies": {
"fsevents": "^1.1.1"
}yarn PnP
1、 磁盘占用高问题
2、下载速度问题


- 将依赖包的版本区间解析为某个具体的版本号
- 下载对应版本依赖的 tar 包到本地离线镜像
- 将依赖从离线镜像解压到本地缓存
- 将依赖从缓存拷贝到当前目录的 node_modules 目录
执行 yarn install 发生了什么
PnP 是如何处理的?
Yarn 会维护一张静态映射表,该表中包含了以下信息:
- 当前依赖树中包含了哪些依赖包的哪些版本
- 这些依赖包是如何互相关联的
- 这些依赖包在文件系统中的具体位置
这个映射表在 Yarn 的 PnP 实现中对应项目目录中的 .pnp.js 文件。
PnP 好处
- 安装依赖的速度得到了空前的提升
- CI 环境中多个 CI 实例可以共享同一份缓存
- 同一个系统中的多个项目不再需要占用多份磁盘空间
Demo
https://github.com/sorrycc/hello-yarn-2
Question
>2.0、<2.0、~2.0、 ^2.0 是什么意思?

如何发布一个包到 npm 上供社区使用?
完
谢谢
Reference
- https://www.npmjs.com/
- https://yarnpkg.com/en/
- https://zhuanlan.zhihu.com/p/23493436
- https://zhuanlan.zhihu.com/p/24357770
聊聊前端包管理器
By bo feng
聊聊前端包管理器
- 251