🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
[TOC] # 结构 举例`pinia`包的`package.json`内容如下: ``` { "name": "pinia", "version": "2.0.9", "description": "Intuitive, type safe and flexible Store for Vue", "main": "index.js", "module": "dist/pinia.mjs", "unpkg": "dist/pinia.iife.js", "jsdelivr": "dist/pinia.iife.js", "types": "dist/pinia.d.ts", "exports": { ".": { "browser": "./dist/pinia.esm-browser.js", "node": { "import": { "production": "./dist/pinia.prod.cjs", "development": "./dist/pinia.mjs", "default": "./dist/pinia.mjs" }, "require": { "production": "./dist/pinia.prod.cjs", "development": "./dist/pinia.cjs", "default": "./index.js" } }, "import": "./dist/pinia.mjs" }, "./package.json": "./package.json", "./dist/*": "./dist/*" }, "sideEffects": false, "author": { "name": "Eduardo San Martin Morote", "email": "posva13@gmail.com" }, "funding": "https://github.com/sponsors/posva", "scripts": { "build": "rimraf dist && rollup -c ../../rollup.config.js --environment TARGET:pinia", "build:dts": "api-extractor run --local --verbose && tail -n +3 ./src/globalExtensions.ts >> dist/pinia.d.ts", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s --commit-path . -l pinia -r 1", "test:dts": "tsc -p ./test-dts/tsconfig.json", "test": "yarn run build && yarn run build:dts && yarn test:dts" }, "files": [ "dist/*.js", "dist/*.mjs", "dist/*.cjs", "dist/pinia.d.ts", "index.js", "index.cjs", "LICENSE", "README.md" ], "keywords": [ "vue", "vuex", "store", "pinia", ], "license": "MIT", "devDependencies": { "@microsoft/api-extractor": "7.19.2", "@vue/compiler-sfc": "^3.2.26", "@vue/server-renderer": "^3.2.26", "@vue/test-utils": "^2.0.0-rc.17", "vue": "^3.2.26", "vue2": "npm:vue@2" }, "dependencies": { "@vue/devtools-api": "^6.0.0-beta.21", "vue-demi": "*" }, "peerDependencies": { "@vue/composition-api": "^1.4.0", "typescript": ">=4.4.4", "vue": "^2.6.14 || ^3.2.0" }, "peerDependenciesMeta": { "typescript": { "optional": true }, "@vue/composition-api": { "optional": true } }, "repository": { "type": "git", "url": "git+https://github.com/vuejs/pinia.git" }, "bugs": { "url": "https://github.com/vuejs/pinia/issues" }, "homepage": "https://github.com/vuejs/pinia#readme" } ``` 其中 `package.json` 中设置的所有字段,都会被设置为 **`npm_package_`** 开头的环境变量。 可以得到 `npm_package_name`、`npm_package_version`、`npm_package_scripts_build`、`npm_package_browserslist_production_0` 等变量。 不止 `package.json`,npm 相关的所有配置也会有 **`npm_config_`** 开头的环境变量。 > [package.json 非官方字段集合](https://github.com/senntyou/blogs/blob/master/web-extend/3.md) # package-lock.json 加上`--no-save`选项即可防止`npm install`命令对`package.json`和`package-lock.json`的更改: ~~~ npm install --no-save ~~~ > [关于 `package-lock.json` 的一切](https://codertx.github.io/2018/01/09/about-package-json/) # 设置 nodejs 的 global 和 cache 路径 设置路径能够把通过npm 安装的模块集中在一起,便于管理。 在nodejs 的安装目录 `D:\nodejs\` 下,新建`node_global`和 `node_cache` 两个文件夹 执行指令: ``` npm config set prefix "D:\nodejs\node_global" npm config set cache "D:\nodejs\node_cache" ``` 设置成功后,后续用命令 `npm install -g XXX` 安装的 XXX模块就在 `D:\nodejs\node_global\node_modules` 里。 查看配置信息指令: `npm config list` # npm script 可以通过命令行的方式:在`package.json`中新增 prepare scripts: ~~~shell npm set-script prepare "husky install" && npm run prepare ~~~ `npm run XXX`是执行配置在`package.json`中的脚本,比如: ``` "scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js", "unit": "karma start test/unit/karma.conf.js --single-run", "e2e": "node test/e2e/runner.js", "test": "npm run unit && npm run e2e", "lint": "eslint --ext .js,.vue src test/unit/specs test/e2e/specs" }, ``` 只有这里配置了,你才能 run,所以不是所有的项目都能`npm run dev/build`。要了解这些命令做了什么,就要去scripts中看具体执行的是什么代码。这里就像是一些命令的快捷方式,免去每次都要输入很长的的命令(比如unit那行)。 为什么会出现`ERROR`,就是因为在跑这些对应的脚本文件的时候,可能是某些依赖没有被加载等的。 一般项目都会有 build, dev, unit等,从名字上基本能看出来是干什么的。 比如上面配置的 unit,就是开启 karma 去跑单元测试,具体测试内容,要去看`karma.conf.js`; e2e就是 End to End 的端到端测试; 而 test 则会将单元测试和端到端测试都执行。 有些项目中根据需要,还会配置其他命令,例如自动生成文档,比如这里: ~~~ "build:doc": "node ./scripts/build-doc.js", ~~~ 如果你去`build-doc.js`中看的话,会发现,这个脚本在遍历所有源文件,解析注释和其他内容,自动生成API文档 > [用 npm-run 自动化任务](http://blog.csdn.net/yy374864125/article/details/40740073) ## 多 script 运行 因为 npm scripts 在内部实际产生的是一个`shell`进程,所以我们可以使用`shell`语法来实现我们所需要的功能。 具体来说: * 使用 `;`(不管失败与否,所有命令都会被执行) (或者 `&&`(**任何一个串联命令失败,则停止后续的所有命令执行**)) 来串联运行 * 使用 `&` 来并行运行。 使用此语法的示例如下所示: 并行: ``` "scripts": { ..., "lint:bx": "npm run lint:js & npm run lint:jsx & npm run lint:css & npm run lint:json & npm run lint:markdown" } ``` > 注: `&`语法会创建一个子进程,这会导致无法判断原始的 npm 进程是否已经完成。这可能是有问题的,特别是长时间运行 scripts 时。并行命令,为了稳定复现一些错误,可在命令最后加上 `& wait`。另外,加上 `& wait` 的好处还有,如果我们在子命令启动长时间运行的进程,可用 `ctrl + c` 来结束进程。 串行: ``` "scripts": { ..., "build": "babel; jest" } ``` ## 使用 hook scripts (钩子脚本) npm 中的每条 script 在引擎内部都会运行三个单独的 script 。 1. `pre` scripts 2. scripts 本身 3. `post` scripts 这两个额外运行的 scripts ,正如他们的名字所描述的那样,就是在该 scripts 运行前 和该 scripts 运行后运行的脚本。 例如,在部署期间,它们可用来做一些设置和清理。 这两个 scripts 使用与之前`scripts`名称相同的`pre[scriptname]`和`post[scriptname]`来表示。 假设我们要构建我们的项目,这将是一个非常简单的例子,只是为了展示这个概念。 我们会做的是这样的: * 创建一个新的一个`dist`目录,如果这个目录已经存在,那么从中删除所有内容 * 创建`tmp`目录 * 将项目构建到`tmp`目录 * 将我们的包压缩到到`dist`目录 * 删除`tmp`目录 `package.json` 代码: ~~~ "scripts": { ..., "prebuild": "mkdir dist tmp; rm -rf dist/*", "build": "browserify main.js -o tmp/bundle.js && uglifyjs -o dist/bundle.min.js -- tmp/bundle.js", "postbuild": "rm -rf tmp" } ~~~ 现在,每当运行`npm run build`时,它会触发所有命令,并且保证他们以正确的顺序被执行。 # 传入参数 对于上面的脚本`"test": "mocha"`如果希望给 mocha 传入一些选项,比如希望执行: ~~~shell mocha --reporter spec ~~~ 需要这样执行 npm test: ~~~shell npm test -- --reporter spec ~~~ 需要使用**两个短线**将选项隔开,或者将选项直接写在`package.json`中: ~~~js "scripts":{ "test": "mocha --reporter spec" } ~~~ 在 shell 中传入的参数都要使用`--`隔开,这个`--`被视作 npm run 命令参数的结束,`--`后面的内容都会原封不动地传给运行的命令。 > [NPM 相关知识](https://github.com/wy-ei/notebook/issues/42) # 设置环境变量 添加用户变量PATH:`D:\nodejs\node_global` 新增系统变量NODE_PATH:`D:\nodejs\node_global\node_modules` # `peerDependencies` 它会告诉`npm`:如果某个`package`依赖我,那么这个`package` 也应该对`peer-dependencies-plugin-core`依赖,这个时候,你 `npm install peer-dependencies-plugin` 的时候,将得到下面这样的目录: ``` ├──package.json ├──src │ └──index.js └──node_modules └──peer-dependencies-plugin-core └──peer-dependencies-plugin └──node_modules └──peer-dependencies-plugin-core ``` 这势必会靠成很多不必要的麻烦,首当齐冲的就是,你的项目依赖的是`1.0.0`,而你依赖的另一个插件却只能支持到`0.0.8`,这个时候,导致一个项目里面依赖了两次`peer-dependencies-plugin-core`,而且还不是同一个版本。 npm 3 中不会再要求 `peerDependencies` 所指定的依赖包被强制安装,相反 npm 3 会在安装结束后检查本次安装是否正确,如果不正确会给用户打印警告提示。 我们在 `webpack-plugin-a@1.0.0` 的 `package.json` 中添加如下配置: ``` "peerDependencies": { "webpack": "^2.0.0" } ``` 这样就指定了 `webpack-plugin-a@1.0.0` 只兼容 `webpack@2.x.x`,当用户同时安装 `webpack@3.0.0` 和 `webpack-plugin-a@1.0.0`的时候就会抛出: ``` > UNMET PEER DEPENDENCY webpack@3.0.0 > npm WARN webpack-plugin-a@1.0.0 requires a peer of webpack@^2.0.0 but none was installed ``` > [peerDependencies 的理解](https://xwenliang.cn/p/5af2a97d5a8a996548000003) ## 什么时候使用`peerDependencies`? 通常是在插件开发的场景下,你的插件需要某些依赖的支持,但是你又没必要去安装,因为使用该插件的宿主会去安装这些依赖,你就可以用 `peerDependencies` 去声明一下需要依赖的插件和版本,如果出问题 npm 就会有警告来提醒使用者去解决版本冲突问题。 `dependencies`及`devDependencies`常见,而`peerDependencies`并不是。`peerDependencies` 不会被自动安装。 示例:当一个依赖项 c 被列在某个包 b 的 peerDependency 中时,**它就不会被自动安装**。取而代之的是,包含了 b 包的代码库 a 则必须将对应的依赖项 c 包含为其依赖。(npm 3 中只会打印警告提示) *a/package.json*: ``` { //... "dependencies": { "b": "1.x", "c": "1.x" } } ``` > [探讨npm依赖管理之peerDependencies](https://www.cnblogs.com/wonyun/p/9692476.html) > [package.json 文件中的 peerDependencies](https://pantao.parcmg.com/press/peer-dependencies-in-package.html) # `package.json` 其他配置字段 `package.json` 中可以配置很多字段。其他程序会去自动读取其配置或者该文件中的配置字段~!比如:babel、eslint、browserslist等。 # Semantic Versioning 在NPM包依赖项中经常使用的版本是`脱字符号(又叫 插入符号^ )范围`或`版本`。npm的安装也使用了`脱字符号范围`。 脱字符号范围: `[major, minor, patch] (主要、次要、补丁)`,这个元祖中请不要修改最左边的非零位。换言之,这允许**补丁**和**版本次要更新**`1.0.0或以上`,**补丁更新**为`版本0.X> = 0.1.0`,并且**没有进行版本更新**`0.0.x`。 例如: ``` * ^1.2.3 := >=1.2.3 =1.2.3,并且<2.0.0。) * ^0.2.3 := >=0.2.3 <0.3.0 * ^0.0.3 := >=0.0.3 <0.0.4 * ^1.2.3-beta.2 := >=1.2.3-beta.2 <2.0.0 Note that prereleases in the 1.2.3 version will be allowed, if they are greater than or equal to beta.2. So, 1.2.3-beta.4 would be allowed, but 1.2.4-beta.2 would not, because it is a prerelease of a different \[major, minor, patch\] tuple. * ^0.0.3-beta := >=0.0.3-beta <0.0.4 Note that prereleases in the 0.0.3 version only will be allowed, if they are greater than or equal to beta. So, 0.0.3-pr.2 would be allowed. When parsing caret ranges, a missing patch value desugars to the number 0, but will allow flexibility within that value, even if the major and minor versions are both 0. * ^1.2.x := >=1.2.0 <2.0.0 * ^0.0.x := >=0.0.0 <0.1.0 * ^0.0 := >=0.0.0 <0.1.0 A missing minor and patch values will desugar to zero, but also allow flexibility within those values, even if the major version is zero. * ^1.x := >=1.0.0 <2.0.0 * ^0.x := >=0.0.0 <1.0.0 ``` 大多数情况下,使用插入符号范围作为依赖版本工作完全正常。但是有时候会出现bug。在一个项目中,使用并安装了带有插入符号版本`^3.4.3`的[JS-YAML](https://github.com/nodeca/js-yaml): ```json { "dependencies": { "js-yaml": "^3.4.3" } } ``` 过了段时间。当在一个新克隆的项目代码库再次运行`npm install`时,安装了`3.5.2`版本。由于js-yaml版本`3.5.0`,当安全加载带有重复密钥的规范时,会抛出错误。如果在YAML文件中没有重复的键,这是好的。然而,其中一个文件有它。正确的方法是修复重复的密钥。但这需要额外的工作。你以前听过这句话:“我当时安装它的时候,它工作得很好,现在怎么不行了”。 这里的要点是使用精确的版本,而不是让包管理器通过删除脱字符来决定: ```json { "dependencies": { "js-yaml": "3.4.3" } } ``` 这将避免上述问题。我们可以手动[更新过时的NPM包](https://realguess.net/2014/12/13/update-outdated-npm-packages/)。 不要让机器来决定。自己动手去做! > [What's the difference between tilde(~) and caret(^) in package.json?](https://stackoverflow.com/questions/22343224/whats-the-difference-between-tilde-and-caret-in-package-json) > [Semver explained - why is there a caret (^) in my package.json?](https://bytearcher.com/articles/semver-explained-why-theres-a-caret-in-my-package-json/) > [Versions of dependencies | Yarn](https://classic.yarnpkg.com/en/docs/dependency-versions/) # 详细查看安装过程 **Append the`--loglevel verbose`argument to the command you want to run** and all logs will be shown on STDERR and saved to`npm-debug.log`file in the current working directory. Example usage: ``` npm install ionic --loglevel verbose ``` Running the`npm`commands like this, shows the logs in realtime and saves the logs to the directory its running within. ``` npm config set loglevel verbose ``` For permanent solution, just edit the global`npm`configuration. To do this, run`npm config edit`command and add`loglevel=verbose`. Now every`npm`command will show detailed logs # 生成多入口的包 ## [Package.json with multiple entrypoints](https://stackoverflow.com/questions/63058081/package-json-with-multiple-entrypoints) # `exports`字段 `"exports"` 字段算是 `"main"` 的替代品,它既可以定义包的主入口(`main`),又封闭了包,**防止其他未被定义的内容被访问**。这种封闭允许模块作者为他们的包定义公共接口。 如果同时定义了 `"exports"` 和 `"main"`,在支持`"exports"`的 Node(>= v12.7.0) 中`"exports"`会覆盖`"main"`,否则`"main"`生效。因此, `"main"` **不能作为 CommonJS 的降级回落,但它可以作为不支持** `"exports"` **字段的 Node.js 旧版本的降级回落**。 在 `"exports"` 中使用“条件导出”(Conditional exports)可以为每个环境定义不同入口,包括包是通过 `require` 还是 `import` 来引用。 **注意**:使用 `"exports"` 字段可以防止包的使用者使用其他未定义的入口点,包括 `package.json`(例如:`require('your-package/package.json')`。**这很可能是一个重大变更**。 为了使 `"exports"` 的引入不具有破坏性,请确保之前支持的每个入口都被导出。最好明确指定各个入口,这样包的就有了明确的公共API定义。 ```json { ... "types": "dist/pinia.d.ts", "name": "my-package", "type": "module", "exports": { ".": { // Entry-point for `import "my-package"` in ESM "import": "./esm/index.js", // Entry-point for `require("my-package") in CJS "require": "./commonjs/index.cjs", // Entry-point for TypeScript resolution "types": "./types/index.d.ts" }, }, // CJS fall-back for older versions of Node.js "main": "./commonjs/index.cjs", // Fall-back for older versions of TypeScript "types": "./types/index.d.ts", // 自定义子路径 "./client": { "types": "./client.d.ts" }, "./dist/client/*": "./dist/client/*", "./package.json": "./package.json" }, "sideEffects": false, ... } ``` 当使用 `"exports"` 字段时,可以将主入口视为 `"."` 路径,然后构造自定义路径: ``` { "main": "./main.js", "exports": { ".": "./main.js", "./submodule": "./src/submodule.js" } } ``` 目前只有在 `"exports"` 中定义的子路径才能被导入: ``` import submodule from 'es-module-package/submodule'; // 加载 ./node_modules/es-module-package/src/submodule.js ``` 导入其他子路径就会报错: ``` import submodule from 'es-module-package/private-module.js'; // 抛错 ERR_PACKAGE_PATH_NOT_EXPORTED ``` 可以参考`vite` 包的`package.json`设置的比较全面。目前 typescript 对该字段的解析还是支持的不够完善,但是已经纳入下个计划了。 > [Node 最新 Module 导入导出规范 - 掘金 (juejin.cn)](https://juejin.cn/post/6972006652631318564#heading-16) > [New package.json `exports` field not working with TypeScript](https://stackoverflow.com/questions/58990498/new-package-json-exports-field-not-working-with-typescript) > [Support for NodeJS 12.7+ package exports · Issue #33079 · microsoft/TypeScript (github.com)](https://github.com/microsoft/TypeScript/issues/33079) # 工具 [Package Dependency Graph for npm](http://npm-dependencies.com/) [pastelsky/bundlephobia](https://github.com/pastelsky/bundlephobia) 了解在应用程序包中包含npm包的性能影响。 # 附录 npm.io [npm scripts : 每个前端开发都应知道的一些使用提示](https://www.html.cn/archives/8029) [Docco](http://ashkenas.com/docco/)