关于node.js:javascript开发后端程序的神器nodejs

简介

javascript尽管始终都能够做服务端编程语言,然而它更多的是以客户端编程语言来展现在世人背后的。兴许javascript本人都遗记了还能够做服务器端编程,直到2009年nodejs的横空出世。

nodejs的历史

javascript作为一门解释性语言,是不须要像C或者C++那样进行编译的。然而在晚期的时候,javascript引擎的执行效率是比拟低的,所以导致javascript只能做做dom操作。

随着ajax的衰亡和古代web2.0的技术的倒退,支流浏览器开发商尽可能的晋升javascript的执行效率,最初Chrome V8呈现了,Chrome V8是 Chromium 我的项目开源的 JavaScript 引擎,使得javascript的执行效率失去了极大的晋升。

nodejs借着V8浴火重生了。

nodejs从一诞生就取得了极大的关注。比拟javascript的开发者还是十分十分多的。而且一门语言能够通用前后端是如许的有吸引力。

nodejs从2009年倒退到2020年的nodejs 14,经验了11年的历史,和它的先辈javascript相比还是很年老,然而因为其开放性和包容性,nodejs在以一个十分快的速度向前倒退。

nodejs简介

nodejs借助于V8引擎和一组异步的 I/O 原生性能,极大的晋升了nodejs的解决效率。

异步IO咱们大家应该都很分明,和同步IO相比,线程不必阻塞,能够去解决其余更有意义的事件。只是在响应返回的时候复原操作,所以不会节约CPU工夫。

咱们简略看一下nodejs的IO模型:

一个好的语言须要有良好的生态系统相配合,因为语言自身只能提供最根本的一些操作,咱们还须要第三方零碎来丰盛这个语言的生态。

而nodejs的npm仓库,托管着寰球最大的开源库生态系统。

基本上应用nodejs你能够实现绝大多数须要的性能。

nodejs的另外一个特点就是简略,考虑一下咱们最罕用的web利用,如果用java来写,十分麻烦,你还须要一个web服务器。

在nodejs中,一切都是那么的简略:

const http = require('http')

const hostname = '127.0.0.1'
const port = 3000

const server = http.createServer((req, res) => {
  res.statusCode = 200
  res.setHeader('Content-Type', 'text/plain')
  res.end('welcome to www.flydean.com\n')
})

server.listen(port, hostname, () => {
  console.log(`please visit http://${hostname}:${port}/`)
})

下面的代码就创立了一个web服务,监听在3000端口,

咱们首先引入了http模块,用来进行http解决。

接着应用http 的 createServer() 办法会创立新的 HTTP 服务器并返回它。

在createServer办法外部,咱们能够设定要返回的对象。

最初启用server.listen性能,来监听特定的端口和服务器,当服务就绪之后,会调用前面的回调函数,执行特定的命令。

每当接管到新的申请的时候,就会触发request事件,request事件能够传递两个参数:

  • request 是一个http.IncomingMessage对象,提供了申请的详细信息。
  • response 是一个http.ServerResponse对象,用于返回数据给调用方。

在下面的例子中,咱们并没有应用request,而是应用response间接构建了返回的对象。

咱们设置了statusCode和header,最初应用end来敞开响应。

这就是一个简略应用的nodejs程序。

nodejs的运行环境

nodejs作为js的一种,是一种解释性语言,个别解释性语言都有两种运行形式。

一种是间接运行,一种是开启一个解释性的环境,在其中运行,nodejs也不例外。

间接运行很简略,咱们写好nodejs的程序之后,比方app.js,间接这样运行:

node app.js

如果间接执行node命令,就会开启REPL模式:

node
Welcome to Node.js v12.13.1.
Type ".help" for more information.
>

REPL 也被称为运行评估打印循环,是一种编程语言环境(次要是控制台窗口),它应用单个表达式作为用户输出,并在执行后将后果返回到控制台。

REPL有什么作用呢?

第一,咱们能够间接在REPL中运行某些测试方法,已验证输入后果。

比方这样:

> console.log('www.flydean.com');
www.flydean.com

除此之外REPL还有一些更加有用的性能,咱们晓得JS中所有皆对象,比方下面咱们提到的http对象,如果咱们想晓得http对象的大略构造怎么办呢?

间接在REPL环境中输出http即可:

> http
{
  _connectionListener: [Function: connectionListener],
  METHODS: [
    'ACL',         'BIND',       'CHECKOUT',
    'CONNECT',     'COPY',       'DELETE',
    'GET',         'HEAD',       'LINK',
    'LOCK',        'M-SEARCH',   'MERGE',
    'MKACTIVITY',  'MKCALENDAR', 'MKCOL',
    'MOVE',        'NOTIFY',     'OPTIONS',
    'PATCH',       'POST',       'PROPFIND',
    'PROPPATCH',   'PURGE',      'PUT',
    'REBIND',      'REPORT',     'SEARCH',
    'SOURCE',      'SUBSCRIBE',  'TRACE',
    'UNBIND',      'UNLINK',     'UNLOCK',
    'UNSUBSCRIBE'
  ],
  STATUS_CODES: {
    '100': 'Continue',
    '101': 'Switching Protocols',
    '102': 'Processing',
    '103': 'Early Hints',
    '200': 'OK',
    '201': 'Created',
    '202': 'Accepted',
    '203': 'Non-Authoritative Information',
    '204': 'No Content',
    '205': 'Reset Content',
    '206': 'Partial Content',
    '207': 'Multi-Status',
    '208': 'Already Reported',
    '226': 'IM Used',
    '300': 'Multiple Choices',
    '301': 'Moved Permanently',
    '302': 'Found',
    '303': 'See Other',
    '304': 'Not Modified',
    '305': 'Use Proxy',
    '307': 'Temporary Redirect',
    '308': 'Permanent Redirect',
    '400': 'Bad Request',
    '401': 'Unauthorized',
    '402': 'Payment Required',
    '403': 'Forbidden',
    '404': 'Not Found',
    '405': 'Method Not Allowed',
    '406': 'Not Acceptable',
    '407': 'Proxy Authentication Required',
    '408': 'Request Timeout',
    '409': 'Conflict',
    '410': 'Gone',
    '411': 'Length Required',
    '412': 'Precondition Failed',
    '413': 'Payload Too Large',
    '414': 'URI Too Long',
    '415': 'Unsupported Media Type',
    '416': 'Range Not Satisfiable',
    '417': 'Expectation Failed',
    '418': "I'm a Teapot",
    '421': 'Misdirected Request',
    '422': 'Unprocessable Entity',
    '423': 'Locked',
    '424': 'Failed Dependency',
    '425': 'Unordered Collection',
    '426': 'Upgrade Required',
    '428': 'Precondition Required',
    '429': 'Too Many Requests',
    '431': 'Request Header Fields Too Large',
    '451': 'Unavailable For Legal Reasons',
    '500': 'Internal Server Error',
    '501': 'Not Implemented',
    '502': 'Bad Gateway',
    '503': 'Service Unavailable',
    '504': 'Gateway Timeout',
    '505': 'HTTP Version Not Supported',
    '506': 'Variant Also Negotiates',
    '507': 'Insufficient Storage',
    '508': 'Loop Detected',
    '509': 'Bandwidth Limit Exceeded',
    '510': 'Not Extended',
    '511': 'Network Authentication Required'
  },
  Agent: [Function: Agent] { defaultMaxSockets: Infinity },
  ClientRequest: [Function: ClientRequest],
  IncomingMessage: [Function: IncomingMessage],
  OutgoingMessage: [Function: OutgoingMessage],
  Server: [Function: Server],
  ServerResponse: [Function: ServerResponse],
  createServer: [Function: createServer],
  get: [Function: get],
  request: [Function: request],
  maxHeaderSize: [Getter],
  globalAgent: [Getter/Setter]
}

间接输入了http对象的简洁构造,咱们还能够应用tab按钮来主动补全http的办法:

> http.
http.__defineGetter__      http.__defineSetter__      http.__lookupGetter__      http.__lookupSetter__      http.__proto__             http.constructor
http.hasOwnProperty        http.isPrototypeOf         http.propertyIsEnumerable  http.toLocaleString        http.toString              http.valueOf

http.Agent                 http.ClientRequest         http.IncomingMessage       http.METHODS               http.OutgoingMessage       http.STATUS_CODES
http.Server                http.ServerResponse        http._connectionListener   http.createServer          http.get                   http.globalAgent
http.maxHeaderSize         http.request

PREL还反对一些特定的点操作:

> .help
.break    Sometimes you get stuck, this gets you out
.clear    Alias for .break
.editor   Enter editor mode
.exit     Exit the repl
.help     Print this help message
.load     Load JS from a file into the REPL session
.save     Save all evaluated commands in this REPL session to a file

PERL还有一个非凡变量 _ ,如果在某些代码之后输出 _,则会打印最初一次操作的后果。

process

process 对象是一个全局变量,提供了无关以后 Node.js 过程的信息并对其进行管制。 作为全局变量,它始终可供 Node.js 应用程序应用,无需应用 require()。 它也能够应用 require() 显式地拜访。

因为process代表的是nodejs的过程信息,所以能够解决过程终止,读取环境变量,接管命令行参数等作用。

终止过程

先看一下怎么应用process来终止过程:

process.exit(0)

0示意失常退出,当然,咱们能够传入不同的退出码,示意不同的含意。

失常状况下,如果没有异步操作正在期待,那么 Node.js 会以状态码 0 退出,其余状况下,会用如下的状态码:

1 未捕捉异样 – 一个未被捕捉的异样, 并且没被 domain 或 ‘uncaughtException’ 事件处理器解决。

2 – 未被应用 (Bash 为防外部滥用而保留)

3 外部的 JavaScript 解析谬误 – Node.js 外部的 JavaScript 源代码在疏导过程中导致了一个语法解析谬误。个别只会在开发 Node.js 自身的时候呈现。

4 外部的 JavaScript 执行失败 – 疏导过程执行 Node.js 外部的 JavaScript 源代码时,返回函数值失败。个别只会在开发 Node.js 自身的时候呈现。

5 致命谬误 – 在 V8 中有一个致命的谬误。 比拟典型的是以 FATALERROR 为前缀从 stderr 打印进去的音讯。

6 非函数的外部异样解决 – 产生了一个外部异样,然而外部异样处理函数被设置成了一个非函数,或者不能被调用。

7 外部异样解决运行时失败 – 有一个不能被捕捉的异样,在试图解决这个异样时,处理函数自身抛出了一个谬误。比方, 如果一个 ‘uncaughtException’ 或者 domain.on(‘error’) 处理函数抛出了一个谬误。

8 – 未被应用,在之前版本的 Node.js, 退出码 8 有时候示意一个未被捕捉的异样。

9 – 不可用参数 – 某个未知选项没有确定,或者没给必须要的选项填值。

10 外部的 JavaScript 运行时失败 – 调用疏导函数时,疏导过程执行 Node.js 外部的 JavaScript 源代码抛出谬误。 个别只会在开发 Node.js 自身的时候呈现。

12 不可用的调试参数

13 未实现的Top-Level Await: await传入的Promise始终没有调用resolve办法

128 退出信号 – 如果 Node.js 接管到致命信号, 诸如 SIGKILL 或 SIGHUP,那么它的退出代码将是 128 加上信号的码值。 例如,信号 SIGABRT 的值为 6,因而预期的退出代码将为 128 + 6 或 134。

咱们能够通过process的on办法,来监听信号事件:

process.on('SIGTERM', () => {
  server.close(() => {
    console.log('过程已终止')
  })
})

什么是信号?信号是一个 POSIX 外部通信零碎:发送告诉给过程,以告知其产生的事件。

或者咱们能够从程序外部发送这个信号:

process.kill(process.pid, 'SIGTERM')

env

因为process过程是和外部环境打交道的,process提供了env属性,该属性承载了在启动过程时设置的所有环境变量。

默认状况下,env中的NODE_ENV被设置为development。

process.env.NODE_ENV // "development"

咱们能够通过批改这个环境变量,来切换nodejs的不同运行环境。

argv

process提供了argv来接管内部参数。

比方:

node app.js joe

argv是一个蕴含所有命令行调用参数的数组。

下面的例子中,第一个参数是 node 命令的残缺门路。第二个参数是正被执行的文件的残缺门路。所有其余的参数从第三个地位开始。

要想获取joe,咱们能够这样做:

const args = process.argv.slice(2)
args[0]

如果是key=value的状况,咱们能够这样传参数,并且应用minimist 库来解决参数:

node app.js --name=joe

const args = require('minimist')(process.argv.slice(2))
args['name'] //joe

CLI交互

从 nodejs7开始,nodejs提供了readline模块,能够从process.stdin获取输出:

const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout
})

readline.question(`how are you?`, answer => {
  console.log(`${answer}!`)
  readline.close()
})

如果须要更加简单的操作,则能够应用Inquirer.js:

const inquirer = require('inquirer')

var questions = [
  {
    type: 'input',
    name: 'hello',
    message: "how are you?"
  }
]

inquirer.prompt(questions).then(answers => {
  console.log(`${answers['hello']}!`)
})

exports模块

nodejs领有内置的模块零碎,当咱们须要应用其余lib提供的性能时候,咱们能够应用require来引入其余lib公开的模块。

然而前提是该lib须要公开,也就是exports对应的模块进去。

nodejs的对象导出有两种形式module.exports和将对象增加为 exports 的属性。

先看第一种形式,square 模块定义在 square.js 中:

module.exports = class Square {
  constructor(width) {
    this.width = width;
  }

  area() {
    return this.width ** 2;
  }
};

上面的例子中, bar.js 应用了导出 Square 类的 square 模块:

const Square = require('./square.js');
const mySquare = new Square(2);
console.log(`mySquare 的面积是 ${mySquare.area()}`);

再看第二种形式,定义一个circle.js:

const { PI } = Math;

exports.area = (r) => PI * r ** 2;

exports.circumference = (r) => 2 * PI * r;

应用:

const circle = require('./circle.js');
console.log(`半径为 4 的圆的面积是 ${circle.area(4)}`);

两者都能够导出特定的模块,然而module.exports只会导出特定的对象,而exports是将对象增加为exports的属性,咱们还须要依据属性名称来查找对象的属性。

nodejs API

除了咱们下面提到的http,process, nodejs还提供了很多其余十分有用的API :

nodejs的框架

除了根本的nodejs之外,nodejs还有十分多优良的框架,借助这些框架咱们能够是nodejs程序的搭建更加容易和弱小。

像AdonisJs,express,koa,Socket.io等等。

本文作者:flydean程序那些事

本文链接:http://www.flydean.com/nodejs-kickoff/

本文起源:flydean的博客

欢送关注我的公众号:「程序那些事」最艰深的解读,最粗浅的干货,最简洁的教程,泛滥你不晓得的小技巧等你来发现!

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理