npm过往基础
Node
Node概要
Node既是一个js执行环境还能充当服务器,服务器做的东西完全由编码决定。相比于用Java或者PHP而言,对于解析json文件js肯定是更方便的,直接就提供了json.parse。Node Server(需要自己从零编写,没有任何原生模块实现底层功能) / PHP Apache(默认安装好了很多底层细节操作)在Node中,我们开启的web服务是一个完全的黑盒子,它不像php。php中无论是代码还是网页都可以直接通过web url路径访问。在Node中开启的服务之所以称为黑盒子,所有资源都不允许用户访问,用户可以访问哪些资源由具体的开发人员编写设计的代码为准。在Node中可以很方便的来自定义这个url地址,Node天生就可以把url地址处理的非常的精简二漂亮,优雅而艺术。
npm安装模块相关命令行
npm list
该命令主要是为了列举出安装的某个包的版本,相当于conda list xxx_package,实际上两者是类似的。
npm ls
该命令主要用于列举出该项目中安装的所有的包,实际上效果是和npm list是一样的,只不过是简写形式而已。
npm install
-
以开发方式安装包
npm install xxx_package -D
-
一次安装多个包的方式
npm install xxx_package1 xxx_package2(实际上和conda install -c conda-forge xxx_package1 xxx_package2)
该命令同样也是为了安装某个包,具体的使用方法和conda install xxx_package是类似的,值得注意的一点就是如果在某个文件夹下第一次使用这个命令会创建一个新的node环境。简写形式为npm i xxx_packagenpm install xxx_package -g代表全局安装某个包,如果不带-g就只是本地安装某个包。-g 全局安装就是可以让我们以命令行的方式安装某个包,可以重复全局安装某个包,最后的效果就是我们的包被更新到最新版本。
npm uninstall
该命令主要用于卸载安装的模块,类似于conda remove xxx_package,效果实际上也是差不多的。
npm update
同样npm update xxx_package实际上也是更新到最新版本,conda update xx_package也是一样的效果
npm search
同样和conda search xxx_package,conda search -c channel xxx_package都是一样的效果
npm xxx_package -h
用于帮助显示某一命令具体用法,比如npm install -h就会显示npm install相关的用法
npm创建模块
实际上此处创建nodejs模块很大一部分就和conda-build的内容也是相通的,所以只要掌握好一门就可以互通
npm init
初始化模块,主要用于创建模块的第一步,生成package.json文件
npm adduser
在npm资源库中注册用户,使用邮箱进行注册,分为用户名、密码、邮件三个必选项。
npm publish
发布自定义的模块
包中的package.json文件详解
description:包的描述
where:nodejs项目位于位置,而非该模块的位置
author:开源项目的作者名字
contributors:包的其它贡献者姓名
repository:包代码存放的地方的类型,如果是git会显示github仓库的位置
bin:指定index.js文件在哪个位置
bundleDependencies:捆绑依赖的状态,即是不是只安装一个包就可以成功,如果只是一个包就代表捆绑依赖,反之就是false状态。
contacts:作者的联系方式
dependencies:依赖包,安装该目录依赖于哪一些包。
deprecated:过时状态,如果过时就为true,反之就为false
name:模块名
version:模块(包)的版本号
keywords:关键字
main:main字段指定了程序的主入口,require('moduleName')就会加载这个文件。这个字段的默认值是模块根目录下面的index.js。
npm添加镜像源
全局安装镜像模块
npm install -g cnpm --registry=https://registry.npm.taobao.org
使用cnpm代替npm
npm install xxx_package
Node.js REPL(交互解释器)
进入REPL模式
node,类似于ipython一样,使用node就可以进入node的REPL环境
下划线(_)变量
> var x = 10
> var y = 10
> x + y
> var sum = _
sum为20,因为_就代表上一次表达式输出的结果
REPL命令
ctrl + c 退出当前终端
ctrl + c 两次按下即可退出REPL模式
ctrl + d 退出Node REPL
向上/向下 查看历史的命令
tab 列出当前命令,补全作用
.help 列出使用命令帮助
.break 退出多行表达式
.clear 退出多行表达式
.save filename 保存当前Node REPL会话到指定文件
.load filename 载入当前Node REPL会话的文件内容
Node.js回调函数
回调函数优点
异步编程依托于回调来实现,但不能说使用了回调后程序就异步化了。回调函数在完成任务后就会被调用,Node使用了大量的回调函数,Node所有API都支持回调函数。我们可以一边读取文件,一边执行其他命令,在文件读取完成后,我们将文件内容作为回调函数的参数返回。这样在执行代码时就没有阻塞或等待文件 I/O 操作。这就大大提高了 Node.js 的性能,可以处理大量的并发请求。
回调函数的形式
function foo1(name, age, callback) { }
function foo2(value, callback1, callback2){ }
同步与异步的比较
console.log("同步执行文件读取------start")
var fs = require("fs")var data = fs.readFileSync('input.txt')console.log(data.toString())
console.log("同步执行文件读取------end")console.log("异步读取文件----start")fs.readFile('input.txt',function(err,data){if(err) return console.error(err);console.log(data.toString())
})console.log("异步读取文件主程序------end")
执行结果
Node.js中的事件
Node.js事件循环
Node.js是单进程单线程应用程序,但是因为V8引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。
Node.js中几乎每一个API都是支持回调函数的。基本上所有的事件机制都是用设计模式中观察者模式实现。
Node.js单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数。
Node.js的事件驱动流程
-
引入events模块
// 引入events模块 var events = require('events'); // 创建eventEmitter对象,用于事件注册与发送 var eventEmitter = new events.EventEmitter();
-
注册事件并绑定事件处理函数
// 创建事件处理函数 var connectHandler = function connected(){console.log('连接成功'); } // 注册事件并绑定事件处理函数 eventEmitter.on('connect',connectHandler);
-
触发事件进行响应
eventEmitter.emit('connnect');
EventEmitter类
-
EventEmitter类详解
events模块只提供了一个对象:events.EventEmitter。EventEmitter的核心就是事件触发和事件监听器功能的封装。
-
简单封装示例
var events = require('events'); var eventEmitter = new events.EventEmitter(); var index = 1; var timeoutHandler = function timeout(){console.log('触发事件'+index.toString()+'次');++index; } eventEmitter.on('timeout_event',timeoutHandler); // 每隔一秒触发一次事件,向eventEmitter对象发送事件timeout_event setInterval(function(){eventEmitter.emit('timeout_event'); },1000);
-
带参数的事件触发
var events = require('events'); var eventEmitter = new events.EventEmitter(); var index = 1; var timeoutHandler = function timeout(arg1,arg2){console.log('参数一'+arg1+'参数二'+arg2)console.log('触发事件'+index.toString()+'次');++index; } eventEmitter.on('timeout_event',timeoutHandler);setInterval(function(){// 传入EventHandler参数eventEmitter.emit('timeout_event','little','baby'); }, 1000);
-
EventEmitter属性介绍
// 为指定事件添加一个监听器到监听器数组的尾部(单个事件绑定多个事件监听器即多个处理函数) addListener(event,listener) // 为指定事件注册一个监听器,接收一个字符串event和一个回调函数(单个事件绑定单个监听器即响应函数) on(event,listener) // 为指定事件注册一个单次监听器,即监听器最多只会触发一次,触发后立刻解除该监听器 once(event,listener) // 移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。 // 它接受两个参数,第一个是事件名称,第二个是回调函数名称。 removeListener(event, listener) // 移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。 removeAllListeners([event]) // 默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于改变监听器的默认限制的数量。 setMaxListeners(n) // 返回指定事件的监听器数组。 listeners(event) // 按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。 emit(event, [arg1], [arg2], [...])
-
单个事件绑定多个监听器
// 绑定的多个监听器是根据绑定的顺序依次执行的 var events = require('events'); var eventEmitter = new events.EventEmitter(); var index = 1; var timeoutHandler = function timeout(arg1,arg2){console.log('参数一'+arg1+'参数二'+arg2)console.log('触发事件'+index.toString()+'次');++index; } // 为事件绑定监听器1 eventEmitter.addListener('timeout_event',timeoutHandler); var timeoutCallback = function timecallback() {console.log("执行回调函数") } // 为事件绑定监听器2 eventEmitter.addListener('timeout_event',timeoutCallback); // 打印timeout_event绑定的监听器 console.log(eventEmitter.listeners('timeout_event')) setInterval(function(){// 传入值eventEmitter.emit('timeout_event','little','baby'); },1000);
-
error事件
EventEmitter定义了一个特殊的事件error,它包含了错误的语义,我们在遇到异常的时候通常会触发error事件。当error被触发时,EventEmitter规定如果没有响应的监听器,Node.js会把它当作异常,退出程序并输出错误信息。我们一般要为会触发 error 事件的对象设置监听器,避免遇到错误后整个程序崩溃。
继承EventEmitter
大多数时候我们并不会直接使用EventEmitter,而是在对象中继承它。包括fs、net、http在内的,只要是支持事件响应的核心模块都是EventEmitter的子类。之所有这么做,原因有二。其一,具有某个实体功能的对象实现事件符合语义,事件的监听和发送应该是一个对象的方法。其二, JavaScript 的对象机制是基于原型的,支持部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。
Node.js Buffer(缓冲区)
缓冲区介绍
Javascript 语言自身只有字符串数据类型,没有二进制数据类型。但在处理TCP流或文件流时,必须使用二进制数据。因此在Node.js中,定义了一个Buffer类,该类用于创建一个专门存放二进制数据的缓冲区。在Node.js中,Buffer类随Node内核一起发布的核心库。Buffer库为Node.js带来了一种存储原始数据的方法,可以让Node.jsj处理二进制数据,每当需要在Node.js中处理I/O操作中移动的数据时,就有可能使用Buffer库。原始数据存储在Buffer类的实例中。一个Buffer类似于一个整数数组,但它对应于V8堆内存之外的一块原始内存。
Buffer类转化编码
// 将runoob以ascii方式进行存储在const常量中的Buffer中
const buf = Buffer.from('runoob', 'ascii');
// 输出为16进制
console.log(buf.toString('hex'));
console.log(buf.toString('base64'));
Buffer类支持转化的格式
- ascii - 仅支持7位ascii数据,如果设置去掉高位这种编码是非常快的。
- utf8 - 多字节编码的Unicode字符,许多网页和其它文档格式都使用UTF-8
- utf16le - 2或4个字节,小字节序编码的Unicode字符。支持代理对(U+10000至U+10FFFF)
- ucs2 - utf16le的别名
- base64 - Base64编码
- latin1 - 一种把Buffer编码成一字节编码的字符串的方式
- binary - latin1的别名
- hex - 将每个字节编码为两个十六进制字符
创建Buffer类实例
-
Buffer.alloc(size[, fill[, encoding]]):返回一个指定大小的Buffer实例,如果没有设置fill,则默认填满0
-
const buf1 = Buffer.alloc(10); // 相比于设置填充为1,这种未初始化的方法会更快 const buf2 = Buffer.alloc(10, 1);
-
-
Buffer.allocUnsafe(size): 返回一个指定大小的Buffer实例,但是它不会初始化,所以它可能包含敏感的数据
-
Buffer.allocUnsafeSlow(size)
-
Buffer.from(array): 返回一个被array的值初始化的新的Buffer实例(传入的array的元素只能是数字,不然)
-
// 创建一个包含 [0x1, 0x2, 0x3] 的 Buffer。 const buf4 = Buffer.from([1, 2, 3]);
-
-
Buffer.from(arrayBuffer[, byteOffset[, length]]): 返回一个新建的与给定的 ArrayBuffer 共享同一内存的 Buffer。
-
Buffer.from(buffer):复制传入的Buffer实例的数据,并返回一个新的Buffer实例
-
Buffer.from(string[, encoding]): 返回一个被 string 的值初始化的新的 Buffer 实例
-
// 创建一个包含 UTF-8 字节 [0x74, 0xc3, 0xa9, 0x73, 0x74] 的 Buffer。 const buf5 = Buffer.from('tést'); // 创建一个包含 Latin-1 字节 [0x74, 0xe9, 0x73, 0x74] 的 Buffer。 const buf6 = Buffer.from('tést', 'latin1');
-
写入缓冲区
// string - 写入缓冲区的字符串。offset - 缓冲区开始写入的索引值,默认为 0 。length - 写入的字节数,默认为 buffer.length。 encoding - 使用的编码。默认为 'utf8' 。
buf.write(string[, offset[, length]][, encoding])
// 根据 encoding 的字符编码写入 string 到 buf 中的 offset 位置。 length 参数是写入的字节数。 如果 buf 没有足够的空间保存整个字符串,则只会写入 string 的一部分。 只部分解码的字符不会被写入
buf = Buffer.alloc(256);
len = buf.write("www.runoob.com"); // 返回写入字节数
console.log("写入字节数 : "+ len);
从缓冲区读取数据
// encoding - 使用的编码。默认为 'utf8' 。
// start - 指定开始读取的索引位置,默认为 0。
// end - 结束位置,默认为缓冲区的末尾。
// 返回值 - 解码缓冲区数据并使用指定的编码返回字符串。
buffer.toString([encoding[, start[, end]]])// 实例
const buffer = Buffer.alloc(26);
for (var i = 0 ; i < 26 ; i++) {buf[i] = i + 97; //从小写字母a输出到z
}
console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz
console.log( buf.toString('ascii',0,5)); //使用 'ascii' 编码, 并输出: abcde
console.log( buf.toString('utf8',0,5)); // 使用 'utf8' 编码, 并输出: abcde
console.log( buf.toString(undefined,0,5)); // 使用默认的 'utf8' 编码, 并输出: abcde
返回JSON对象
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
// 输出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);
const copy = JSON.parse(json, (key, value) => {return value && value.type === 'Buffer' ?Buffer.from(value.data) :value;
});
// 输出: <Buffer 01 02 03 04 05>
console.log(copy);
Node中的模块系统
核心模块
第三方模块
自编码模块
循环加载
e
## 返回JSON对象```js
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
// 输出: {"type":"Buffer","data":[1,2,3,4,5]}
console.log(json);
const copy = JSON.parse(json, (key, value) => {return value && value.type === 'Buffer' ?Buffer.from(value.data) :value;
});
// 输出: <Buffer 01 02 03 04 05>
console.log(copy);