{
"name": "pony-react-ui",
"version": "1.0.0",
"description": "React组件库",
"main": "index.js",
"module": "lib/index.js",
"types": "lib/index.d.ts",
"author": "",
"license": "MIT",
"homepage": "", // 项目官网的url
"keywords": [], // 关键字,数组、字符串。还是方便屌丝们在npm search中搜索。
"directories": {
bin: "./bin",
doc: "./doc",
lib: "./lib",
man: "./man"
},
"scripts": {},
// 你项目的提交问题的url和(或)邮件地址。这对遇到问题的屌丝很有帮助
"bugs": {
"url": "",
"email": ""
},
// 指定你的代码存放的地方。这个对希望贡献的人有帮助。如果git仓库在github上,那么npm docs命令能找到你。
"repository": {
"type": "git",
"url": ""
},
// files是一个包含项目中的文件的数组。如果命名了一个文件夹,那也会包含文件夹中的文件。(除非被其他条件忽略了)
// 你也可以提供一个.npmignore文件,让即使被包含在files字段中得文件被留下。其实就像.gitignore一样。
"files": [],
"dependencies": {},
"devDependencies": {}
}
main
main字段配置一个文件名指向模块的入口程序。如果你包的名字叫foo,然后用户require("foo"),main配置的模块的exports对象会被返回。
types
定义类型声明入口文件
module
之前查阅了package.json的文档,并没有找到我们想要的 module 字段的定义,无意中看了一个帖子才知道它是 rollup 中最早就提出的概念 — pkg.module。在这之前 npm 包大都是基于 CommonJS 规范的。当我们当 require 引入包的时候,就会根据 main 字段去查找入口文件。
而在 ES6 规范出现后,ES6 定义了一套基于 import、export 操作符的模块规范。它与 CommonJS 规范最大的区别在于 ES6 中的 import 和 export 都是静态的。静态意味着一个模块要暴露或引入的所有方法在编译阶段就能全部确定,之后不能再改变。这样做的好处就是打包工具在打包阶段就可以分析出代码中用到了某个模块中的哪几个方法。其它没有用到的方法就可以从最终的 bundle 文件中剔除掉。这样既可以减少 bundle 文件的大小,又可以提高脚本的执行速度。这个机制被称为 Tree Shaking。在这个构建思想的基础上,开发基于 ES Module 规范的包是很有必要的。
之前我们说过 CommonJS 规范的包都是以 main 字段表示入口文件了,如果 ES Module 的也用 main 字段,就会对使用者造成困扰,如果他的项目不支持打包构建,比如大多数 node 项目(尽管 node9+ 支持 ES Module),这时库开发者的模块系统跟项目构建的模块系统的冲突,更像是一种规范上的问题。况且目前大部分仍是采用 CommonJS,所以 rollup 便使用了另一个字段:module。如下配置:
{
"name": "mypck",
"version": "1.0.0",
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js"
}
webpack 从版本 2 开始也可以识别 pkg.module 字段。打包时,如果存在 module 字段,会优先使用,如果没找到对应的文件,则使用 main 字段,并按照 CommonJS 规范打包。所以目前主流的打包工具(webpack, rollup)都是支持 pkg.module 的,鉴于其优点,module 字段很有可能加入 package.json 的规范之中。另外,越来越多的 npm 包已经同时支持两种模块,使用者可以根据情况自行选择,并且实现也比较简单,只是模块导出的方式。
注意:虽然打包工具支持了 ES Module,但是并不意味着其他的 es6 代码可以正常使用,因为使用者并不会对我们的 npm 包做编译处理,比如 webpack rules 中 exclude: /node_modules/,所以如果不是事先约定好后编译或者没有兼容性的需求,我们仍需要用 babel 处理,从而产出兼容性更好的 npm 包。
files
files是一个包含项目中的文件的数组。如果命名了一个文件夹,那也会包含文件夹中的文件。(除非被其他条件忽略了)
你也可以提供一个.npmignore文件,让即使被包含在files字段中得文件被留下。其实就像.gitignore一样。
大概意思就是指定这个包被install时候有哪些文件 类似npmignore的反面
bugs
你项目的提交问题的url和(或)邮件地址。这对遇到问题的屌丝很有帮助。
差不多长这样:
{ "url" : "http://github.com/owner/project/issues"
, "email" : "[email protected]"
}
你可以指定一个或者指定两个。如果你只想提供一个url,那就不用对象了,字符串就行。
如果提供了url,它会被npm bugs
命令使用。
homepage
项目官网的url。
注意:这和“url”_不_一样。如果你放一个“url”字段,registry会以为是一个跳转到你发布在其他地方的地址,然后喊你滚粗。
keywords
关键字,数组、字符串。还是方便屌丝们在npm search
中搜索。
engines
你可以指定工作的node的版本:
{ "engines" : { "node" : ">=0.10.3 <0.12" } }
并且,像dependensies一样,如果你不指定版本或者指定“*”作为版本,那么所有版本的node都可以。
如果指定一个“engines”字段,那么npm会需要node在里面,如果“engines”被省略,npm会假定它在node上工作。
你也可以用“engines”字段来指定哪一个npm版本能更好地初始化你的程序,如:
{ "engines" : { "npm" : "~1.0.20" } }
记住,除非用户设置engine-strict
标记,这个字段只是建议值。
peerDependencies
在一些场景中,如在一个host中不必须进行require
时候,你想表现你的package与一个host工具或者库的兼容关键。这一般用来引用_插件_。尤其是你的模块可能要暴露一个特定的接口,并由host文档来预期和指定。
比如:
{
"name": "tea-latte",
"version": "1.3.5"
"peerDependencies": {
"tea": "2.x"
}
}
这能保证你的package可以只和tea的2.x版本一起初始化。npm install tea-latte
可能会产生下面的依赖关系
├── [email protected]
└── [email protected]
试图初始化另一个有会冲突的依赖的插件将导致一个错误。因此,确保你的插件的需求约束越弱越好,而不要去把它锁定到一个特定的版本。
假设这个host遵守semver规范,只改变这个package的主版本会打破你的插件。因此,如果你在package中用过每个1.x版本,就用"^1.0"或者"1.x"来表示。如果你依赖于功能介绍1.5.2,用">= 1.5.2 < 2"。
os
你可以指定你的模块要运行在哪些操作系统中:
"os" : [ "darwin", "linux" ]
你也可以用黑名单代替白名单,在名字前面加上“!”就可以了:
"os" : [ "!win32" ]
操作系统用process.platform
来探测。
虽然没有很好地理由,但它是同时支持黑名单和白名单的。
private
如果你设置"private": true
,npm就不会发布它。
这是一个防止意外发布私有库的方式。如果你要确定给定的包是只发布在特定registry(如内部registry)的,用publishConfig
hash的描述来重写registry
的publish-time配置参数。
publishConfig
这是一个在publish-time使用的配置集合。当你想设置tag或者registry的时候它非常有用,所以你可以确定一个给定的包没有打上“lastest”的tag或者被默认发布到全局的公开registry。
任何配置都可以被重写,但当然可能只有“tag”和“registry”与发布的意图有关。
config
"config" hash可以用来配置用于包脚本中的跨版本参数。在实例中,如果一个包有下面的配置:
{ "name" : "foo"
, "config" : { "port" : "8080" } }
然后有一个“start”命令引用了npm_package_config_port
环境变量,用户可以通过npm config set foo:port 8001
来重写他。
directories
CommonJS Packages规范说明了几种方式让你可以用directories
hash标示出包得结构。如果看一下npm's package.json,你会看到有directories标示出doc, lib, and man。
在未来,这个信息可能会被用到。
directories.lib
告诉屌丝们你的库文件夹在哪里。目前没有什么特别的东西需要用到lib文件夹,但确实是重要的元信息。
directories.bin
如果你指定一个“bin”目录,然后在那个文件夹中得所有文件都会被当做"bin"字段使用。
如果你已经指定了“bin”字段,那这个就无效。
directories.man
一个放满man页面的文件夹。贴心地创建一个“man”字段。
A folder that is full of man pages. Sugar to generate a "man" array by
walking the folder.
directories.doc
将markdown文件放在这里。最后,这些会被很好地展示出来,也许,某一天。
Put markdown files in here. Eventually, these will be displayed nicely,
maybe, someday.
directories.example
将事例脚本放在这里。某一天,它可能会以聪明的方式展示出来。
workspace
workspace是除缓存外yarn区别于npm最大的优势
workspace的作用:
- 能帮助你更好地管理多个子project的repo,这样你可以在每个子project里使用独立的package.json管理你的依赖,又不用分别进到每一个子project里去yarn install/upfrade安装/升级依赖,而是使用一条yarn命令去处理所有依赖就像只有一个package.json一样
- yarn会根据就依赖关系帮助你分析所有子project的共用依赖,保证所有的project公用的依赖只会被下载和安装一次。
workspace的使用
- yarn workspace并不需要安装什么其他的包,只需要简单的更改package.json便可以工作。 首先我们需要确定workspace root,一般来说workspace root都会是repo的根目录
3.yarn workspace目录结构树
4.package.json:
{
//当private为true时workspace才会被启用
"private": true,
"workspace": ["workspace-a","workspace-b"]
}
workspaces属性的值为一个字符串数组,每一项指代一个workspace路径,支持全局匹配,这里的路径指向指的是package.json所在文件夹文件夹名。
5.workspace-a子project的package.json
{
"name": "workspace-a",
"version": "1.0.0",
"dependencies": {
"cross-env": 5.0.5
}
}
6.workspace-b子project的package.json
{
"name": "workspace-b",
"version": "1.0.0",
"dependencies": {
"cross-env": "5.0.5",
"workspace-a": "1.0.5"
}
}
7.在根目录执行yarn insatll
你会发现整个repo只生成了一份yarn.lock,绝大多数的依赖包都被提升到了根目录下的node_modules之内。各个子project的node_modules里面不会重复存在依赖,只会有针对根目录下cross-env的引用。不仅如此,你会发现,对于repo内部的依赖关系(比如workspace-b依赖于workspace-a),yarn也能很好的进行管理。
8.workspace有哪些不足和限制
- yarn workspace并没有像lerna那样封装大量的高层API,整个workspace整体上还是依赖于整个yarn命令体系。
- workspace不能嵌套(只能有一个根workspace)
- workspace采用的是向上遍历,所以workspace并不能识别根workspace之外的依赖。
参考资料: