@author ASCE1885的 Github 简书 微博 CSDN 知乎
本文由于潜在的商业目的,不开放全文转载许可,谢谢!
广而告之时间:我的新书《Android 高级进阶》(https://item.jd.com/10821975932.html )在京东开始预售了,欢迎订购!
npm,全称是 node package manager,顾名思义最开始是作为 Node 的包管理器存在的。不过经过不断的发展和壮大,现在的 npm 早就不再局限于 Node 的范畴,已经成为 Javascript 的包管理器,看看下面的 npm 官网首页介绍就知道了:
本系列教程假设你已经按照 React Native 官方入门指南安装好相关的环境(尤其是 Node.js),并且执行 react-native init AwesomeProject
命令生成了 Demo 工程 AwesomeProject,工程目录结构如下图所示:
package.json 的文件结构
从上图中可以看到,AwesomeProject 工程的根目录有一个名为 package.json
的文件,它是工程的元数据集,主要作用有:
- 管理项目中依赖的第三方包,可以很方便的和团队中其他开发者共享工程的依赖配置,这样不需要每个人都手动
npm install
相应的依赖包 - 定义 npm 中可以执行的脚本
一个合格的 package.json
文件需要至少包含 name
和 version
两个字段,这两个字段组成的二元组可以唯一标识一个包,如下所示:
{
"name": "AwesomeProject",
"version": "0.0.1"
}
当然一般情况下,工程的 package.json 不可能这么简单,常用的字段和解释如下表所示,开发者需要根据具体的业务需求进行选择:
字段名 | 含义 | 示例 |
---|---|---|
name | 包名需要具备唯一性,而且字母必须全部小写,如果一个包缺少这个字段,使用 npm install 将会失败 | "name": "redux" |
version | 包的版本号,遵循语义化版本(http://semver.org/lang/zh-CN/)格式,也就是版本号包含三位:MAJOR.MINOR.PATCH 。MAJOR 表示版本发生大的变化,例如 API 不兼容旧版本;MINOR 表示版本增加新功能,但是兼容旧版本的;PATCH 表示兼容旧版本的一些 bug 修复 |
"version": "3.5.2" |
description | 项目的描述,尽量保持言简意赅 | "description": "Predictable state container for JavaScript apps" |
author | 项目的作者名字和邮件地址,如果有多个,以 JSON 数组形式表示 | "authors": ["Dan Abramov dan.abramov@me.com(https://github.com/gaearon)","Andrew Clark acdlite@me.com(https://github.com/acdlite)"] |
contributors | 项目的贡献者名单,以 JSON 数组的形式表示 | "contributors": [{"name": "asce1885","email": "asce1885@gmail.com"}] |
bin | 项目对外暴露的 CLI 接口,提供给其他项目使用的脚本 | "bin": {"module-name":"./bin/module-name"} |
scripts | 定义 npm 脚本命令,key 值表示命令名,value 值表示命令对应的脚本或者脚本的路径,通过 npm run 或者 npm run-script 可以执行对应的命令 |
"scripts": { "clean": "rimraf lib dist es coverage", "lint": "eslint src test examples build", "start": "node node_modules/react-native/local-cli/cli.js start" |
main | 工程生成的 Package 的主入口点,当在 node 中调用 require('{module name}') 时会 require 到这个文件 |
"main": "lib/index.js" |
repostitory | 如果我们这个工程是开源的,这个字段用来指明工程的仓库 URL 地址以及版本控制系统的类型,这可以方便其他开发者贡献代码 | "repository": { "type": "git", "url": "https://github.com/reactjs/redux.git" } |
bugs | 使用者可以提交bugs的 URL 或者邮件地址 | "bugs": {"url": "https://github.com/reactjs/redux/issues"} |
keywords | 描述这个 Package 的关键字信息,方便用户通过关键字搜索到这个 Package | "keywords": ["redux","reducer","state","predictable","functional","immutable","hot","live","replay","flux","elm"] |
dependencies | 这个 Package 的生产依赖,当用户安装你的 Package 时会自动安装这些依赖 | "dependencies": { "react": "^15.1.0", "react-native": "^0.27.0-rc2" } |
devDependencies | 这个 Package 在开发或者测试阶段的依赖,不会打包到最终的生产包中 | "devDependencies": { "babel-eslint": "^5.0.0", "eslint": "^2.1.0", "eslint-plugin-react": "^3.16.1" } |
preferGlobal | 表明这个 Package 希望通过 npm install -g {module-name} 全局安装,这个字段是给包含了 CLI 的 Package 使用, 其他情况下不要使用这个字段 |
"preferGlobal": true |
private | 设置为 true 时,npm 将不会发布这个 Package,这个标记主要用来防止不小心发布某个内部使用的私有 Package 到公共的 npm registry | "private": true |
publishConfig | 发布这个 Package 时用到的一些配置信息,这些配置信息会覆盖默认的 npm 配置 | "publishConfig": { "registry": "https://your-private-hosted-npm.registry.nodejitsu.com" } |
subdomain | 指明应用的 subdomain,说明应该只包含 subdomain,而不是 root domain | |
analyze | 如果你的 Package 托管在 Nodejitsu(https://www.nodejitsu.com/) 上面,同时将这个字段设置为 true,Nodejitsu 将会自动尝试扫描你的 Package,可以及时发现缺少的依赖,可能存在的 bugs 以及语法错误等 | "analyze": true |
license | 如果这个 Package 是开源的,此处指定它遵循的许可协议 | "license": "MIT" |
一个真实项目的 Package.json
文件内容如下所示(取自 redux-logger[1])
{
"name": "redux-logger",
"version": "2.6.1",
"description": "Logger for Redux",
"main": "lib/index.js",
"scripts": {
"lint": "$(npm bin)/eslint src",
"test": "npm run lint",
"clean": "$(npm bin)/rimraf dist lib",
"build:lib": "$(npm bin)/babel src --out-dir lib",
"build:umd": "LIBRARY_NAME=reduxLogger NODE_ENV=development $(npm bin)/webpack src/index.js dist/index.js --config webpack.build.js",
"build:umd:min": "LIBRARY_NAME=reduxLogger NODE_ENV=production $(npm bin)/webpack -p src/index.js dist/index.min.js --config webpack.build.js",
"build": "npm run build:lib && npm run build:umd && npm run build:umd:min",
"prepublish": "npm run clean && npm run test && npm run build"
},
"files": [
"dist",
"lib",
"src"
],
"repository": {
"type": "git",
"url": "git+https://github.com/theaqua/redux-logger.git"
},
"keywords": [
"redux",
"logger",
"redux-logger",
"redux",
"middleware"
],
"author": "Eugene Rodionov (https://github.com/theaqua)",
"license": "MIT",
"bugs": {
"url": "https://github.com/theaqua/redux-logger/issues"
},
"homepage": "https://github.com/theaqua/redux-logger#readme",
"devDependencies": {
"@dtrussia/eslint-config-dtrussia": "2.2.1",
"babel-cli": "6.3.13",
"babel-core": "6.3.13",
"babel-eslint": "6.0.4",
"babel-loader": "6.2.0",
"babel-plugin-add-module-exports": "0.1.1",
"babel-preset-es2015": "6.3.13",
"babel-preset-react": "6.3.13",
"babel-preset-stage-0": "6.3.13",
"eslint": "2.10.2",
"eslint-plugin-react": "5.1.1",
"rimraf": "2.4.4",
"webpack": "1.12.9"
},
"dependencies": {
"deep-diff": "0.3.4"
}
}
npm 的模块管理
熟悉 npm 的常用命令,往往能够使得你的工作事半功倍。首先我们来介绍最常用的 npm install
命令,它是用来将依赖的模块安装到 node_modules
目录中,依赖分为两种:生产环境的依赖和开发环境的依赖,这个在前面一节已经介绍过了,对应的命令分别如下所示:
npm install redux // 生产环境的依赖
npm install redux -dev // 开发环境的依赖
在安装之前,npm 会先检查 node_modules
目录中是否已经存在指定的模块,如果存在,则不会重新安装,即使这个模块已经有新版本。当然,我们可以通过增加 -f
或者 --force
参数来强制重新安装最新版本。上面的命令安装完成后,我们可以在 node_modules
目录中找到 redux 的包,但这时 package.json
文件内容并没有发生变化,为了在发布我们这个包给其他开发者使用时,他们可以自动安装这些依赖,我们需要将依赖写入 package.json
文件中,当然你可以选择手动写入,但更方便的方法是在 npm install
时增加 --save
参数,如下所示:
npm install redux --save // 生产环境的依赖
npm install redux --save-dev // 开发环境的依赖
这时 npm 会自动帮我们写入 package.json
文件,如下所示:
{
...
"dependencies": {
...
"redux": "^3.5.2"
},
"devDependencies": {
"eslint": "^2.11.1"
}
}
有了依赖的安装,当然也有卸载的命令,很简单就是 npm uninstall
,后面参数是需要卸载的包名,例如 npm uninstall redux
。
前面使用 React Native 提供的 react-native init AwesomeProject
命令生成的 Demo 工程已经自动帮我们生成了 package.json
文件,如果我们自己手动建立一个 React Native 的工程,那么可以选择从其他工程拷贝现成的 package.json
并进行修改,当正确的做法是使用 npm init
命令来生成它。在 Terminal 中输入 npm init
,npm 将会一步一步引导我们输入一些关键的字段,最终生成的文件内容如下所示,从中看到的字段几乎是每个工程必备的:
{
"name": "asce1885",
"version": "1.0.0",
"description": "One Piece",
"main": "index.js",
"scripts": {
"test": "op"
},
"repository": {
"type": "git",
"url": "git+https://github.com/PaicHyperionDev/MobileDevWeekly.git"
},
"keywords": [
"mobile",
"dev"
],
"author": "asce1885",
"license": "MIT",
"bugs": {
"url": "https://github.com/PaicHyperionDev/MobileDevWeekly/issues"
},
"homepage": "https://github.com/PaicHyperionDev/MobileDevWeekly#readme"
}
当某个第三方依赖库需要发布新版本,我们项目也需要跟着升级时,可以通过 npm update
命令对指定的 Package 进行升级,例如 npm update redux
。同时,我们可以通过执行命令 npm outdated
来查询当前安装的所有 npm 包中是否有存在新版本的。
一般来说,掌握上面几个命令,对开发 React Native 来说就已经足够了,对于不熟悉的命令,我们可以通过 npm help
来查询对应命令的用法,例如输入 npm help registry
,会得到如下结果:
npm scripts
上一节我们介绍了 npm 的模块管理功能,事实上,npm 另外一个高频使用的功能就是用来执行脚本。我们在 package.json
文件的 scripts
字段中定义的脚本可以通过 npm run
或者 npm run-script
执行,例如在 React Native 工程中,我们可以定义如下脚本,分别用来创建,启动 Android 模拟器,启动 node 服务和打包等:
"scripts": {
"avd:create": "android create avd -t 1 -n MuchVote -d 9 -b x86_64 -s 1440x2560",
"avd:start": "emulator -avd MuchVote -gpu on -dpi-device 560 -scale ${SCALE:-0.25}",
"adb:reverse": "adb reverse tcp:8081 tcp:8081",
"android": "npm run adb:reverse && node node_modules/react-native/local-cli/cli.js run-android",
"start": "npm run adb:reverse && node_modules/react-native/packager/packager.sh",
}
定义完成之后,就可以在 React Native 目录中像下面这样执行对应的命令:
npm run adb:reverse
npm run android
...
为了方便脚本的执行,npm 默认定义了一些命令的快捷键,例如 npm test
,npm start
,npm stop
等等,也就是说执行这些命令时,我们省去了 run
的输入。之所以定义这些快捷键,除了可以节省执行命令的时间,更重要的一点是这些命令的命名是通用的约定,很多持续构建平台例如 Travis 会默认为 Node.js 工程添加 npm test
命令;同时,这些通用的命令定义也方便其他开发者使用你的包。
npm 为每一条命令都提供了 pre-
和 -post
这两个钩子,分别表示在命令执行前和执行后会执行对应的钩子命令。例如下面的脚本,当用户执行 npm run test
命令时,事实上 npm 会先执行 pretest
命令,然后才执行 test
命令。
"scripts": {
"eslint": "eslint --rulesdir **",
"test": "mocha test/",
"pretest": "npm run eslint"
}
当一个命令比较复杂时,我们还可以将这个命令定义在一个单独的 js
文件中,然后通过 node
来执行,如下所示:
"scripts": {
"build": "node build.js"
}
本系列关于 npm 的介绍就到这里,基本上对开发 React Native 已经足够用了,如果你不满足于这些基础知识,可以查阅拓展阅读部分的文档,进一步学习。
拓展阅读
《npm 官方文档》[2]
《我为何放弃 Gulp 与 Grunt,转投 npm scripts》上[3] 中[4] 下[5]
《Introduction to Using NPM as a Build Tool》[6]
《How to Use npm as a Build Tool》[7]
《package.json》[8]
《玩转 npm》[9]
《玩转 npm》[10]
《npm 模块安装机制简介》[11]
欢迎关注我的微信公众号,专注与原创或者分享 Android,iOS,ReactNative,Web 前端移动开发领域高质量文章,主要包括业界最新动态,前沿技术趋势,开源函数库与工具等。
-
http://www.infoq.com/cn/news/2016/02/gulp-grunt-npm-scripts-part1 ↩
-
http://www.infoq.com/cn/news/2016/02/gulp-grunt-npm-scripts-part2 ↩
-
http://www.infoq.com/cn/news/2016/02/gulp-grunt-npm-scripts-part3 ↩
-
https://medium.com/@dabit3/introduction-to-using-npm-as-a-build-tool-b41076f488b0#.tylyovxdw ↩
-
http://blog.keithcirkel.co.uk/how-to-use-npm-as-a-build-tool/ ↩