@冯博

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 --global

     2️⃣ 搭梯子

     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 upgrade

package.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 --dev

14. dependencies

项目依赖包,主要记录在线上环境必须依赖的包,如果不安装这个包项目就跑步起来的包,一般放在这个里面

npm install react react-dom --save

// 或者

npm i react react-dom -S

// 亦或

yarn add react react-dom

15. 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 --peer

16. optionDependencies

如果一个依赖模块可以被使用, 同时你也希望在该模块找不到或无法获取时npm继续运行,你可以把这个模块依赖放到optionalDependencies配置中。这个配置的写法和dependencies的写法一样,不同的是这里边写的模块安装失败不会导致npm install失败,optionalDependencies 中的配置会覆盖dependencies中的配置,最好只在一个地方写

  "optionalDependencies": {
    "fsevents": "^1.1.1"
  }

yarn PnP

1、 磁盘占用高问题

2、下载速度问题

  1. 将依赖包的版本区间解析为某个具体的版本号
  2. 下载对应版本依赖的 tar 包到本地离线镜像
  3. 将依赖从离线镜像解压到本地缓存
  4. 将依赖从缓存拷贝到当前目录的 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