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

3次阅读

共计 8184 个字符,预计需要花费 21 分钟才能阅读完成。

简介

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 的博客

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

正文完
 0