npm
NPM 是随同 NodeJS 一起安装的包管理工具, nodejs
的包管理器,用于 node 插件管理,包括安装,卸载,管理依赖等
用 npm 命令安装模块
npm 安装 Node.js 模块语法格式如下:
1 | npm install <Module Name> |
npm 的包安装分为本地安装(local)、全局安装(global)两种,从敲的命令行来看,差别只是有没有-g 而已,比如
1 | npm install express # 本地安装 |
本地安装
- 将安装包放在 ./node_modules 下(运行 npm 命令时所在的目录),如果没有 node_modules 目录,会在当前执行 npm 命令的目录下生成 node_modules 目录。
- 可以通过 require() 来引入本地安装的包。
- 可通过
npm list
查看项目安装的模块
全局安装
- 将安装包放在 /usr/local 下或者你 node 的安装目录。
- 可以直接在命令行里使用。
- 可通过
npm root -g
查看所有全局安装的模块的目录: - 可通过
npm list -g
查看全局安装的模块
使用 package.json
package.json 位于模块的目录下,用于定义包的属性
npm init
初始化,生成package.json
Package.json 属性说明
name - 包名。 version - 包的版本号。 description - 包的描述。 scripts 使用
npm run command
实际执行其配置的值。例如{"dev": "node build/dev-server.js"}
homepage - 包的官网 url 。 author - 包的作者姓名。 contributors - 包的其他贡献者姓名。 dependencies - 依赖包列表。如果依赖包没有安装,npm 会自动将依赖包安装在 node_module 目录下。 repository - 包代码存放的地方的类型,可以是 git 或 svn,git 可在 Github 上。 main - main 字段指定了程序的主入口文件,require('moduleName') 就会加载这个文件。这个字段的默认值是模块根目录下面的 index.js。 keywords - 关键字
安装软件时可以将模块的依赖写入package.json
的节点下
-S
-s
--save
将安装包信息加入到dependencies
-D
-d
--save --dev
将安装包信息加入devDependencies
配置
1 | 显示所有配置 |
事件驱动
Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。 Node.js 单线程类似进入一个 while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.
Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:
1 | // 引入 events 模块 |
以下程序绑定事件处理程序:
1 | // 绑定事件及事件的处理程序 |
我们可以通过程序触发事件:
1 | // 触发事件 |
Stream(流)
Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对 http 服务器发起请求的 request 对象就是一个 Stream,还有 stdout(标准输出)。 Node.js,Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
从流中读取数据
创建 input.txt 文件,内容如下:
菜鸟教程官网地址:www.runoob.com 创建 main.js 文件, 代码如下:
1 | var fs = require("fs"); |
以上代码执行结果如下:
程序执行完毕 菜鸟教程官网地址:www.runoob.com
写入流
创建 main.js 文件, 代码如下:
1 | var fs = require("fs"); |
以上程序会将 data 变量的数据写入到 output.txt 文件中。代码执行结果如下:
node main.js 程序执行完毕 写入完成。
查看 output.txt 文件的内容:
cat output.txt 菜鸟教程官网地址:www.runoob.com
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
设置 input.txt 文件内容如下:
菜鸟教程官网地址:www.runoob.com 管道流操作实例
创建 main.js 文件, 代码如下:
1 | var fs = require("fs"); |
代码执行结果如下:
node main.js 程序执行完毕
查看 output.txt 文件的内容:
cat output.txt 菜鸟教程官网地址:www.runoob.com
管道流操作实例
链式流 链式是通过连接输出流到另外一个流并创建多个流操作链的机制。链式流一般用于管道操作。 接下来我们就是用管道和链式来压缩和解压文件。 创建 compress.js 文件, 代码如下:
1 | var fs = require("fs"); |
代码执行结果如下:
node decompress.js 文件解压完成。
模块系统
模块是 Node.js 应用程序的基本组成部分,文件和模块是一一对应的。换言之,一个 Node.js 文件就是一个模块,这个文件可能是 JavaScript 代码、JSON 或者编译过的 C/C++ 扩展。Node.js 提供了 exports 和 require 两个对象,其中 exports 是模块公开的接口,require 用于从外部获取一个模块的接口,即所获取模块的 exports 对象。
require
引入其他模块
1 | var http = require("http"); |
引入 json 文件
1 | var data = require("./data.json"); |
exports
创建 hello.js 文件,代码如下:
1 | exports.world = function () { |
则我们可以在代码中引用该模块的方法
1 | var hello = require("./hello"); |
在以上示例中,hello.js 通过 exports 对象把 world 作为模块的访问接口,在 main.js 中通过 require('./hello') 加载这个模块,然后就可以直接访问 hello.js 中 exports 对象的成员函数了。 有时候我们只是想把一个对象封装到模块中,格式如下:
1 | module.exports = function () { |
模块接口的唯一变化是使用 module.exports = Hello 代替了 exports.world = function(){}。 在外部引用该模块时,其接口对象就是要输出的 Hello 对象本身,而不是原先的 exports
export 和 import
export 与 import 是 JavaScript 用来进行模块化管理的两个关键词
export 可以标记在任何变量、方法、类的声明前 例如
1 | // 📁 say.js |
我们可以通过 import 导入其他模块的变量,语法为import {...}
1 | // 📁 main.js |
也可以给 import 的对象命名
1 | // 📁 main.js |
当使用export default
时
1 | // 📁 user.js |
export | default export |
---|---|
export class User {...} | export default class User {...} |
import {User} from ... | import User from ... |
模块的加载过程
由于 Node.js 中存在 4 类模块(原生模块和 3 种文件模块),尽管 require 方法极其简单,但是内部的加载却是十分复杂的,其加载优先级也各自不同。如下图所示:
从文件模块缓存中加载
尽管原生模块与文件模块的优先级不同,但是都会优先从文件模块的缓存中加载已经存在的模块。
从原生模块加载
原生模块的优先级仅次于文件模块缓存的优先级。require 方法在解析文件名之后,优先检查模块是否在原生模块列表中。以 http 模块为例,尽管在目录下存在一个 http/http.js/http.node/http.json 文件,require("http") 都不会从这些文件中加载,而是从原生模块中加载。 原生模块也有一个缓存区,同样也是优先从缓存区加载。如果缓存区没有被加载过,则调用原生模块的加载方式进行加载和执行。
从文件加载
当文件模块缓存中不存在,而且不是原生模块的时候,Node.js 会解析 require 方法传入的参数,并从文件系统中加载实际的文件,加载过程中的包装和编译细节在前一节中已经介绍过,这里我们将详细描述查找文件模块的过程,其中,也有一些细节值得知晓。 require 方法接受以下几种参数的传递:
- http、fs、path 等,原生模块。
- ./mod 或../mod,相对路径的文件模块。
- /pathtomodule/mod,绝对路径的文件模块。
- mod,非原生模块的文件模块。 在路径 Y 下执行 require(X) 语句执行顺序:
1 | 1. 如果 X 是内置模块 |
路由
路由简单来说是一个请求的 uri 地址与相对应的 function 的映射表
1 | var http = require("http"); |
路由中可以根据pathname
调用对应的函数去处理
1 | function route(pathname) { |
1 | var server = require("./server"); |
错误问题
npm 安装软件时 报错 reason: getaddrinfo EAI_AGAIN
关闭代理软件即可