深入理解包与NPMM,package.json深入解读

包与NPM

CommonJS的包规范的定义其实也十分简单,它由包结构和包描述文件两个部分组成,前者用于组织包中的各种文件,后者则用于描述包的相关信息,以供外部读取分析。

包结构

包实际上是一个存档文件,即一个目录直接打包为 .zip 或 tar.gz 格式的文件,安装后解压还原为目录。完全符合CommonJS规范的包目录应该包含如下文件。

  1. package.json: 包描述文件。
  2. bin:用于存放可执行二进制文件的目录。
  3. lib:用于存放JavaScript代码的目录。
  4. doc:用于存放文档的目录。
  5. test:用于存放单元测试用例的代码。

可以看到,CommonJS包规范从文档、测试等方面都做过考虑。当一个包完成后向外公布时,用户可以看到单元测试和文档时,会给人一种踏实可靠的感觉。

包描述文件

包描述文件用于表达非代码相关的信息,它是一个JSON格式的文件——package.json,位于包的根目录下,是包的重要组成部分。而npm的所有行为都与包描述文件的字段息息相关。

CommonJS为package.json文件定义一些必须的字段。

  1. name:包名。规范定义它需要小写字符和数字的组成,可以包含. \ _和 - ,但不允许出现空格。包名必须是唯一的,以免对外公布时产生重名冲突的误解。除此以外,NPM还建议不要在包名中附带上node或js来重复表示它是JavaScript或Node模块。
  2. description:包简介。
  3. version:版本号。
  4. keywords:关键词数组,NPM中主要用来做分类搜索。一个好的关键词数组有利于用户快速找到你编写的包。
  5. maintainers:包维护者列表,每个维护者由name、email和web这3个属性组成。

NPM通过该属性进行权限认证。

  1. contribuctors:贡献者列表。
  2. bugs:一个可以反馈bug的网页地址或邮箱地址。
  3. licenses:当前包所使用的许可证列表,表示这个包在哪些许可证下使用。
  4. repositories:托管源代码的位置列表,表明可以通过哪些方式和地址访问包的源代码。
  5. dependencies:使用当前包所依赖的包列表。这个属性非常重要,NPM会通过这个属性自动加载依赖的包。

除了必选字段外,规范还定义了一部分可选的字段

  1. homepage:当前包的网站地址。

  2. os:操作系统支持的列表。这些操作系统的取值包括aix,freebsd,Linux,macos,solaris,vxworks,windows。如果设置了列表为空时,则不对操作系统做任何假设。

  3. CPU:CPU架构的支持列表。

  4. engine:支持的js引擎列表,有效的引擎列表取值包括ejs,flusspferd,gpsee,jsc,spidermonkey,narwhal,node,v8.

  5. builtin:标志当前包是否是内建在底层系统的标准组件。

  6. directories:包目录说明。

  7. implements:实现规范的列表,标志当前包实现了CommonJS的那些规范。

  8. scripts:脚本说明对象。它主要被包管理器用来安装,编译,测试和卸载包,为其提供钩子机制。

    1
    2
    3
    4
    5
    6
    7
    //示例如下:
    "scripts":{
    "install":"install.js",
    "uninstall":"uninstall.js",
    "build":"build.js",
    "test":"test.js"
    }

包规范的定义可以帮助Node解决依赖包安装的问题,而NPM正是基于该规范进行了实现。最初,NPM工具是有Issac Z.Schlueter单独创建,提供给node服务的Node包管理器,需要单独安装。后来,在v6.3版本时集成进Node中作为默认包管理器,作为软件包的一部分一起安装。

在包描述文件的规范中,NPM实际需要的字段主要有name,version,description,keywords,repositories,author,bin,main,scripts,engines,dependencies,devDependencies。

与包规范的区别在于多了author,bin,main和devDependencies这4个字段。

author:包作者。

bin:一些包作者希望包可以作为命令行工具使用。配置好bin字段后,通过npm install package_name -g命令可以将脚本添加到执行路径中,之后可以在命令行中直接执行。通过-g命令安装的模块包称为全局模式。

main:模块引入方法require()在引入包时,会优先检查这个字段,并将其作为包中其余模块的入口。如果不存在这个字段,require()方法会查找包目录下的 index.js , index.node , index.json 文件作为默认入口。

devDependencies:一些模块只在开发时需要依赖。配置这个属性,可以提示包的后续开发者安装依赖包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
//以下是node比较有名的框架express项目的package.json文件
{
"name": "express",
"description": "Fast, unopinionated, minimalist web framework",
"version": "4.17.1",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
"Aaron Heckmann <aaron.heckmann+github@gmail.com>",
"Ciaran Jessup <ciaranj@gmail.com>",
"Douglas Christopher Wilson <doug@somethingdoug.com>",
"Guillermo Rauch <rauchg@gmail.com>",
"Jonathan Ong <me@jongleberry.com>",
"Roman Shtylman <shtylman+expressjs@gmail.com>",
"Young Jae Sim <hanul@hanul.me>"
],
"license": "MIT",
"repository": "expressjs/express",
"homepage": "http://expressjs.com/",
"keywords": [
"express",
"framework",
"sinatra",
"web",
"rest",
"restful",
"router",
"app",
"api"
],
"dependencies": {
"accepts": "~1.3.7",
"array-flatten": "1.1.1",
"body-parser": "1.19.0",
"content-disposition": "0.5.3",
"content-type": "~1.0.4",
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~1.1.2",
"encodeurl": "~1.0.2",
"escape-html": "~1.0.3",
"etag": "~1.8.1",
"finalhandler": "~1.1.2",
"fresh": "0.5.2",
"merge-descriptors": "1.0.1",
"methods": "~1.1.2",
"on-finished": "~2.3.0",
"parseurl": "~1.3.3",
"path-to-regexp": "0.1.7",
"proxy-addr": "~2.0.5",
"qs": "6.7.0",
"range-parser": "~1.2.1",
"safe-buffer": "5.1.2",
"send": "0.17.1",
"serve-static": "1.14.1",
"setprototypeof": "1.1.1",
"statuses": "~1.5.0",
"type-is": "~1.6.18",
"utils-merge": "1.0.1",
"vary": "~1.1.2"
},
"devDependencies": {
"after": "0.8.2",
"connect-redis": "3.4.1",
"cookie-parser": "~1.4.4",
"cookie-session": "1.3.3",
"ejs": "2.6.1",
"eslint": "2.13.1",
"express-session": "1.16.1",
"hbs": "4.0.4",
"istanbul": "0.4.5",
"marked": "0.6.2",
"method-override": "3.0.0",
"mocha": "5.2.0",
"morgan": "1.9.1",
"multiparty": "4.2.1",
"pbkdf2-password": "1.2.1",
"should": "13.2.3",
"supertest": "3.3.0",
"vhost": "~3.0.2"
},
"engines": {
"node": ">= 0.10.0"
},
"files": [
"LICENSE",
"History.md",
"Readme.md",
"index.js",
"lib/"
],
"scripts": {
"lint": "eslint .",
"test": "mocha --require test/support/env --reporter spec --bail --check-leaks test/ test/acceptance/",
"test-ci": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --require test/support/env --reporter spec --check-leaks test/ test/acceptance/",
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --require test/support/env --reporter dot --check-leaks test/ test/acceptance/",
"test-tap": "mocha --require test/support/env --reporter tap --check-leaks test/ test/acceptance/"
},
"__npminstall_done": "Sun Feb 16 2020 11:22:54 GMT+0800 (GMT+08:00)",
"_from": "express@4.17.1",
"_resolved": "https://registry.npm.taobao.org/express/download/express-4.17.1.tgz"
}

NPM常用功能

CommonJS包规范是理论,NPM是其中的一种实践。对于Node而言,NPM帮助完成了第三方模块的发布,安装和依赖等。借助NPM,Node与第三方模块之间形成了很好地一个生态系统。

安装依赖包

安装依赖包是NPM最常见的用法,它的执行语句是npm install express.。执行该命令后,NPM会在当前目录下创建node_module目录,然后在node_module目录下创建express目录,接着将包解压到这个目录下。

安装好依赖包后,直接在代码中调用require(‘express’),即可引入该包,require()方法在做路径分析时会通过,模块路径查找到express所在的位置。模块引入和包的安装是相辅相成的。

全局模式安装

如果包含有命令行工具,那么需要执行npm install express -g命令进行全局安装。需要注意的是,全局模式并不是将一个模块包安装为一个全局包的意思,他并不意味着可以从任何地方通过require()来引用到它。

全局模式这个称谓其实并不精确,存在诸多误导。实际上,-g是将一个包安装为全局可用的可执行命令。他根据包描述文件中的bin字段配置,将实际脚本链接到与Node可执行文件相同的路径下:

1
2
3
"bin":{
"express":"./bin/express"
}

事实上,通过全局模式安装的所有模块都被安装进一个统一的目录下,这个目录可以通过如下方式运算出来:

path.resolve(process.execPath, ‘..’ , ‘lib’ , ‘node_modules’);

如果Node可执行文件的位置是/user/local/bin/node,那么模块目录就是/user/local/lib/node_modules。最后,通过软链接的方式将bin字段配置的可执行文件链接到Node的可执行目录下。

从本地安装

对于一些没有发布到NPM上的包,或是因为网络原因导致无法直接安装的包,可通过将包下载到本地,然后本地安装。本地安装只需要为NPM指定package.json文件所在的位置即可:它可以是一个包含package.json的存档文件,也可以是一个URL地址,也可以是一个目录下有package.json文件的目录位置。具体参数如下:

1
2
3
npm install <tarbar file>
npm install <tarbar url>
npm install <folder>
从非官方源安装

如果不能通过官方源安装,可通过镜像安装。在执行命令时,添加 --registry=http://registry.url即可,实例:

1
npm insatll underscore --registry=http://registry.yrl

如果使用过程几乎使用镜像源安装,可执行命令指定默认源

1
npm config set registry http://registry.url

NPM潜在问题

作为为模块和包服务的工具,NPM十分便捷。它实质上已经是一个包共享平台,所有人都可以贡献并将其打包分享到这个平台上,也可以在许可证的允许下免费使用他们

文章目录
  1. 1. 包与NPM
    1. 1.0.1. 包结构
    2. 1.0.2. 包描述文件
    3. 1.0.3. NPM常用功能
      1. 1.0.3.0.1. 安装依赖包
      2. 1.0.3.0.2. 全局模式安装
      3. 1.0.3.0.3. 从本地安装
      4. 1.0.3.0.4. 从非官方源安装
  2. 1.0.4. NPM潜在问题
|