node.js使用手册_权威的Node.js手册

node.js使用手册

Note: you can get a PDF, ePub, or Mobi version of this handbook for easier reference, or for reading on your Kindle or tablet.

注意:您可以获取本手册的PDF,ePub或Mobi版本,以方便参考或在Kindle或平板电脑上阅读。

Node.js简介 (Introduction to Node.js)

This handbook is a getting started guide to Node.js, the server-side JavaScript runtime environment.

本手册是服务器端JavaScript运行时环境Node.js的入门指南。

总览 (Overview)

Node.js is a runtime environment for JavaScript that runs on the server.

Node.js是在服务器运行JavaScript运行时环境

Node.js is open source, cross-platform, and since its introduction in 2009, it got hugely popular and now plays a significant role in the web development scene. If GitHub stars are one popularity indication factor, having 58000+ stars means being very popular.

Node.js是开放源代码,跨平台的,自2009年问世以来,它非常受欢迎,现在在Web开发领域中发挥着重要作用。 如果GitHub星级是一个受欢迎程度的指标,那么拥有58000多个星级意味着非常受欢迎。

Node.js runs the V8 JavaScript engine, the core of Google Chrome, outside of the browser. Node.js is able to leverage the work of the engineers that made (and will continue to make) the Chrome JavaScript runtime blazing fast, and this allows Node.js to benefit from the huge performance improvements and the Just-In-Time compilation that V8 performs. Thanks to this, JavaScript code running in Node.js can become very performant.

Node.js在浏览器外部运行V8 JavaScript引擎(Google Chrome的核心)。 Node.js能够利用使(并将继续进行)Chrome JavaScript运行时快速发展的工程师的工作,这使Node.js能够从巨大的性能改进和即时编译中受益。 V8执行。 因此,在Node.js中运行JavaScript代码可以变得非常出色。

A Node.js app is run by a single process, without creating a new thread for every request. Node provides a set of asynchronous I/O primitives in its standard library that will prevent JavaScript code from blocking and generally, libraries in Node.js are written using non-blocking paradigms, making a blocking behavior an exception rather than the normal.

Node.js应用程序由单个进程运行,无需为每个请求创建新线程。 Node在其标准库中提供了一组异步I / O原语,这些原语将防止JavaScript代码被阻塞,并且通常,Node.js中的库是使用非阻塞范式编写的,从而使阻塞行为成为异常而非常规行为。

When Node.js needs to perform an I/O operation, like reading from the network, access a database or the filesystem, instead of blocking the thread Node.js will resume the operations when the response comes back, instead of wasting CPU cycles waiting.

当Node.js需要执行I / O操作(例如从网络读取,访问数据库或文件系统)而不是阻塞线程时,Node.js将在响应返回时恢复操作,而不是浪费CPU等待周期。

This allows Node.js to handle thousands of concurrent connections with a single server without introducing the burden of managing threads concurrency, which would be a major source of bugs.

这使Node.js可以在一台服务器上处理数千个并发连接,而​​不会带来管理线程并发的负担,这将是漏洞的主要来源。

Node.js has a unique advantage because millions of frontend developers that write JavaScript for the browser are now able to run the server-side code and frontend-side code without the need to learn a completely different language.

Node.js具有独特的优势,因为数百万为浏览器编写JavaScript的前端开发人员现在可以运行服务器端代码和前端代码,而无需学习完全不同的语言。

In Node.js the new ECMAScript standards can be used without problems, as you don’t have to wait for all your users to update their browsers — you are in charge of deciding which ECMAScript version to use by changing the Node.js version, and you can also enable specific experimental features by running Node with flags.

在Node.js中,可以毫无问题地使用新的ECMAScript标准,因为您不必等待所有用户更新其浏览器-您可以通过更改Node.js版本来决定要使用哪个ECMAScript版本,并且您还可以通过运行带有标志的Node来启用特定的实验功能。

它拥有大量的图书馆 (It has a huge number of libraries)

With its simple structure, the node package manager (npm) helped the ecosystem of Node.js proliferate. Now the npm registry hosts almost 500,000 open source packages you can freely use.

凭借其简单的结构,节点包管理器 ( npm )帮助Node.js的生态系统激增。 现在, npm注册表托管了将近500,000个您可以自由使用的开源软件包。

一个示例Node.js应用程序 (A sample Node.js application)

The most common example Hello World of Node.js is a web server:

最常见的Node.js的Hello World示例是Web服务器:

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('Hello World\n')})
server.listen(port, hostname, () => {  console.log(`Server running at http://${hostname}:${port}/`)})

To run this snippet, save it as a server.js file and run node server.js in your terminal.

要运行此代码段,请将其另存为server.js文件,然后在终端中运行node server.js

This code first includes the Node.js http module.

此代码首先包含Node.js http 模块

Node.js has an amazing standard library, including a first-class support for networking.

Node.js具有出色的标准库 ,包括对网络的一流支持。

The createServer() method of http creates a new HTTP server and returns it.

httpcreateServer()方法创建一个新的HTTP服务器并返回它。

The server is set to listen on the specified port and hostname. When the server is ready, the callback function is called, in this case informing us that the server is running.

服务器设置为侦听指定的端口和主机名。 服务器准备好后,将调用回调函数,在这种情况下,通知我们服务器正在运行。

Whenever a new request is received, the request event is called, providing two objects: a request (an http.IncomingMessageobject) and a response (an http.ServerResponseobject).

每当接收到新请求时,都会调用request 事件 ,并提供两个对象:一个请求(一个http.IncomingMessage对象)和一个响应(一个http.ServerResponse对象)。

These 2 objects are essential to handle the HTTP call.

这两个对象对于处理HTTP调用至关重要。

The first provides the request details. In this simple example, this is not used, but you could access the request headers and request data.

第一个提供请求详细信息。 在这个简单的示例中,没有使用它,但是您可以访问请求标头和请求数据。

The second is used to return data to the caller.

第二个用于将数据返回给调用方。

In this case with:

在这种情况下:

res.statusCode = 200

We set the statusCode property to 200, to indicate a successful response.

我们将statusCode属性设置为200 ,以指示成功的响应。

We set the Content-Type header:

我们设置Content-Type标头:

res.setHeader('Content-Type', 'text/plain')

…and we end close the response, adding the content as an argument to end():

…然后结束响应,将内容作为参数添加到end()

res.end('Hello World\n')

Node.js框架和工具 (Node.js frameworks and tools)

Node.js is a low-level platform. To make things easier and more interesting for developers, thousands of libraries were built upon Node.js.

Node.js是一个低级平台。 为了使开发人员更轻松,更有趣,在Node.js上构建了数千个库。

Many of those established over time as popular options. Here is a non-comprehensive list to the ones I consider very relevant and worth learning:

随着时间的流逝,其中许多已成为受欢迎的选择。 这是我认为非常相关且值得学习的列表,不全面:

  • Express

    表达

    One of the most simple yet powerful ways to create a web server. Its minimalist approach and unopinionated focus on the core features of a server is key to its success.

    创建Web服务器的最简单但功能最强大的方法之一。 它的极简方法和对服务器核心功能的专心致志是其成功的关键。

  • Meteor

    流星

    An incredibly powerful full-stack framework, empowering you with an isomorphic approach to building apps with JavaScript and sharing code on the client and the server. Once an off-the-shelf tool that provided everything, it now integrates with front-end libraries such as

    一个功能强大的全栈框架,使您可以采用同构方法来使用JavaScript构建应用程序并在客户端和服务器上共享代码。 一旦提供了所有功能的现成工具,它现在就可以与诸如

    React, Vue and Angular. Meteor can be used to create mobile apps as well.

    ReactVueAngular 。 流星也可以用来创建移动应用程序。

  • Koa

    考阿

    Built by the same team behind Express, Koa aims to be even simpler and smaller, building on top of years of knowledge. The new project was born out of the need to create incompatible changes without disrupting the existing community.

    Koa由Express背后的同一团队构建,其目标是在多年的知识基础上,变得更小,更小。 新项目的诞生是出于创建不兼容的更改而又不破坏现有社区的需要。

  • Next.js

    Next.js

    This is a framework to render server-side rendered

    这是渲染服务器端渲染的框架

    React applications.

    React应用程序。

  • Micro

    This is a very lightweight server to create asynchronous HTTP microservices.

    这是一个非常轻量级的服务器,用于创建异步HTTP微服务。

  • Socket.io

    套接字

    This is a real-time communication engine to build network applications.

    这是用于构建网络应用程序的实时通信引擎。

Node.js的简要历史 (A brief history of Node.js)

回顾一下从2009年至今的Node.js的历史 (A look back on the history of Node.js from 2009 to today)

Believe it or not, Node.js is just 9 years old.

信不信由你,Node.js才9岁。

In comparison, JavaScript is 23 years old and the web as we know it (after the introduction of Mosaic) is 25 years old.

相比之下,JavaScript已有23年的历史了,而我们所知道的网络(在Mosaic引入之后)已有25年的历史了。

9 years is such a little amount of time for a technology, but Node.js seems to have been around forever.

9年对于技术来说是很少的时间,但是Node.js似乎永远存在。

I’ve had the pleasure to work with Node.js since the early days when it was just 2 years old, and despite the little information available, you could already feel it was a huge thing.

从成立仅2年的早期开始,我就很高兴与Node.js一起工作,尽管可以获得的信息很少,但您已经可以感觉到这是一件大事。

In this section, I want to draw the big picture of Node.js in its history, to put things in perspective.

在本节中,我想勾勒出Node.js的历史概况,以便对事物进行透视。

一点历史 (A little bit of history)

JavaScript is a programming language that was created at Netscape as a scripting tool to manipulate web pages inside their browser, Netscape Navigator.

JavaScript是一种在Netscape中创建的编程语言,它是一种脚本工具,用于在其浏览器Netscape Navigator中操纵网页。

Part of the business model of Netscape was to sell Web Servers, which included an environment called “Netscape LiveWire”, which could create dynamic pages using server-side JavaScript. So the idea of server-side JavaScript was not introduced by Node.js, it’s old just like JavaScript — but at the time it was not successful.

Netscape的业务模型的一部分是出售Web服务器,其中包括一个称为“ Netscape LiveWire”的环境,该环境可以使用服务器端JavaScript创建动态页面。 因此,Node.js并没有引入服务器端JavaScript的想法,它像JavaScript一样古老,但是当时还没有成功。

One key factor that led to the rise of Node.js was timing. A few years ago, JavaScript was starting to be considered a serious language, thanks for the “Web 2.0” applications that showed the world what a modern experience on the web could be like (think Google Maps or GMail).

导致Node.js兴起的一个关键因素是计时。 几年前,由于“ Web 2.0”应用程序向世界展示了现代的Web体验(例如Google Maps或GMail),JavaScript开始被认为是一种严肃的语言。

The JavaScript engines performance bar raised considerably thanks to the browser competition battle, which is still going strong. Development teams behind each major browser work hard every day to give us better performance, which is a huge win for JavaScript as a platform. Chrome V8, the engine that Node.js uses under the hood, is one of those and in particular it’s the Chrome JavaScript engine.

由于浏览器竞争的激烈,JavaScript引擎的性能标准大为提高,但该竞争仍在继续。 每个主要浏览器背后的开发团队每天都在努力工作,以提高性能,这对于JavaScript作为平台是一个巨大的胜利。 Node.js在后台使用的Chrome V8引擎就是其中之一,尤其是Chrome JavaScript引擎。

But of course, Node.js is not popular just because of pure luck or timing. It introduced much innovative thinking on how to program in JavaScript on the server.

但是,当然,仅由于运气或时机原因,Node.js并不流行。 它引入了有关如何在服务器上使用JavaScript编程的许多创新思想。

2009年 (2009)

Node.js is born

Node.js诞生了

The first form of npm is created

创建npm的第一种形式

2010 (2010)

Express is born

快递出生

Socket.io is born

Socket.io诞生了

2011年 (2011)

npm hits 1.0

npm达到1.0

Big companies start adopting Node: LinkedIn, Uber

大公司开始采用Node: LinkedInUber

Hapi is born

哈皮出生

2012年 (2012)

Adoption continues very rapidly

采纳非常Swift

2013年 (2013)

First big blogging platform using Node.js: Ghost

第一个使用Node.js的大型博客平台: Ghost

Koa is born

Koa出生

2014年 (2014)

Big drama: IO.js is a major fork of Node.js, with the goal of introducing ES6 support and move faster

重大事件IO.js是Node.js的主要分支,旨在引入ES6支持并加快迁移速度

2015年 (2015)

The Node.js Foundation is born

Node.js Foundation诞生了

IO.js is merged back into Node.js

IO.js合并回Node.js

npm introduces private modules

npm引入了私有模块

Node 4 (no 1, 2, 3 versions were previously released)

节点4 (之前没有发布1、2、3个版本)

2016年 (2016)

The leftpad incident

左垫事件

Yarn is born: Node 6

纱线诞生:节点6

2017年 (2017)

npm focuses more on security: Node 8

npm更加关注安全性:节点8

HTTP/2

HTTP / 2

V8 introduces Node in its testing suite, officially making Node a target for the JavaScript engine, in addition to Chrome

V8在其测试套件中引入了Node,除了Chrome之外,它还正式使Node成为JavaScript引擎的目标

3 billion npm downloads every week

每周30亿次npm下载

2018年 (2018)

Node 10

节点10

ES modules.

ES模块

mjs experimental support

mjs实验支持

如何安装Node.js (How to install Node.js)

如何在系统上安装Node.js:软件包管理器,官方网站安装程序或nvm (How you can install Node.js on your system: a package manager, the official website installer or nvm)

Node.js can be installed in different ways. This post highlights the most common and convenient ones.

Node.js可以通过不同的方式进行安装。 这篇文章重点介绍了最常见,最方便的文章。

Official packages for all the major platforms are available here.

所有主要平台的官方软件包都可在此处获得

One very convenient way to install Node.js is through a package manager. In this case, every operating system has its own.

一种非常方便的安装Node.js的方法是通过程序包管理器。 在这种情况下,每个操作系统都有其自己的。

On macOS, Homebrew is the de-facto standard, and — once installed — allows to install Node.js very easily, by running this command in the CLI:

在macOS上, Homebrew是事实上的标准,并且一旦安装,就可以通过在CLI中运行以下命令来非常轻松地安装Node.js:

brew install node

Other package managers for Linux and Windows are listed here.

这里列出其他用于Linux和Windows的软件包管理器。

nvm is a popular way to run Node.js. It allows you to easily switch the Node.js version, and install new versions to try and easily rollback if something breaks, for example.

nvm是运行Node.js的一种流行方法。 例如,它使您可以轻松切换Node.js版本,并安装新版本以尝试并轻松回滚。

It is also very useful to test your code with old Node.js versions.

使用旧的Node.js版本测试代码也非常有用。

My suggestion is to use the official installer if you are just starting out and you don’t use Homebrew already. Otherwise, Homebrew is my favorite solution.

我的建议是,如果您刚刚开始并且还没有使用Homebrew,请使用官方安装程序。 否则,自制软件是我最喜欢的解决方案。

使用Node.js您需要知道多少JavaScript? (How much JavaScript do you need to know to use Node.js?)

If you are just starting out with JavaScript, how deeply do you need to know the language?

如果您刚开始使用JavaScript,则需要深入了解该语言?

As a beginner, it’s hard to get to a point where you are confident enough in your programming abilities.

作为一个初学者,很难达到对编程能力足够自信的地步。

While learning to code, you might also be confused at where does JavaScript end, and where Node.js begins, and vice versa.

在学习编码的同时,您可能还会对JavaScript的结束位置,Node.js的起始位置以及相反的位置感到困惑。

I would recommend you to have a good grasp of the main JavaScript concepts before diving into Node.js:

我建议您在深入研究Node.js之前先掌握主要JavaScript概念:

  • Lexical Structure

    词汇结构
  • Expressions

    表达方式
  • Types

    种类
  • Variables

    变数
  • Functions

    功能
  • this

    这个
  • Arrow Functions

    箭头功能
  • Loops

    循环
  • Loops and Scope

    循环和范围
  • Arrays

    数组
  • Template Literals

    模板文字
  • Semicolons

    分号
  • Strict Mode

    严格模式
  • ECMAScript 6, 2016, 2017

    ECMAScript 6,2016,2017

With those concepts in mind, you are well on your road to become a proficient JavaScript developer, in both the browser and in Node.js.

考虑到这些概念,无论是在浏览器还是在Node.js中,您都将成为一名熟练JavaScript开发人员。

The following concepts are also key to understand asynchronous programming, which is one fundamental part of Node.js:

以下概念也是理解异步编程的关键,异步编程是Node.js的基本组成部分:

  • Asynchronous programming and callbacks

    异步编程和回调
  • Timers

    计时器
  • Promises

    承诺
  • Async and Await

    异步并等待
  • Closures

    关闭
  • The Event Loop

    事件循环

Luckily I wrote a free ebook that explains all those topics, and it’s called JavaScript Fundamentals. It’s the most compact resource you’ll find to learn all of this.

幸运的是,我写了一本免费的电子书来解释所有这些主题,它被称为JavaScript基础 。 这是您可以找到的最紧凑的资源。

Node.js和浏览器之间的区别 (Differences between Node.js and the Browser)

How writing JavaScript application in Node.js differs from programming for the Web inside the browser.

在Node.js中编写JavaScript应用程序与在浏览器中进行Web编程的方式有所不同。

Both the browser and Node use JavaScript as their programming language.

浏览器和Node均使用JavaScript作为其编程语言。

Building apps that run in the browser is a completely different thing than building a Node.js application.

构建在浏览器中运行的应用程序与构建Node.js应用程序完全不同。

Despite the fact that it’s always JavaScript, there are some key differences that make the experience radically different.

尽管事实始终是JavaScript,但仍存在一些关键差异,使体验完全不同。

A front-end developer that writes Node.js apps has a huge advantage — the language is still the same.

编写Node.js应用程序的前端开发人员具有巨大的优势-语言仍然相同。

You have a huge opportunity because we know how hard it is to fully, deeply learn a programming language. By using the same language to perform all your work on the web — both on the client and on the server — you’re in a unique position of advantage.

您拥有巨大的机会,因为我们知道全面深入地学习编程语言是多么困难。 通过使用相同的语言在客户端和服务器上在Web上执行所有工作,您将处于独特的优势地位。

What changes is the ecosystem.

改变的是生态系统。

In the browser, most of the time what you are doing is interacting with the DOM, or other Web Platform APIs like Cookies. Those do not exist in Node.js, of course. You don’t have the document, window and all the other objects that are provided by the browser.

在浏览器中,大多数时候您正在做的是与DOM或其​​他Web平台API(例如Cookies)进行交互。 当然,那些在Node.js中不存在。 您没有documentwindow以及浏览器提供的所有其他对象。

And in the browser, we don’t have all the nice APIs that Node.js provides through its modules, like the file system access functionality.

而且在浏览器中,我们没有Node.js通过其模块提供的所有不错的API,例如文件系统访问功能。

Another big difference is that in Node.js you control the environment. Unless you are building an open source application that anyone can deploy anywhere, you know which version of Node.js you will run the application on. Compared to the browser environment, where you don’t get the luxury to choose what browser your visitors will use, this is very convenient.

另一个很大的不同是,在Node.js中,您可以控制环境。 除非您构建任何人都可以在任何地方部署的开源应用程序,否则您将知道将在哪个版本的Node.js上运行该应用程序。 与浏览器环境相比,在浏览器环境中,您无法选择访客会使用的浏览器,这非常方便。

This means that you can write all the modern ES6–7–8–9 JavaScript that your Node version supports.

这意味着您可以编写您的Node版本支持的所有现代ES6–7–8–9 JavaScript。

Since JavaScript moves so fast, but browsers can be a bit slow and users a bit slow to upgrade — sometimes on the web, you are stuck using older JavaScript/ECMAScript releases.

由于JavaScript的移动速度如此之快,但浏览器可能会变慢,而用户的升级会变慢-有时在网络上,您会被困在使用较旧JavaScript / ECMAScript版本。

You can use Babel to transform your code to be ES5-compatible before shipping it to the browser, but in Node.js, you won’t need that.

您可以使用Babel将代码转换为与ES5兼容,然后再将其交付给浏览器,但是在Node.js中,则不需要这样做。

Another difference is that Node.js uses the CommonJS module system, while in the browser we are starting to see the ES Modules standard being implemented.

另一个区别是Node.js使用CommonJS模块系统,而在浏览器中,我们开始看到正在实现ES模块标准。

In practice, this means that for the time being you use require() in Node.js and import in the browser.

实际上,这意味着您暂时在Node.js中使用require()并在浏览器中import

V8 JavaScript引擎 (The V8 JavaScript Engine)

V8 is the name of the JavaScript engine that powers Google Chrome. It’s the thing that takes our JavaScript and executes it while browsing with Chrome.

V8是支持Google Chrome浏览器JavaScript引擎的名称。 这是需要我们JavaScript并在使用Chrome浏览器时执行它的东西。

V8 provides the runtime environment in which JavaScript executes. The DOM, and the other Web Platform APIs are provided by the browser.

V8提供了运行JavaScript的运行时环境。 DOM和其他Web平台API由浏览器提供。

The cool thing is that the JavaScript engine is independent by the browser in which it’s hosted. This key feature enabled the rise of Node.js. V8 was chosen for being the engine chosen by Node.js back in 2009, and as the popularity of Node.js exploded, V8 became the engine that now powers an incredible amount of server-side code written in JavaScript.

很棒的事情是JavaScript引擎独立于托管它的浏览器。 这项关键功能推动了Node.js的兴起。 V8于2009年被选为Node.js的引擎,并且随着Node.js的爆炸性发展,V8成为了现在可以驱动大量用JavaScript编写的服务器端代码的引擎。

The Node.js ecosystem is huge and thanks to it V8 also powers desktop apps, with projects like Electron.

Node.js生态系统非常庞大,由于有了V8,V8还为桌面应用程序提供了支持,并支持Electron等项目。

其他JS引擎 (Other JS engines)

Other browsers have their own JavaScript engine:

其他浏览器具有自己JavaScript引擎:

and many others exist as well.

还有很多其他的存在。

All those engines implement the ECMA ES-262 standard, also called ECMAScript, the standard used by JavaScript.

所有这些引擎都实现ECMA ES-262标准,也称为ECMAScript,JavaScript使用该标准。

对性能的追求 (The quest for performance)

V8 is written in C++, and it’s continuously improved. It is portable and runs on Mac, Windows, Linux and several other systems.

V8用C ++编写,并且不断改进。 它是便携式的,可在Mac,Windows,Linux和其他几个系统上运行。

In this V8 introduction, I will ignore the implementation details of V8. They can be found on more authoritative sites, including the V8 official site, and they change over time, often radically.

在此V8简介中,我将忽略V8的实现细节。 可以在更权威的网站(包括V8官方网站 )上找到它们,并且它们会随着时间而发生变化,通常会发生根本性变化。

V8 is always evolving, just like the other JavaScript engines around, to speed up the Web and the Node.js ecosystem.

与其他JavaScript引擎一样,V8也在不断发展以加速Web和Node.js生态系统。

On the web, there is a race for performance that’s been going on for years, and we (as users and developers) benefit a lot from this competition because we get faster and more optimized machines year after year.

在网络上,性能竞赛一直持续了多年,我们(作为用户和开发人员)从竞争中受益匪浅,因为我们年复一年地获得了更快,更优化的机器。

汇编 (Compilation)

JavaScript is generally considered an interpreted language, but modern JavaScript engines no longer just interpret JavaScript, they compile it.

JavaScript通常被认为是一种解释语言,但是现代JavaScript引擎不再只是解释JavaScript,而是对其进行编译。

This happens since 2009 when the SpiderMonkey JavaScript compiler was added to Firefox 3.5, and everyone followed this idea.

自2009年将SpiderMonkey JavaScript编译器添加到Firefox 3.5以来,这种情况就发生了,每个人都遵循了这个想法。

JavScript is internally compiled by V8 with just-in-time (JIT) compilation to speed up the execution.

JavScript由V8内部进行实时编译(JIT),以加快执行速度。

This might seem counter-intuitive,. But since the introduction of Google Maps in 2004, JavaScript has evolved from a language that was generally executing a few dozens of lines of code to complete applications with thousands to hundreds of thousands of lines running in the browser.

这似乎违反直觉。 但是,自2004年推出Google Maps以来,JavaScript已经从一种通常执行数十行代码的语言演变为可以在浏览器中运行成千上万行的完整应用程序。

Our applications now can run for hours inside a browser, rather than being just a few form validation rules or simple scripts.

现在,我们的应用程序可以在浏览器中运行数小时,而不仅仅是一些表单验证规则或简单的脚本。

In this new world, compiling JavaScript makes perfect sense because while it might take a little bit more to have the JavaScript ready, once done it’s going to be much more performant that purely interpreted code.

在这个新世界中 ,编译JavaScript非常有意义,因为尽管准备好 JavaScript可能需要花费更多时间,但一旦完成,它将比纯解释代码具有更高的性能。

如何从Node.js程序退出 (How to exit from a Node.js program)

There are various ways to terminate a Node.js application.

有多种方法可以终止Node.js应用程序。

When running a program in the console you can close it with ctrl-C, but what I want to discuss here is programmatically exiting.

在控制台中运行程序时,可以使用ctrl-C其关闭,但是我这里要讨论的是通过编程退出。

Let’s start with the most drastic one, and see why you’re better off not using it.

让我们从最激烈的话题开始,看看为什么使用它会更好。

The process core module is provides a handy method that allows you to programmatically exit from a Node.js program: process.exit().

process核心模块提供了一种方便的方法,使您可以以编程方式退出Node.js程序: process.exit()

When Node.js runs this line, the process is immediately forced to terminate.

当Node.js运行此行时,该过程立即被迫终止。

This means that any callback that’s pending, any network request still being sent, any file system access, or processes writing to stdout or stderr — all is going to be ungracefully terminated right away.

这意味着任何待处理的回调,仍在发送任何网络请求,任何文件系统访问或写入stdoutstderr进程-所有这些都将立即被非正常终止。

If this is fine for you, you can pass an integer that signals the operating system the exit code:

如果这对您来说很好,则可以传递一个整数,向操作系统发出退出代码:

process.exit(1)

By default the exit code is 0, which means success. Different exit codes have different meaning, which you might want to use in your own system to have the program communicate to other programs.

默认情况下,退出代码为0 ,表示成功。 不同的退出代码具有不同的含义,您可能希望在自己的系统中使用这些退出代码,以使程序与其他程序进行通信。

You can read more on exit codes here.

您可以在此处阅读有关退出代码的更多信息

You can also set the process.exitCode property:

您还可以设置process.exitCode属性:

process.exitCode = 1

and when the program will later end, Node.js will return that exit code.

当程序稍后结束时,Node.js将返回该退出代码。

A program will gracefully exit when all the processing is done.

完成所有处理后,程序将正常退出。

Many times with Node.js we start servers, like this HTTP server:

很多时候,我们使用Node.js启动服务器,例如HTTP服务器:

const express = require('express')const app = express()
app.get('/', (req, res) => {  res.send('Hi!')})
app.listen(3000, () => console.log('Server ready'))

This program is never going to end. If you call process.exit(), any currently pending or running request is going to be aborted. This is not nice.

这个程序永远不会结束。 如果您调用process.exit() ,那么任何当前挂起或正在运行的请求都将被中止。 这不好

In this case you need to send the command a SIGTERM signal, and handle that with the process signal handler:

在这种情况下,您需要向该命令发送SIGTERM信号,并使用过程信号处理程序进行处理:

Note: process does not require a require, it's automatically available.

注意: process不需要require ,它是自动可用的。

const express = require('express')
const app = express()
app.get('/', (req, res) => {  res.send('Hi!')})
app.listen(3000, () => console.log('Server ready'))
process.on('SIGTERM', () => {  app.close(() => {    console.log('Process terminated')  })})

What are signals? Signals are a Portable Operating System Interface (POSIX) intercommunication system: a notification sent to a process in order to notify it of an event that occurred.

什么是信号? 信号是可移植操作系统接口(POSIX)互通系统:一种发送到进程的通知,用于将发生的事件通知给进程。

SIGKILL is the signals that tells a process to immediately terminate, and would ideally act like process.exit().

SIGKILL是告诉进程立即终止的信号,理想情况下,其行为类似于process.exit()

SIGTERM is the signals that tells a process to gracefully terminate. It is the signal that's sent from process managers like upstart or supervisord and many others.

SIGTERM是告诉进程正常终止的信号。 它是从流程管理者(例如upstartsupervisord以及其他许多人发出的信号。

You can send this signal from inside the program, in another function:

您可以在另一个函数中从程序内部发送此信号:

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

Or from another Node.js running program, or any other app running in your system that knows the PID of the process you want to terminate.

或从另一个正在运行的Node.js程序,或从系统中运行的其他任何知道您要终止的进程的PID的应用程序。

如何从Node.js读取环境变量 (How to read environment variables from Node.js)

The process core module of Node provides the envproperty which hosts all the environment variables that were set at the moment the process was started.

Node的process核心模块提供env属性,该属性承载在启动进程时设置的所有环境变量。

Here is an example that accesses the NODE_ENV environment variable, which is set to development by default.

这是访问NODE_ENV环境变量的示例,该环境变量默认情况下设置为development

process.env.NODE_ENV // "development"

Setting it to production before the script runs will tell Node.js that this is a production environment.

在脚本运行之前将其设置为production将告诉Node.js这是一个生产环境。

In the same way you can access any custom environment variable you set.

您可以用相同的方式访问您设置的任何自定义环境变量。

在哪里托管Node.js应用 (Where to host a Node.js app)

A Node.js application can be hosted in a lot of places, depending on your needs.

Node.js应用程序可以托管在很多地方,具体取决于您的需求。

Here is a non-exhaustive list of the options you can explore when you want to deploy your app and make it publicly accessible.

这是一份详尽的选项列表,您可以在要部署应用并使之公开访问时进行探索。

I will list the options from simplest and constrained to more complex and powerful.

我将列出从最简单的约束到更复杂和强大的选项。

最简单的选择:本地隧道 (Simplest option ever: local tunnel)

Even if you have a dynamic IP, or you’re under a NAT, you can deploy your app and serve the requests right from your computer using a local tunnel.

即使您拥有动态IP或处于NAT下,也可以使用本地隧道从您的计算机部署应用程序并处理请求。

This option is suited for some quick testing, demo a product or sharing of an app with a very small group of people.

此选项适用于一些快速测试,演示产品或与极少数人共享应用程序。

A very nice tool for this, available on all platforms, is ngrok.

ngrok是在所有平台上都可用的一个非常好的工具。

Using it, you can just type ngrok PORT and the PORT you want is exposed to the internet. You will get a ngrok.io domain, but with a paid subscription you can get a custom URL as well as more security options (remember that you are opening your machine to the public Internet).

使用它,您只需键入ngrok PORT ,所需的端口就会暴露在Internet上。 您将获得一个ngrok.io域,但是通过付费订阅,您可以获得一个自定义URL以及更多的安全选项(请记住,您正在向公共Internet打开计算机)。

Another service you can use is localtunnel.

您可以使用的另一项服务是localtunnel

零配置部署 (Zero configuration deployments)
毛刺 (Glitch)

Glitch is a playground and a way to build your apps faster than ever, and see them live on their own glitch.com subdomain. You cannot currently have a a custom domain, and there are a few restrictions in place, but it’s really great to prototype. It looks fun (and this is a plus), and it’s not a dumbed down environment — you get all the power of Node.js, a CDN, secure storage for credentials, GitHub import/export and much more.

Glitch是一个游乐场,可让您比以往更快地构建应用程序,并在自己的glitch.com子域中看到它们。 您目前无法拥有一个自定义域,并且有一些限制 ,但是原型制作真的很棒。 它看起来很有趣(这是一个加号),并且它不是一个愚蠢的环境-您可以获得Node.js的所有功能,CDN,用于凭证的安全存储,GitHub导入/导出等等。

Provided by the company behind FogBugz and Trello (and co-creators of Stack Overflow).

由FogBugz和Trello(以及Stack Overflow的共同创建者)背后的公司提供。

I use it a lot for demo purposes.

我经常将其用于演示目的。

码笔 (Codepen)

Codepen is an amazing platform and community. You can create a project with multiple files, and deploy it with a custom domain.

Codepen是一个了不起的平台和社区。 您可以创建包含多个文件的项目,并使用自定义域进行部署。

无服务器 (Serverless)

A way to publish your apps, and have no server at all to manage, is Serverless. Serverless is a paradigm where you publish your apps as functions, and they respond on a network endpoint (also called FAAS — Functions As A Service).

无服务器是一种发布应用且完全无需管理服务器的方法。 无服务器是一种将应用程序发布为功能的范例,它们会在网络终结点上响应(也称为FAAS —功能即服务)。

To very popular solutions are:

最受欢迎的解决方案是:

They both provide an abstraction layer to publishing on AWS Lambda and other FAAS solutions based on Azure or the Google Cloud offering.

它们都提供了一个抽象层,可以在AWS Lambda和其他基于Azure或Google Cloud产品的FAAS解决方案上发布。

PAAS (PAAS)

PAAS stands for Platform As A Service. These platforms take away a lot of things you should otherwise worry about when deploying your application.

PAAS代表平台即服务。 这些平台消除了许多您在部署应用程序时应担心的事情。

现在时代 (Zeit Now)

Zeit is an interesting option. You just type now in your terminal, and it takes care of deploying your application. There is a free version with limitations, and the paid version is more powerful. You simply forget that there’s a server, you just deploy the app.

Zeit是一个有趣的选择。 您now只需在终端中键入内容,它就可以部署您的应用程序。 有一个免费版本有限制,而付费版本则功能更强大。 您只是忘了有一个服务器,只是部署了该应用程序。

纳米盒 (Nanobox)

Nanobox

纳米盒

Heroku (Heroku)

Heroku is an amazing platform.

Heroku是一个了不起的平台。

This is a great article on getting started with Node.js on Heroku.

这是一篇关于Heroku上Node.js入门的很棒的文章。

微软Azure (Microsoft Azure)

Azure is the Microsoft Cloud offering.

Azure是Microsoft Cloud产品。

Check out how to create a Node.js web app in Azure.

了解如何在Azure中创建Node.js Web应用程序

Google Cloud Platform (Google Cloud Platform)

Google Cloud is an amazing structure for your apps.

Google Cloud对您的应用程序来说是一种了不起的结构。

They have a good Node.js Documentation Section.

他们有一个很好的Node.js文档部分

虚拟专用服务器 (Virtual Private Server)

In this section you find the usual suspects, ordered from more user friendly to less user friendly:

在本节中,您将找到通常的可疑对象,从较高的用户友好程度到较低的用户友好程度进行排序:

Since they provide an empty Linux machine on which you can work, there is no specific tutorial for these.

由于它们提供了可以在其上工作的空Linux计算机,因此没有针对它们的特定教程。

There are lots more options in the VPS category, those are just the ones I used and I would recommend.

VPS类别中还有更多选项,这些只是我使用的选项,我会建议您使用。

裸机 (Bare metal)

Another solution is to get a bare metal server, install a Linux distribution, connect it to the internet (or rent one monthly, like you can do using the Vultr Bare Metal service)

另一种解决方案是获得裸机服务器 ,安装Linux发行版,将其连接到Internet(或每月租用一个服务器,就像使用Vultr Bare Metal服务一样)

如何使用Node.js REPL (How to use the Node.js REPL)

REPL stands for Read-Evaluate-Print-Loop, and it’s a great way to explore the Node.js features in a quick way.

REPL代表Read-Evaluate-Print-Loop,它是快速探索Node.js功能的好方法。

The node command is the one we use to run our Node.js scripts:

node命令是我们用来运行Node.js脚本的命令:

node script.js

If we omit the filename, we use it in REPL mode:

如果省略文件名,则在REPL模式下使用它:

node

If you try it now in your terminal, this is what happens:

如果您现在在终端中尝试,将发生以下情况:

❯ node>

the command stays in idle mode and waits for us to enter something.

该命令将保持空闲状态,并等待我们输入内容。

Tip: if you are unsure how to open your terminal, Google “How to open terminal on <your operating system>”.

提示 :如果不确定如何打开终端,请单击“如何在<您的操作系统>上打开终端”。

The REPL is waiting for us to enter some JavaScript code.

REPL正在等待我们输入一些JavaScript代码。

Start simple and enter:

从简单开始,然后输入:

> console.log('test')testundefined>

The first value, test, is the output we told the console to print, then we get undefined which is the return value of running console.log().

第一个值test是我们告诉控制台打印的输出,然后我们得到undefined,它是正在运行的console.log()的返回值。

We can now enter a new line of JavaScript.

现在,我们可以输入一行新JavaScript。

使用标签自动完成 (Use the tab to autocomplete)

The cool thing about the REPL is that it’s interactive.

REPL的优点在于它是交互式的。

As you write your code, if you press the tab key the REPL will try to autocomplete what you wrote to match a variable you already defined or a predefined one.

在编写代码时,如果按tab键,REPL将尝试自动完成编写的内容以匹配已定义的变量或预定义的变量。

探索JavaScript对象 (Exploring JavaScript objects)

Try entering the name of a JavaScript class, like Number, add a dot and press tab.

尝试输入JavaScript类的名称,例如Number ,添加一个点并按tab

The REPL will print all the properties and methods you can access on that class:

REPL将打印您可以在该类上访问的所有属性和方法:

探索全局对象 (Explore global objects)

You can inspect the globals you have access to by typing global. and pressing tab:

您可以通过键入global.来检查您可以访问的全局变量global. 然后按tab

_特殊变量 (The _ special variable)

If after some code you type _, that is going to print the result of the last operation.

如果在某些代码后键入_ ,则将打印最后一个操作的结果。

点命令 (Dot commands)

The REPL has some special commands, all starting with a dot .. They are

REPL有一些特殊的命令,所有命令都以点开头. 。 他们是

  • .help: shows the dot commands help

    .help :显示点命令帮助

  • .editor: enables editor more, to write multiline JavaScript code with ease. Once you are in this mode, enter ctrl-D to run the code you wrote.

    .editor :使编辑者更容易编写多行JavaScript代码。 进入此模式后,输入ctrl-D运行您编写的代码。

  • .break: when inputting a multi-line expression, entering the .break command will abort further input. Same as pressing ctrl-C.

    .break :输入多行表达式时,输入.break命令将中止进一步的输入。 与按Ctrl-C相同。

  • .clear: resets the REPL context to an empty object and clears any multi-line expression currently being input.

    .clear :将REPL上下文重置为空对象,并清除当前正在输入的任何多行表达式。

  • .load: loads a JavaScript file, relative to the current working directory

    .load :加载相对于当前工作目录JavaScript文件

  • .save: saves all you entered in the REPL session to a file (specify the filename)

    .save :将您在REPL会话中输入的所有内容保存到文件中(指定文件名)

  • .exit: exists the repl (same as pressing ctrl-C two times)

    .exit :存在.exit (与按两次Ctrl-C相同)

The REPL knows when you are typing a multi-line statement without the need to invoke .editor.

REPL知道何时键入多行语句,而无需调用.editor

For example if you start typing an iteration like this:

例如,如果您开始输入这样的迭代:

[1, 2, 3].forEach(num =>; {

and you press enter, the REPL will go to a new line that starts with 3 dots, indicating you can now continue to work on that block.

然后按enter键,REPL将转到以3个点开头的新行,表示您现在可以继续在该块上工作。

... console.log(num)... })

If you type .break at the end of a line, the multiline mode will stop and the statement will not be executed.

如果在一行的末尾键入.break ,则多行模式将停止并且该语句将不会执行。

Node.js,从命令行接受参数 (Node.js, accept arguments from the command line)

How to accept arguments in a Node.js program passed from the command line

如何在从命令行传递的Node.js程序中接受参数

You can pass any number of arguments when invoking a Node.js application using:

调用Node.js应用程序时,可以使用以下任意数量的参数:

node app.js

Arguments can be standalone or have a key and a value.

参数可以是独立的,也可以具有键和值。

For example:

例如:

node app.js flavio

or

要么

node app.js name=flavio

This changes how you will retrieve this value in the Node.js code.

这将改变您在Node.js代码中检索此值的方式。

The way you retrieve it is using the process object built into Node.js.

检索它的方式是使用Node.js中内置的process对象。

It exposes an argv property, which is an array that contains all the command line invocation arguments.

它公开了一个argv属性,该属性是一个包含所有命令行调用参数的数组。

The first argument is the full path of the node command.

第一个参数是node命令的完整路径。

The second element is the full path of the file being executed.

第二个元素是正在执行的文件的完整路径。

All the additional arguments are present from the third position going forward.

从第三个位置开始,所有其他参数都存在。

You can iterate over all the arguments (including the node path and the file path) using a loop:

您可以使用循环遍历所有参数(包括节点路径和文件路径):

process.argv.forEach((val, index) => {  console.log(`${index}: ${val}`)})

You can get only the additional arguments by creating a new array that excludes the first 2 params:

您可以通过创建一个排除前两个参数的新数组来仅获取其他参数:

const args = process.argv.slice(2)

If you have one argument without an index name, like this:

如果您有一个没有索引名称的参数,如下所示:

node app.js flavio

you can access it using

您可以使用

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

In this case:

在这种情况下:

node app.js name=flavio

args[0] is name=flavio, and you need to parse it. The best way to do so is by using the minimist library, which helps dealing with arguments:

args[0]name=flavio ,您需要对其进行解析。 最好的方法是使用minimist ,该有助于处理参数:

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

使用Node.js输出到命令行 (Output to the command line using Node.js)

How to print to the command line console using Node.js, from the basic console.log to more complex scenarios

如何使用Node.js从基本console.log到更复杂的场景打印到命令行控制台

使用控制台模块的基本输出 (Basic output using the console module)

Node.js provides a console module which provides tons of very useful ways to interact with the command line.

Node.js提供了一个console 模块 ,该模块提供了大量与命令行交互的非常有用的方法。

It is basically the same as the console object you find in the browser.

它基本上与您在浏览器中找到的console对象相同。

The most basic and most used method is console.log(), which prints the string you pass to it to the console.

最基本和最常用的方法是console.log() ,该方法将您传递给控制台的字符串打印出来。

If you pass an object, it will render it as a string.

如果传递对象,它将把它渲染成字符串。

You can pass multiple variables to console.log, for example:

您可以将多个变量传递给console.log ,例如:

const x = 'x'const y = 'y'console.log(x, y)

and Node.js will print both.

和Node.js将同时打印两者。

We can also format pretty phrases by passing variables and a format specifier.

我们还可以通过传递变量和格式说明符来格式化漂亮的短语。

For example:

例如:

console.log('My %s has %d years', 'cat', 2)
  • %s format a variable as a string

    %s将变量格式化为字符串

  • %d or %i format a variable as an integer

    %d%i将变量格式化为整数

  • %f format a variable as a floating point number

    %f将变量格式化为浮点数

  • %O used to print an object representation

    %O用于打印对象表示

Example:

例:

console.log('%O', Number)
清除控制台 (Clear the console)

console.clear() clears the console (the behavior might depend on the console used)

console.clear()清除控制台(其行为可能取决于所使用的控制台)

计数元素 (Counting elements)

console.count() is a handy method.

console.count()是一种方便的方法。

Take this code:

采取以下代码:

const x = 1const y = 2const z = 3console.count(  'The value of x is ' + x + ' and has been checked .. how many times?')console.count(  'The value of x is ' + x + ' and has been checked .. how many times?')console.count(  'The value of y is ' + y + ' and has been checked .. how many times?')

What happens is that count will count the number of times a string is printed, and print the count next to it.

发生的情况是, count将对打印字符串的次数进行计数,并在其旁边打印计数。

You can just count apples and oranges:

您可以只数苹果和橙子:

const oranges = ['orange', 'orange']const apples = ['just one apple']oranges.forEach(fruit => {  console.count(fruit)})apples.forEach(fruit => {  console.count(fruit)})

There might be cases where it’s useful to print the call stack trace of a function, maybe to answer the question: “How did you reach that part of the code?”

在某些情况下,打印函数的调用堆栈跟踪很有用,也许可以回答以下问题:“您是如何到达代码的那一部分的?”

You can do so using console.trace():

您可以使用console.trace()这样做:

const function2 = () => console.trace()const function1 = () => function2()function1()

This will print the stack trace. This is what’s printed if I try this in the Node REPL:

这将打印堆栈跟踪。 如果我在Node REPL中尝试此操作,将显示以下内容:

Trace    at function2 (repl:1:33)    at function1 (repl:1:25)    at repl:1:1    at ContextifyScript.Script.runInThisContext (vm.js:44:33)    at REPLServer.defaultEval (repl.js:239:29)    at bound (domain.js:301:14)    at REPLServer.runBound [as eval] (domain.js:314:12)    at REPLServer.onLine (repl.js:440:10)    at emitOne (events.js:120:20)    at REPLServer.emit (events.js:210:7)
计算花费的时间 (Calculate the time spent)

You can easily calculate how much time a function takes to run, using time() and timeEnd()

您可以使用time()timeEnd()轻松计算函数运行所需的time()

const doSomething = () => console.log('test')const measureDoingSomething = () => {  console.time('doSomething()')  //do something, and measure the time it takes  doSomething()  console.timeEnd('doSomething()')}measureDoingSomething()
标准输出和标准错误 (stdout and stderr)

As we saw console.log is great for printing messages in the Console. This is what’s called the standard output, or stdout.

如我们所见,console.log非常适合在控制台中打印消息。 这就是所谓的标准输出或stdout

console.error prints to the stderr stream.

console.error打印到stderr流。

It will not appear in the console, but it will appear in the error log.

它不会出现在控制台中,但是会出现在错误日志中。

为输出着色 (Color the output)

You can color the output of your text in the console by using escape sequences. An escape sequence is a set of characters that identifies a color.

您可以使用转义序列在控制台中为文本的输出着色。 转义序列是标识颜色的一组字符。

Example:

例:

console.log('\x1b[33m%s\x1b[0m', 'hi!')

You can try that in the Node REPL, and it will print hi! in yellow.

您可以在Node REPL中尝试该方法,它会打印成hi! 黄色。

However, this is the low-level way to do this. The simplest way to go about coloring the console output is by using a library. Chalk is such a library, and in addition to coloring it also helps with other styling facilities, like making text bold, italic or underlined.

但是,这是执行此操作的底层方法。 为控制台输出着色的最简单方法是使用库。 Chalk是这样的一个库,除了为它上色外,它还有助于其他样式设置,例如使文本变为粗体,斜体或带下划线。

You install it with npm install chalk, then you can use it:

您可以使用npm install chalk安装它,然后可以使用它:

const chalk = require('chalk')console.log(chalk.yellow('hi!'))

Using chalk.yellow is much more convenient than trying to remember the escape codes, and the code is much more readable.

与尝试记住转义代码相比,使用chalk.yellow方便得多,并且代码更具可读性。

Check the project link I posted above for more usage examples.

检查我上面发布的项目链接以获取更多用法示例。

创建进度条 (Create a progress bar)

Progress is an awesome package to create a progress bar in the console. Install it using npm install progress.

Progress是一个很棒的软件包,可在控制台中创建进度条。 使用npm install progress安装它。

This snippet creates a 10-step progress bar, and every 100 ms one step is completed. When the bar completes we clear the interval:

此代码段创建了一个10步进度条,每100毫秒完成一个步。 当小节结束时,我们清除间隔:

const ProgressBar = require('progress')
const bar = new ProgressBar(':bar', { total: 10 })const timer = setInterval(() => {  bar.tick()  if (bar.complete) {    clearInterval(timer)  }}, 100)

接受来自Node.js命令行的输入 (Accept input from the command line in Node.js)

How to make a Node.js CLI program interactive?

如何使Node.js CLI程序具有交互性?

Node since version 7 provides the readline module to perform exactly this: get input from a readable stream such as the process.stdin stream, which during the execution of a Node program is the terminal input, one line at a time.

从版本7开始,Node提供了readline 模块来执行以下操作:从可读流(例如process.stdin流)获取输入,该流在Node程序执行期间是终端输入,一次只能一行。

const readline = require('readline').createInterface({  input: process.stdin,  output: process.stdout})
readline.question(`What's your name?`, (name) => {  console.log(`Hi ${name}!`)  readline.close()})

This piece of code asks the username, and once the text is entered and the user presses enter, we send a greeting.

这段代码询问用户名,一旦输入了文本并且用户按下Enter键,我们就会发送问候语。

The question() method shows the first parameter (a question) and waits for the user input. It calls the callback function once enter is pressed.

question()方法显示第一个参数(问题),并等待用户输入。 一旦按下回车键,它将调用回调函数。

In this callback function, we close the readline interface.

在此回调函数中,我们关闭readline接口。

readline offers several other methods, and I’ll let you check them out on the package documentation I linked above.

readline提供了其他几种方法,我将让您在上面链接的软件包文档中进行检查。

If you need to require a password, it’s best to now echo it back, but instead showing a *symbol.

如果您需要密码,最好现在将其回显,而要显示一个*符号。

The simplest way is to use the readline-sync package which is very similar in terms of the API and handles this out of the box.

最简单的方法是使用readline-sync 程序包 ,该程序包在API方面非常相似,并且可以立即使用。

A more complete and abstract solution is provided by the Inquirer.js package.

Inquirer.js软件包提供了更完整和抽象的解决方案。

You can install it using npm install inquirer, and then you can replicate the above code like this:

您可以使用npm install inquirer ,然后可以复制上述代码,如下所示:

const inquirer = require('inquirer')
var questions = [{  type: 'input',  name: 'name',  message: "What's your name?",}]
inquirer.prompt(questions).then(answers => {  console.log(`Hi ${answers['name']}!`)})

Inquirer.js lets you do many things like asking multiple choices, having radio buttons, confirmations, and more.

Inquirer.js允许您执行许多操作,例如询问多项选择,具有单选按钮,确认等。

It’s worth knowing all the alternatives, especially the built-in ones provided by Node.js, but if you plan to take CLI input to the next level, Inquirer.js is an optimal choice.

值得一提的是所有替代方案,尤其是Node.js提供的内置替代方案,但是如果您打算将CLI输入提高到一个新水平,那么Inquirer.js是一个最佳选择。

使用导出从Node.js文件公开功能 (Expose functionality from a Node.js file using exports)

How to use the module.exports API to expose data to other files in your application, or to other applications as well

如何使用module.exports API将数据公开给应用程序中的其他文件或其他应用程序

Node.js has a built-in module system.

Node.js具有内置的模块系统。

A Node.js file can import functionality exposed by other Node.js files.

Node.js文件可以导入其他Node.js文件公开的功能。

When you want to import something you use:

当您要导入内容时,请使用:

const library = require('./library')

to import the functionality exposed in the library.js file that resides in the current file folder.

导入驻留在当前文件夹中的library.js文件中提供的功能。

In this file, functionality must be exposed before it can be imported by other files.

在此文件中,必须先公开功能,然后才能由其他文件导入。

Any other object or variable defined in the file by default is private and not exposed to the outer world.

默认情况下,文件中定义的任何其他对象或变量都是私有的,不会暴露给外界。

This is what the module.exports API offered by the module system allows us to do.

这就是module 系统提供的module.exports API允许我们执行的操作。

When you assign an object or a function as a new exports property, that is the thing that’s being exposed. As such, it can be imported in other parts of your app, or in other apps as well.

当您将对象或函数分配为新的exports属性时,这就是要公开的内容。 因此,可以将其导入应用程序的其他部分,也可以导入其他应用程序。

You can do so in 2 ways.

您可以通过两种方式进行操作。

The first is to assign an object to module.exports, which is an object provided out of the box by the module system, and this will make your file export just that object:

首先是将一个对象分配给module.exports ,这是模块系统提供的开箱即用的对象,这将使文件导出该对象

const car = {  brand: 'Ford',  model: 'Fiesta'}
module.exports = car
//..in the other file
const car = require('./car')

The second way is to add the exported object as a property of exports. This way allows you to export multiple objects, functions or data:

第二种方法是将导出的对象添加为一个属性exports 。 这样,您可以导出多个对象,函数或数据:

const car = {  brand: 'Ford',  model: 'Fiesta'}
exports.car = car

or directly

或直接

exports.car = {  brand: 'Ford',  model: 'Fiesta'}

And in the other file, you’ll use it by referencing a property of your import:

在另一个文件中,您将通过引用导入的属性来使用它:

const items = require('./items')items.car

or

要么

const car = require('./items').car

What’s the difference between module.exports and exports?

module.exportsexports什么区别?

The first exposes the object it points to. The latter exposes the properties of the object it points to.

第一个公开它指向的对象 。 后者公开它指向的对象的属性

npm简介 (Introduction to npm)

npm means node package manager.

npm表示节点程序包管理器

In January 2017 over 350,000 packages were reported as being listed in the npm registry, making it the biggest single language code repository on Earth, and you can be sure there is a package for (almost!) everything.

在2017年1月,据报道在npm注册表中列出了超过350,000个软件包,这使其成为地球上最大的单一语言代码存储库,并且您可以肯定有(几乎!)所有内容的软件包。

It started as a way to download and manage dependencies of Node.js packages, but it has since become a tool used also in front-end JavaScript.

它起初是一种下载和管理Node.js包依赖项的方式,但此后它也成为前端JavaScript中使用的工具。

There are many things that npm does.

npm有很多功能。

资料下载 (Downloads)

npm manages downloads of dependencies of your project.

npm manages downloads of dependencies of your project.

Installing all dependencies (Installing all dependencies)

If a project has a packages.json file, by running

If a project has a packages.json file, by running

npm install

it will install everything the project needs, in the node_modules folder, creating it if it’s not existing already.

it will install everything the project needs, in the node_modules folder, creating it if it's not existing already.

Installing a single package (Installing a single package)

You can also install a specific package by running

You can also install a specific package by running

npm install <package-name>

Often you’ll see more flags added to this command:

Often you'll see more flags added to this command:

  • --save installs and adds the entry to the package.json file dependencies

    --save installs and adds the entry to the package.json file dependencies

  • --save-dev installs and adds the entry to the package.json file devDependencies

    --save-dev installs and adds the entry to the package.json file devDependencies

The difference is mainly that devDependencies are usually development tools, like a testing library, while dependencies are bundled with the app in production.

The difference is mainly that devDependencies are usually development tools, like a testing library, while dependencies are bundled with the app in production.

Updating packages (Updating packages)

Updating is also made easy, by running

Updating is also made easy, by running

npm update

npm will check all packages for a newer version that satisfies your versioning constraints.

npm will check all packages for a newer version that satisfies your versioning constraints.

You can specify a single package to update as well:

You can specify a single package to update as well:

npm update <package-name>
版本控制 (Versioning)

In addition to plain downloads, npm also manages versioning, so you can specify any specific version of a package, or require a version higher or lower than what you need.

In addition to plain downloads, npm also manages versioning , so you can specify any specific version of a package, or require a version higher or lower than what you need.

Many times you’ll find that a library is only compatible with a major release of another library.

Many times you'll find that a library is only compatible with a major release of another library.

Or a bug in the latest release of a lib, still unfixed, is causing an issue.

Or a bug in the latest release of a lib, still unfixed, is causing an issue.

Specifying an explicit version of a library also helps to keep everyone on the same exact version of a package, so that the whole team runs the same version until the package.json file is updated.

Specifying an explicit version of a library also helps to keep everyone on the same exact version of a package, so that the whole team runs the same version until the package.json file is updated.

In all those cases, versioning helps a lot, and npm follows the semantic versioning (semver) standard.

In all those cases, versioning helps a lot, and npm follows the semantic versioning (semver) standard.

Running Tasks (Running Tasks)

The package.json file supports a format for specifying command line tasks that can be run by using

The package.json file supports a format for specifying command line tasks that can be run by using

npm <task-name>

For example:

例如:

{  "scripts": {    "start-dev": "node lib/server-development",    "start": "node lib/server-production"  }}

It’s very common to use this feature to run Webpack:

It's very common to use this feature to run Webpack:

{  "scripts": {    "watch": "webpack --watch --progress --colors --config webpack.conf.js",    "dev": "webpack --progress --colors --config webpack.conf.js",    "prod": "NODE_ENV=production webpack -p --config webpack.conf.js",  }}

So instead of typing those long commands, which are easy to forget or mistype, you can run

So instead of typing those long commands, which are easy to forget or mistype, you can run

$ npm watch$ npm dev$ npm prod

Where does npm install the packages? (Where does npm install the packages?)

When you install a package using npm (or yarn), you can perform 2 types of installation:

When you install a package using npm (or yarn ), you can perform 2 types of installation:

  • a local install

    a local install
  • a global install

    a global install

By default, when you type an npm install command, like:

By default, when you type an npm install command, like:

npm install lodash

the package is installed in the current file tree, under the node_modules subfolder.

the package is installed in the current file tree, under the node_modules subfolder.

As this happens, npm also adds the lodash entry in the dependencies property of the package.json file present in the current folder.

As this happens, npm also adds the lodash entry in the dependencies property of the package.json file present in the current folder.

A global installation is performed using the -g flag:

A global installation is performed using the -g flag:

npm install -g lodash

When this happens, npm won’t install the package under the local folder, but instead, it will use a global location.

When this happens, npm won't install the package under the local folder, but instead, it will use a global location.

Where, exactly?

Where, exactly?

The npm root -g command will tell you where that exact location is on your machine.

The npm root -g command will tell you where that exact location is on your machine.

On macOS or Linux this location could be /usr/local/lib/node_modules. On Windows it could be C:\Users\YOU\AppData\Roaming\npm\node_modules

On macOS or Linux this location could be /usr/local/lib/node_modules . On Windows it could be C:\Users\YOU\AppData\Roaming\npm\node_modules

If you use nvm to manage Node.js versions, however, that location would differ.

If you use nvm to manage Node.js versions, however, that location would differ.

I for example use nvm and my packages location was shown as/Users/flavio/.nvm/versions/node/v8.9.0/lib/node_modules.

I for example use nvm and my packages location was shown as /Users/flavio/.nvm/versions/node/v8.9.0/lib/node_modules .

How to use or execute a package installed using npm (How to use or execute a package installed using npm)

How to include and use in your code a package installed in your node_modules folder (How to include and use in your code a package installed in your node_modules folder)

When you install using npm a package into your node_modules folder, or also globally, how do you use it in your Node code?

When you install using npm a package into your node_modules folder, or also globally, how do you use it in your Node code?

Say you install lodash, the popular JavaScript utility library, using

Say you install lodash , the popular JavaScript utility library, using

npm install lodash

This is going to install the package in the local node_modules folder.

This is going to install the package in the local node_modules folder.

To use it in your code, you just need to import it into your program using require:

To use it in your code, you just need to import it into your program using require :

const _ = require('lodash)

What if your package is an executable?

What if your package is an executable?

In this case, it will put the executable file under the node_modules/.bin/ folder.

In this case, it will put the executable file under the node_modules/.bin/ folder.

One easy way to demonstrate this is cowsay.

One easy way to demonstrate this is cowsay .

The cowsay package provides a command line program that can be executed to make a cow say something (and other animals as well).

The cowsay package provides a command line program that can be executed to make a cow say something (and other animals as well).

When you install the package using npm install cowsay, it will install itself and a few dependencies in the node_modules folder:

When you install the package using npm install cowsay , it will install itself and a few dependencies in the node_modules folder:

There is a hidden .bin folder, which contains symbolic links to the cowsay binaries.

There is a hidden .bin folder, which contains symbolic links to the cowsay binaries.

How do you execute those?

How do you execute those?

You can of course type ./node_modules/.bin/cowsay to run it, and it works, but npx, included in the recent versions of npm (since 5.2), is a much better option. You just run:

You can of course type ./node_modules/.bin/cowsay to run it, and it works, but npx , included in the recent versions of npm (since 5.2), is a much better option. You just run:

npx cowsay

and npx will find the package location.

and npx will find the package location.

package.json指南 (The package.json guide)

The package.json file is a key element in lots of app codebases based on the Node.js ecosystem.

The package.json file is a key element in lots of app codebases based on the Node.js ecosystem.

If you work with JavaScript, or you’ve ever interacted with a JavaScript project, Node.js or a front-end project, you surely met the package.json file.

If you work with JavaScript, or you've ever interacted with a JavaScript project, Node.js or a front-end project, you surely met the package.json file.

What’s that for? What should you know about it, and what are some of the cool things you can do with it?

那个有什么用途? What should you know about it, and what are some of the cool things you can do with it?

The package.json file is kind of a manifest for your project. It can do a lot of things, completely unrelated. It’s a central repository of configuration for tools, for example. It’s also where npmand yarnstore the names and versions of the package it installed.

The package.json file is kind of a manifest for your project. It can do a lot of things, completely unrelated. It's a central repository of configuration for tools, for example. It's also where npm and yarn store the names and versions of the package it installed.

The file structure (The file structure)

Here’s an example package.json file:

Here's an example package.json file:

{
}

It’s empty! There are no fixed requirements of what should be in a package.json file, for an application. The only requirement is that it respects the JSON format, otherwise it cannot be read by programs that try to access its properties programmatically.

It's empty! There are no fixed requirements of what should be in a package.json file, for an application. The only requirement is that it respects the JSON format, otherwise it cannot be read by programs that try to access its properties programmatically.

If you’re building a Node.js package that you want to distribute over npm things change radically, and you must have a set of properties that will help other people use it. We’ll see more about this later on.

If you're building a Node.js package that you want to distribute over npm things change radically, and you must have a set of properties that will help other people use it. We'll see more about this later on.

This is another package.json:

This is another package.json:

{  "name": "test-project"}

It defines a name property, which tells the name of the app, or package, that’s contained in the same folder where this file lives.

It defines a name property, which tells the name of the app, or package, that's contained in the same folder where this file lives.

Here’s a much more complex example, which I extracted this from a sample Vue.js application:

Here's a much more complex example, which I extracted this from a sample Vue.js application:

{  "name": "test-project",  "version": "1.0.0",  "description": "A Vue.js project",  "main": "src/main.js",  "private": true,  "scripts": {    "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",    "start": "npm run dev",    "unit": "jest --config test/unit/jest.conf.js --coverage",    "test": "npm run unit",    "lint": "eslint --ext .js,.vue src test/unit",    "build": "node build/build.js"  },  "dependencies": {    "vue": "^2.5.2"  },  "devDependencies": {    "autoprefixer": "^7.1.2",    "babel-core": "^6.22.1",    "babel-eslint": "^8.2.1",    "babel-helper-vue-jsx-merge-props": "^2.0.3",    "babel-jest": "^21.0.2",    "babel-loader": "^7.1.1",    "babel-plugin-dynamic-import-node": "^1.2.0",    "babel-plugin-syntax-jsx": "^6.18.0",    "babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",    "babel-plugin-transform-runtime": "^6.22.0",    "babel-plugin-transform-vue-jsx": "^3.5.0",    "babel-preset-env": "^1.3.2",    "babel-preset-stage-2": "^6.22.0",    "chalk": "^2.0.1",    "copy-webpack-plugin": "^4.0.1",    "css-loader": "^0.28.0",    "eslint": "^4.15.0",    "eslint-config-airbnb-base": "^11.3.0",    "eslint-friendly-formatter": "^3.0.0",    "eslint-import-resolver-webpack": "^0.8.3",    "eslint-loader": "^1.7.1",    "eslint-plugin-import": "^2.7.0",    "eslint-plugin-vue": "^4.0.0",    "extract-text-webpack-plugin": "^3.0.0",    "file-loader": "^1.1.4",    "friendly-errors-webpack-plugin": "^1.6.1",    "html-webpack-plugin": "^2.30.1",    "jest": "^22.0.4",    "jest-serializer-vue": "^0.3.0",    "node-notifier": "^5.1.2",    "optimize-css-assets-webpack-plugin": "^3.2.0",    "ora": "^1.2.0",    "portfinder": "^1.0.13",    "postcss-import": "^11.0.0",    "postcss-loader": "^2.0.8",    "postcss-url": "^7.2.1",    "rimraf": "^2.6.0",    "semver": "^5.3.0",    "shelljs": "^0.7.6",    "uglifyjs-webpack-plugin": "^1.1.1",    "url-loader": "^0.5.8",    "vue-jest": "^1.0.2",    "vue-loader": "^13.3.0",    "vue-style-loader": "^3.0.1",    "vue-template-compiler": "^2.5.2",    "webpack": "^3.6.0",    "webpack-bundle-analyzer": "^2.9.0",    "webpack-dev-server": "^2.9.1",    "webpack-merge": "^4.1.0"  },  "engines": {    "node": ">= 6.0.0",    "npm": ">= 3.0.0"  },  "browserslist": [    "> 1%",    "last 2 versions",    "not ie &lt;= 8"  ]}

there are lots of things going on here:

there are lots of things going on here:

  • name sets the application/package name

    name sets the application/package name

  • version indicates the current version

    version indicates the current version

  • description is a brief description of the app/package

    description is a brief description of the app/package

  • main set the entry point for the application

    main set the entry point for the application

  • private if set to true prevents the app/package to be accidentally published on npm

    private if set to true prevents the app/package to be accidentally published on npm

  • scripts defines a set of node scripts you can run

    scripts defines a set of node scripts you can run

  • dependencies sets a list of npm packages installed as dependencies

    dependencies sets a list of npm packages installed as dependencies

  • devDependencies sets a list of npm packages installed as development dependencies

    devDependencies sets a list of npm packages installed as development dependencies

  • engines sets which versions of Node this package/app works on

    engines sets which versions of Node this package/app works on

  • browserslist is used to tell which browsers (and their versions) you want to support

    browserslist is used to tell which browsers (and their versions) you want to support

All those properties are used by either npm or other tools that we can use.

All those properties are used by either npm or other tools that we can use.

Properties breakdown (Properties breakdown)

This section describes the properties you can use in detail. I refer to “package” but the same thing applies to local applications which you do not use as packages.

This section describes the properties you can use in detail. I refer to “package” but the same thing applies to local applications which you do not use as packages.

Most of those properties are only used on the npm website, other by scripts that interact with your code, like npm or others.

Most of those properties are only used on the npm website , other by scripts that interact with your code, like npm or others.

name (name)

Sets the package name.

Sets the package name.

Example:

例:

"name": "test-project"

The name must be less than 214 characters, must not have spaces, it can only contain lowercase letters, hyphens (-) or underscores (_).

The name must be less than 214 characters, must not have spaces, it can only contain lowercase letters, hyphens ( - ) or underscores ( _ ).

This is because when a package is published on npm, it gets its own URL based on this property.

This is because when a package is published on npm , it gets its own URL based on this property.

If you published this package publicly on GitHub, a good value for this property is the GitHub repository name.

If you published this package publicly on GitHub, a good value for this property is the GitHub repository name.

author (author)

Lists the package author name

Lists the package author name

Example:

例:

{  "author": "Flavio Copes <flavio@flaviocopes.com> (https://flaviocopes.com)"}

Can also be used with this format:

Can also be used with this format:

{  "author": {    "name": "Flavio Copes",    "email": "flavio@flaviocopes.com",    "url": "https://flaviocopes.com"  }}
contributors (contributors)

As well as the author, the project can have one or more contributors. This property is an array that lists them.

As well as the author, the project can have one or more contributors. This property is an array that lists them.

Example:

例:

{  "contributors": [    "Flavio Copes <flavio@flaviocopes.com> (https://flaviocopes.com)"  ]}

Can also be used with this format:

Can also be used with this format:

{  "contributors": [    {      "name": "Flavio Copes",      "email": "flavio@flaviocopes.com",      "url": "https://flaviocopes.com"    }  ]}
bugs (bugs)

Links to the package issue tracker, most likely a GitHub issues page

Links to the package issue tracker, most likely a GitHub issues page

Example:

例:

{  "bugs": "https://github.com/flaviocopes/package/issues"}
homepage (homepage)

Sets the package homepage

Sets the package homepage

Example:

例:

{  "homepage": "https://flaviocopes.com/package"}
version (version)

Indicates the current version of the package.

Indicates the current version of the package.

Example:

例:

"version": "1.0.0"

This property follows the semantic versioning (semver) notation for versions, which means the version is always expressed with 3 numbers: x.x.x.

This property follows the semantic versioning (semver) notation for versions, which means the version is always expressed with 3 numbers: xxx .

The first number is the major version, the second the minor version and the third is the patch version.

The first number is the major version, the second the minor version and the third is the patch version.

There is a meaning in these numbers: a release that only fixes bugs is a patch release, a release that introduces backward-compatible changes is a minor release, a major release can have breaking changes.

There is a meaning in these numbers: a release that only fixes bugs is a patch release, a release that introduces backward-compatible changes is a minor release, a major release can have breaking changes.

license (license)

Indicates the license of the package.

Indicates the license of the package.

Example:

例:

"license": "MIT"
keywords (keywords)

This property contains an array of keywords that associate with what your package does.

This property contains an array of keywords that associate with what your package does.

Example:

例:

"keywords": [  "email",  "machine learning",  "ai"]

This helps people find your package when navigating similar packages, or when browsing the npm website.

This helps people find your package when navigating similar packages, or when browsing the npm website.

description (description)

This property contains a brief description of the package.

This property contains a brief description of the package.

Example:

例:

"description": "A package to work with strings"

This is especially useful if you decide to publish your package to npm so that people can find out what the package is about.

This is especially useful if you decide to publish your package to npm so that people can find out what the package is about.

repository (repository)

This property specifies where this package repository is located.

This property specifies where this package repository is located.

Example:

例:

"repository": "github:flaviocopes/testing",

Notice the github prefix. There are other popular services baked in:

Notice the github prefix. There are other popular services baked in:

"repository": "gitlab:flaviocopes/testing",
"repository": "bitbucket:flaviocopes/testing",

You can explicitly set the version control system:

You can explicitly set the version control system:

"repository": {  "type": "git",  "url": "https://github.com/flaviocopes/testing.git"}

You can use different version control systems:

You can use different version control systems:

"repository": {  "type": "svn",  "url": "..."}
main (main)

Sets the entry point for the package.

Sets the entry point for the package.

When you import this package in an application, that’s where the application will search for the module exports.

When you import this package in an application, that's where the application will search for the module exports.

Example:

例:

"main": "src/main.js"
private (private)

if set to true prevents the app/package to be accidentally published on npm

if set to true prevents the app/package to be accidentally published on npm

Example:

例:

"private": true
scripts (scripts)

Defines a set of node scripts you can run

Defines a set of node scripts you can run

Example:

例:

"scripts": {  "dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",  "start": "npm run dev",  "unit": "jest --config test/unit/jest.conf.js --coverage",  "test": "npm run unit",  "lint": "eslint --ext .js,.vue src test/unit",  "build": "node build/build.js"}

These scripts are command line applications. You can run them by calling npm run XXXX or yarn XXXX, where XXXX is the command name.

These scripts are command line applications. You can run them by calling npm run XXXX or yarn XXXX , where XXXX is the command name.

Example:npm run dev

Example: npm run dev

You can use any name you want for a command, and scripts can do literally anything you want.

You can use any name you want for a command, and scripts can do literally anything you want.

dependencies (dependencies)

Sets a list of npm packages installed as dependencies.

Sets a list of npm packages installed as dependencies.

When you install a package using npm or yarn:

When you install a package using npm or yarn:

npm install <PACKAGENAME>yarn add <PACKAGENAME>

that package is automatically inserted in this list.

that package is automatically inserted in this list.

Example:

例:

"dependencies": {  "vue": "^2.5.2"}
devDependencies (devDependencies)

Sets a list of npm packages installed as development dependencies.

Sets a list of npm packages installed as development dependencies.

They differ from dependencies because they are meant to be installed only on a development machine, not needed to run the code in production.

They differ from dependencies because they are meant to be installed only on a development machine, not needed to run the code in production.

When you install a package using npm or yarn:

When you install a package using npm or yarn :

npm install --dev <PACKAGENAME>yarn add --dev <PACKAGENAME>

that package is automatically inserted in this list.

that package is automatically inserted in this list.

Example:

例:

"devDependencies": {  "autoprefixer": "^7.1.2",  "babel-core": "^6.22.1"}
engines (engines)

Sets which versions of Node.js and other commands this package/app works on.

Sets which versions of Node.js and other commands this package/app works on.

Example:

例:

"engines": {  "node": ">= 6.0.0",  "npm": ">= 3.0.0",  "yarn": "^0.13.0"}
browserslist (browserslist)

Is used to tell which browsers (and their versions) you want to support. It’s referenced by Babel, Autoprefixer, and other tools, to only add the polyfills and fallbacks needed to the browsers you target.

Is used to tell which browsers (and their versions) you want to support. It's referenced by Babel, Autoprefixer, and other tools, to only add the polyfills and fallbacks needed to the browsers you target.

Example:

例:

"browserslist": [  "> 1%",  "last 2 versions",  "not ie <= 8"]

This configuration means you want to support the last 2 major versions of all browsers with at least 1% of usage (from the CanIUse.com stats), except IE8 and lower (see more on browserslist).

This configuration means you want to support the last 2 major versions of all browsers with at least 1% of usage (from the CanIUse.com stats), except IE8 and lower ( see more on browserslist).

Command-specific properties (Command-specific properties)

The package.json file can also host command-specific configuration, for example for Babel, ESLint, and more.

The package.json file can also host command-specific configuration, for example for Babel, ESLint, and more.

Each has a specific property, like eslintConfig, babel and others. Those are command-specific, and you can find how to use those in the respective command/project documentation.

Each has a specific property, like eslintConfig , babel and others. Those are command-specific, and you can find how to use those in the respective command/project documentation.

Package versions (Package versions)

You have seen in the description above version numbers like these: ~3.0.0 or ^0.13.0. What do they mean, and which other version specifiers can you use?

You have seen in the description above version numbers like these: ~3.0.0 or ^0.13.0 . What do they mean, and which other version specifiers can you use?

That symbol specifies which updates you package accepts, from that dependency.

That symbol specifies which updates you package accepts, from that dependency.

Given that using semver (semantic versioning) all versions have 3 digits, the first being the major release, the second the minor release and the third is the patch release, you have these rules:

Given that using semver (semantic versioning) all versions have 3 digits, the first being the major release, the second the minor release and the third is the patch release, you have these rules:

  • ~: if you write ~0.13.0, you want to only update patch releases: 0.13.1 is ok, but 0.14.0 is not.

    ~ : if you write ~0.13.0 , you want to only update patch releases: 0.13.1 is ok, but 0.14.0 is not.

  • ^: if you write ^0.13.0, you want to update patch and minor releases: 0.13.1, 0.14.0and so on.

    ^ : if you write ^0.13.0 , you want to update patch and minor releases: 0.13.1 , 0.14.0 and so on.

  • *: if you write *, that means you accept all updates, including major version upgrades.

    * : if you write * , that means you accept all updates, including major version upgrades.

  • &gt;: you accept any version higher than the one you specify

    & gt;: you accept any version higher than the one you specify

  • &gt;=: you accept any version equal to or higher than the one you specify

    &g t;=: you accept any version equal to or higher than the one you specify

  • &lt;=: you accept any version equal or lower to the one you specify

    &l t;=: you accept any version equal or lower to the one you specify

  • &lt;: you accept any version lower to the one you specify

    & lt;: you accept any version lower to the one you specify

There are other rules, too:

There are other rules, too:

  • no symbol: you accept only that specific version you specify

    no symbol: you accept only that specific version you specify
  • latest: you want to use the latest version available

    latest : you want to use the latest version available

and you can combine most of the above in ranges, like this: 1.0.0 || >=1.1.0 <;1.2.0, to either use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.

and you can combine most of the above in ranges, like this: 1.0.0 || >=1.1.0 < ;1.2.0, to either use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.

package-lock.json文件 (The package-lock.json file)

The package-lock.json file is automatically generated when installing node packages.

The package-lock.json file is automatically generated when installing node packages.

In version 5, npm introduced the package-lock.json file.

In version 5, npm introduced the package-lock.json file.

What’s that? You probably know about the package.json file, which is much more common and has been around for much longer.

那是什么? You probably know about the package.json file, which is much more common and has been around for much longer.

The goal of the file is to keep track of the exact version of every package that is installed so that a product is 100% reproducible in the same way even if packages are updated by their maintainers.

The goal of the file is to keep track of the exact version of every package that is installed so that a product is 100% reproducible in the same way even if packages are updated by their maintainers.

This solves a very specific problem that package.json left unsolved. In package.json you can set which versions you want to upgrade to (patch or minor), using the semver notation, for example:

This solves a very specific problem that package.json left unsolved. In package.json you can set which versions you want to upgrade to (patch or minor), using the semver notation, for example:

  • if you write ~0.13.0, you want to only update patch releases: 0.13.1 is ok, but 0.14.0is not.

    if you write ~0.13.0 , you want to only update patch releases: 0.13.1 is ok, but 0.14.0 is not.

  • if you write ^0.13.0, you want to update patch and minor releases: 0.13.1, 0.14.0and so on.

    if you write ^0.13.0 , you want to update patch and minor releases: 0.13.1 , 0.14.0 and so on.

  • if you write 0.13.0, that is the exact version that will be used, always

    if you write 0.13.0 , that is the exact version that will be used, always

You don’t commit to Git your node_modules folder, which is generally huge, and when you try to replicate the project on another machine by using the npm install command, if you specified the ~ syntax and a patch release of a package has been released, that one is going to be installed. Same for ^ and minor releases.

You don't commit to Git your node_modules folder, which is generally huge, and when you try to replicate the project on another machine by using the npm install command, if you specified the ~ syntax and a patch release of a package has been released, that one is going to be installed. Same for ^ and minor releases.

If you specify exact versions, like 0.13.0 in the example, you are not affected by this problem.

If you specify exact versions, like 0.13.0 in the example, you are not affected by this problem.

It could be you, or another person trying to initialize the project on the other side of the world by running npm install.

It could be you, or another person trying to initialize the project on the other side of the world by running npm install .

So your original project and the newly initialized project are actually different. Even if a patch or minor release should not introduce breaking changes, we all know bugs can (and so, they will) slide in.

So your original project and the newly initialized project are actually different. Even if a patch or minor release should not introduce breaking changes, we all know bugs can (and so, they will) slide in.

The package-lock.json sets your currently installed version of each package in stone, and npm will use those exact versions when running npm install.

The package-lock.json sets your currently installed version of each package in stone , and npm will use those exact versions when running npm install .

This concept is not new, and other programming languages package managers (like Composer in PHP) use a similar system for years.

This concept is not new, and other programming languages package managers (like Composer in PHP) use a similar system for years.

The package-lock.json file needs to be committed to your Git repository, so it can be fetched by other people, if the project is public or you have collaborators, or if you use Git as a source for deployments.

The package-lock.json file needs to be committed to your Git repository, so it can be fetched by other people, if the project is public or you have collaborators, or if you use Git as a source for deployments.

The dependencies versions will be updated in the package-lock.json file when you run npm update.

The dependencies versions will be updated in the package-lock.json file when you run npm update .

一个例子 (An example)

This is an example structure of a package-lock.json file we get when we run npm install cowsay in an empty folder:

This is an example structure of a package-lock.json file we get when we run npm install cowsay in an empty folder:

{  "requires": true,  "lockfileVersion": 1,  "dependencies": {    "ansi-regex": {      "version": "3.0.0",      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",      "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg="    },    "cowsay": {      "version": "1.3.1",      "resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz",      "integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkMAjufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",      "requires": {        "get-stdin": "^5.0.1",        "optimist": "~0.6.1",        "string-width": "~2.1.1",        "strip-eof": "^1.0.0"      }    },    "get-stdin": {      "version": "5.0.1",      "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz",      "integrity": "sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g="    },    "is-fullwidth-code-point": {      "version": "2.0.0",      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz",      "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8="    },    "minimist": {      "version": "0.0.10",      "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz",      "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8="    },    "optimist": {      "version": "0.6.1",      "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",      "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {        "minimist": "~0.0.1",        "wordwrap": "~0.0.2"      }    },    "string-width": {      "version": "2.1.1",      "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz",      "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==",      "requires": {        "is-fullwidth-code-point": "^2.0.0",        "strip-ansi": "^4.0.0"      }    },    "strip-ansi": {      "version": "4.0.0",      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",      "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",      "requires": {        "ansi-regex": "^3.0.0"      }    },    "strip-eof": {      "version": "1.0.0",      "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",      "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8="    },    "wordwrap": {      "version": "0.0.3",      "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",      "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc="    }  }}

We installed cowsay, which depends on:

We installed cowsay , which depends on:

  • get-stdin

    get-stdin

  • optimist

    optimist

  • string-width

    string-width

  • strip-eof

    strip-eof

In turn, those packages require other packages, as we can see from the requires property that some have:

In turn, those packages require other packages, as we can see from the requires property that some have:

  • ansi-regex

    ansi-regex

  • is-fullwidth-code-point

    is-fullwidth-code-point

  • minimist

    minimist

  • wordwrap

    wordwrap

  • strip-eof

    strip-eof

They are added in alphabetical order into the file, and each one has a version field, a resolved field that points to the package location, and an integrity string that we can use to verify the package.

They are added in alphabetical order into the file, and each one has a version field, a resolved field that points to the package location, and an integrity string that we can use to verify the package.

Find the installed version of an npm package (Find the installed version of an npm package)

To see the latest version of all the npm package installed, including their dependencies:

To see the latest version of all the npm package installed, including their dependencies:

npm list

Example:

例:

❯ npm list/Users/flavio/dev/node/cowsay└─┬ cowsay@1.3.1  ├── get-stdin@5.0.1  ├─┬ optimist@0.6.1  │ ├── minimist@0.0.10  │ └── wordwrap@0.0.3  ├─┬ string-width@2.1.1  │ ├── is-fullwidth-code-point@2.0.0  │ └─┬ strip-ansi@4.0.0  │   └── ansi-regex@3.0.0  └── strip-eof@1.0.0

You can also just open the package-lock.json file, but this involves some visual scanning.

You can also just open the package-lock.json file, but this involves some visual scanning.

npm list -g is the same, but for globally installed packages.

npm list -g is the same, but for globally installed packages.

To get only your top-level packages (basically, the ones you told npm to install and you listed in the package.json), run npm list --depth=0:

To get only your top-level packages (basically, the ones you told npm to install and you listed in the package.json ), run npm list --depth=0 :

❯ npm list --depth=0/Users/flavio/dev/node/cowsay└── cowsay@1.3.1

You can get the version of a specific package by specifying the name:

You can get the version of a specific package by specifying the name:

❯ npm list cowsay/Users/flavio/dev/node/cowsay└── cowsay@1.3.1

This also works for dependencies of packages you installed:

This also works for dependencies of packages you installed:

❯ npm list minimist/Users/flavio/dev/node/cowsay└─┬ cowsay@1.3.1  └─┬ optimist@0.6.1    └── minimist@0.0.10

If you want to see what’s the latest available version of the package on the npm repository, run npm view [package_name] version:

If you want to see what's the latest available version of the package on the npm repository, run npm view [package_name] version :

❯ npm view cowsay version
1.3.1

Install an older version of an npm package (Install an older version of an npm package)

Installing an older version of an npm package might be useful to solve a compatibility problem.

Installing an older version of an npm package might be useful to solve a compatibility problem.

You can install an old version of an npm package using the @ syntax:

You can install an old version of an npm package using the @ syntax:

npm install <package>@<;version>

Example:

例:

npm install cowsay

installs version 1.3.1 (at the time of writing).

installs version 1.3.1 (at the time of writing).

Install version 1.2.0 with:

Install version 1.2.0 with:

npm install cowsay@1.2.0

The same can be done with global packages:

The same can be done with global packages:

npm install -g webpack@4.16.4

You might also be interested in listing all the previous version of a package. You can do it with npm view <package> versions:

You might also be interested in listing all the previous version of a package. You can do it with npm view <package> ve rsions:

❯ npm view cowsay versions
[ '1.0.0',  '1.0.1',  '1.0.2',  '1.0.3',  '1.1.0',  '1.1.1',  '1.1.2',  '1.1.3',  '1.1.4',  '1.1.5',  '1.1.6',  '1.1.7',  '1.1.8',  '1.1.9',  '1.2.0',  '1.2.1',  '1.3.0',  '1.3.1' ]

Update all the Node dependencies to their latest version (Update all the Node dependencies to their latest version)

When you install a package using npm install <packagename>, the latest available version of the package is downloaded and put in the node_modules folder, and a corresponding entry is added to the package.json and package-lock.json files that are present in your current folder.

When you install a package using npm install <packagena me>, the latest available version of the package is downloaded and put i n the node_m odules folder, and a corresponding entry is added t o the packag e.jso n and package-loc k.json files that are present in your current folder.

npm calculates the dependencies and installs the latest available version of those as well.

npm calculates the dependencies and installs the latest available version of those as well.

Let’s say you install cowsay, a cool command line tool that lets you make a cow say things.

Let's say you install cowsay , a cool command line tool that lets you make a cow say things .

When you npm install cowsay, this entry is added to the package.json file:

When you npm install cowsay , this entry is added to the package.json file:

{  "dependencies": {    "cowsay": "^1.3.1"  }}

and this is an extract of package-lock.json, where I removed the nested dependencies for clarity:

and this is an extract of package-lock.json , where I removed the nested dependencies for clarity:

{  "requires": true,  "lockfileVersion": 1,  "dependencies": {    "cowsay": {      "version": "1.3.1",      "resolved": "https://registry.npmjs.org/cowsay/-/cowsay-1.3.1.tgz",      "integrity": "sha512-3PVFe6FePVtPj1HTeLin9v8WyLl+VmM1l1H/5P+BTTDkMAjufp+0F9eLjzRnOHzVAYeIYFF5po5NjRrgefnRMQ==",      "requires": {        "get-stdin": "^5.0.1",        "optimist": "~0.6.1",        "string-width": "~2.1.1",        "strip-eof": "^1.0.0"      }    }  }}

Now those 2 files tell us that we installed version 1.3.1 of cowsay, and our rule for updates is ^1.3.1, which for the npm versioning rules (explained later on) means that npm can update to patch and minor releases: 0.13.1, 0.14.0 and so on.

Now those 2 files tell us that we installed version 1.3.1 of cowsay, and our rule for updates is ^1.3.1 , which for the npm versioning rules (explained later on) means that npm can update to patch and minor releases: 0.13.1 , 0.14.0 and so on.

If there is a new minor or patch release and we type npm update, the installed version is updated, and the package-lock.json file diligently filled with the new version.

If there is a new minor or patch release and we type npm update , the installed version is updated, and the package-lock.json file diligently filled with the new version.

package.json remains unchanged.

package.json remains unchanged.

To discover new releases of the packages, you run npm outdated.

To discover new releases of the packages, you run npm outdated .

Here’s the list of a few outdated packages in one repository I didn’t update for quite a while:

Here's the list of a few outdated packages in one repository I didn't update for quite a while:

Some of those updates are major releases. Running npm update won’t update the version of those. Major releases are never updated in this way because they (by definition) introduce breaking changes, and npm want to save you trouble.

Some of those updates are major releases. Running npm update won't update the version of those. Major releases are never updated in this way because they (by definition) introduce breaking changes, and npm want to save you trouble.

To update to a new major version all the packages, install the npm-check-updates package globally:

To update to a new major version all the packages, install the npm-check-updates package globally:

npm install -g npm-check-updates

then run it:

then run it:

ncu -u

This will upgrade all the version hints in the package.json file, to dependencies and devDependencies, so npm can install the new major version.

This will upgrade all the version hints in the package.json file, to dependencies and devDependencies , so npm can install the new major version.

You are now ready to run the update:

You are now ready to run the update:

npm update

If you just downloaded the project without the node_modules dependencies and you want to install the shiny new versions first, just run

If you just downloaded the project without the node_modules dependencies and you want to install the shiny new versions first, just run

npm install

使用npm的语义版本控制 (Semantic Versioning using npm)

Semantic Versioning is a convention used to provide a meaning to versions.

Semantic Versioning is a convention used to provide a meaning to versions.

If there’s one great thing in Node.js packages, is that all agreed on using Semantic Versioning for their version numbering.

If there's one great thing in Node.js packages, is that all agreed on using Semantic Versioning for their version numbering.

The Semantic Versioning concept is simple: all versions have 3 digits: x.y.z.

The Semantic Versioning concept is simple: all versions have 3 digits: xyz .

  • the first digit is the major version

    the first digit is the major version
  • the second digit is the minor version

    the second digit is the minor version
  • the third digit is the patch version

    the third digit is the patch version

When you make a new release, you don’t just up a number as you please, but you have rules:

When you make a new release, you don't just up a number as you please, but you have rules:

  • you up the major version when you make incompatible API changes

    you up the major version when you make incompatible API changes
  • you up the minor version when you add functionality in a backward-compatible manner

    you up the minor version when you add functionality in a backward-compatible manner
  • you up the patch version when you make backward-compatible bug fixes

    you up the patch version when you make backward-compatible bug fixes

The convention is adopted all across programming languages, and it is very important that every npm package adheres to it, because the whole system depends on that.

The convention is adopted all across programming languages, and it is very important that every npm package adheres to it, because the whole system depends on that.

Why is that so important?

Why is that so important?

Because npm set some rules we can use in the package.json fileto choose which versions it can update our packages to, when we run npm update.

Because npm set some rules we can use in the package.json file to choose which versions it can update our packages to, when we run npm update .

The rules use those symbols:

The rules use those symbols:

  • ^

    ^

  • ~

    ~

  • &gt;

    & gt;

  • &gt;=

    &g t;=

  • &lt;

    & lt;

  • &lt;=

    &l t;=

  • =

    =

  • -

    -

  • ||

    ||

Let’s see those rules in detail:

Let's see those rules in detail:

  • ^: if you write ^0.13.0 when running npm update it can update to patch and minor releases: 0.13.1, 0.14.0 and so on.

    ^ : if you write ^0.13.0 when running npm update it can update to patch and minor releases: 0.13.1 , 0.14.0 and so on.

  • ~: if you write ~0.13.0, when running npm update it can update to patch releases: 0.13.1 is ok, but 0.14.0 is not.

    ~ : if you write ~0.13.0 , when running npm update it can update to patch releases: 0.13.1 is ok, but 0.14.0 is not.

  • &gt;: you accept any version higher than the one you specify

    & gt;: you accept any version higher than the one you specify

  • &gt;=: you accept any version equal to or higher than the one you specify

    &g t;=: you accept any version equal to or higher than the one you specify

  • &lt;=: you accept any version equal or lower to the one you specify

    &l t;=: you accept any version equal or lower to the one you specify

  • &lt;: you accept any version lower to the one you specify

    & lt;: you accept any version lower to the one you specify

  • =: you accept that exact version

    = : you accept that exact version

  • -: you accept a range of versions. Example: 2.1.0 - 2.6.2

    - : you accept a range of versions. Example: 2.1.0 - 2.6.2

  • ||: you combine sets. Example: < 2.1 || &gt; 2.6

    || : you combine sets. Example: < 2.1 || &g t; 2.6

You can combine some of those notations, for example use 1.0.0 || >=1.1.0 <;1.2.0 to either use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.

You can combine some of those notations, for example use 1.0.0 || >=1.1.0 < ;1.2.0 to either use 1.0.0 or one release from 1.1.0 up, but lower than 1.2.0.

There are other rules, too:

There are other rules, too:

  • no symbol: you accept only that specific version you specify (1.2.1)

    no symbol: you accept only that specific version you specify ( 1.2.1 )

  • latest: you want to use the latest version available

    latest : you want to use the latest version available

Uninstalling npm packages locally or globally (Uninstalling npm packages locally or globally)

To uninstall a package you have previously installed locally (using npm install <package-name> in the node_modules folder), run:

To uninstall a package you have previously installed locally (using npm install <package-na me> i n the node_m odules folder), run:

npm uninstall <package-name>

Using the -S flag, or --save, this operation will also remove the reference in the package.json file.

Using the -S flag, or --save , this operation will also remove the reference in the package.json file .

If the package was a development dependency, listed in the devDependencies of the package.json file, you must use the -D / --save-dev flag to remove it from the file:

If the package was a development dependency, listed in the devDependencies of the package.json file, you must use the -D / --save-dev flag to remove it from the file:

npm uninstall -S <package-name>npm uninstall -D <package-name>

If the package is installed globally, you need to add the -g / --global flag:

If the package is installed globally , you need to add the -g / --global flag:

npm uninstall -g <package-name>

Example:

例:

npm uninstall -g webpack

and you can run this command from anywhere you want on your system because the folder where you currently are does not matter.

and you can run this command from anywhere you want on your system because the folder where you currently are does not matter.

npm global or local packages (npm global or local packages)

When is a package best installed globally? And why?

When is a package best installed globally? 又为什么呢

The main difference between local and global packages is this:

The main difference between local and global packages is this:

  • local packages are installed in the directory where you run npm install <package-name>, and they are put in the node_modules folder under this directory

    local packages are installed in the directory where you run npm install <package-na me>, and they are put i n the node_m odules folder under this directory

  • global packages are all put in a single place in your system (exactly where depends on your setup), regardless of where you run npm install -g <package-name>

    global packages are all put in a single place in your system (exactly where depends on your setup), regardless of where you run npm install -g <package-na me>

In your code, they are both required in the same way:

In your code, they are both required in the same way:

require('package-name')

So when should you install in one way or another?

So when should you install in one way or another?

In general, all packages should be installed locally.

In general, all packages should be installed locally .

This makes sure you can have dozens of applications in your computer, all running a different version of each package if needed.

This makes sure you can have dozens of applications in your computer, all running a different version of each package if needed.

Updating a global package would make all your projects use the new release, and as you can imagine this might cause nightmares in terms of maintenance, as some packages might break compatibility with further dependencies, and so on.

Updating a global package would make all your projects use the new release, and as you can imagine this might cause nightmares in terms of maintenance, as some packages might break compatibility with further dependencies, and so on.

All projects have their own local version of a package, even if this might appear like a waste of resources, it’s minimal compared to the possible negative consequences.

All projects have their own local version of a package, even if this might appear like a waste of resources, it's minimal compared to the possible negative consequences.

A package should be installed globally when it provides an executable command that you run from the shell (CLI), and it’s reused across projects.

A package should be installed globally when it provides an executable command that you run from the shell (CLI), and it's reused across projects.

You can also install executable commands locally and run them using npx, but some packages are just better installed globally.

You can also install executable commands locally and run them using npx , but some packages are just better installed globally.

Great examples of popular global packages which you might know are:

Great examples of popular global packages which you might know are:

  • npm

    npm

  • create-react-app

    create-react-app

  • vue-cli

    vue-cli

  • grunt-cli

    grunt-cli

  • mocha

    mocha

  • react-native-cli

    react-native-cli

  • gatsby-cli

    gatsby-cli

  • forever

    forever

  • nodemon

    nodemon

You probably have some packages installed globally already on your system. You can see them by running:

You probably have some packages installed globally already on your system. You can see them by running:

npm list -g --depth 0

on your command line.

on your command line.

npm dependencies and devDependencies (npm dependencies and devDependencies)

When is a package a dependency, and when is it a development dependency?

When is a package a dependency, and when is it a development dependency?

When you install an npm package using npm install <package-name>, you are installing it as a dependency.

When you install an npm package using npm install <package-na me>, you are installing it as a depe ndency.

The package is automatically listed in the package.json file, under the dependencies list (as of npm 5: before you had to manually specify --save).

The package is automatically listed in the package.json file, under the dependencies list (as of npm 5: before you had to manually specify --save ).

When you add the -D flag, or --save-dev, you are installing it as a development dependency, which adds it to the devDependencies list.

When you add the -D flag, or --save-dev , you are installing it as a development dependency, which adds it to the devDependencies list.

Development dependencies are intended as development-only packages, that are unneeded in production. For example testing packages, webpack or Babel.

Development dependencies are intended as development-only packages, that are unneeded in production. For example testing packages, webpack or Babel.

When you go in production, if you type npm install and the folder contains a package.json file, they are installed, as npm assumes this is a development deploy.

When you go in production , if you type npm install and the folder contains a package.json file, they are installed, as npm assumes this is a development deploy.

You need to set the --production flag (npm install --production) to avoid installing those development dependencies.

You need to set the --production flag ( npm install --production ) to avoid installing those development dependencies.

npx节点包运行器 (The npx Node Package Runner)

npx is a very cool way to run the Node.js codes, and provides many useful features.

npx is a very cool way to run the Node.js codes, and provides many useful features.

In this section, I want to introduce a very powerful command that’s been available in npm starting version 5.2, released in July 2017: npx.

In this section, I want to introduce a very powerful command that's been available in npm starting version 5.2, released in July 2017: npx .

If you don’t want to install npm, you can install npx as a standalone package.

If you don't want to install npm, you can install npx as a standalone package .

npx lets you run code built with Node.js and published through the npm registry.

npx lets you run code built with Node.js and published through the npm registry.

Easily run local commands (Easily run local commands)

Node.js developers used to publish most of the executable commands as global packages, in order for them to be in the path and executable immediately.

Node.js developers used to publish most of the executable commands as global packages, in order for them to be in the path and executable immediately.

This was a pain because you could not really install different versions of the same command.

This was a pain because you could not really install different versions of the same command.

Running npx commandname automatically finds the correct reference of the command inside the node_modules folder of a project, without needing to know the exact path, and without requiring the package to be installed globally and in the user’s path.

Running npx commandname automatically finds the correct reference of the command inside the node_modules folder of a project, without needing to know the exact path, and without requiring the package to be installed globally and in the user's path.

Installation-less command execution (Installation-less command execution)

There is another great feature of npm, which is allowing to run commands without first installing them.

There is another great feature of npm , which is allowing to run commands without first installing them.

This is pretty useful, mostly because:

This is pretty useful, mostly because:

  1. you don’t need to install anything

    you don't need to install anything
  2. you can run different versions of the same command, using the syntax @version

    you can run different versions of the same command, using the syntax @version

A typical demonstration of using npx is through the cowsay command. cowsay will print a cow saying what you wrote in the command. For example:

A typical demonstration of using npx is through the cowsay command. cowsay will print a cow saying what you wrote in the command. 例如:

cowsay "Hello" will print

cowsay "Hello" will print

_______< Hello > -------        \   ^__^         \  (oo)\_______            (__)\       )\/\                ||----w |                ||     ||

Now, this if you have the cowsay command globally installed from npm previously, otherwise you’ll get an error when you try to run the command.

Now, this if you have the cowsay command globally installed from npm previously, otherwise you'll get an error when you try to run the command.

npx allows you to run that npm command without having it installed locally:

npx allows you to run that npm command without having it installed locally:

npx cowsay "Hello"

Now, this is a funny useless command. Other scenarios include:

Now, this is a funny useless command. Other scenarios include:

  • running the vue CLI tool to create new applications and run them: npx vue create my-vue-app

    running the vue CLI tool to create new applications and run them: npx vue create my-vue-app

  • creating a new React app using create-react-app: npx create-react-app my-react-app

    creating a new React app using create-react-app : npx create-react-app my-react-app

and many more.

还有很多。

Once downloaded, the downloaded code will be wiped.

Once downloaded, the downloaded code will be wiped.

Run some code using a different Node.js version (Run some code using a different Node.js version)

Use the @ to specify the version, and combine that with the node npm package:

Use the @ to specify the version, and combine that with the node npm package:

npx node@6 -v #v6.14.3npx node@8 -v #v8.11.3

This helps to avoid tools like nvm or the other Node version management tools.

This helps to avoid tools like nvm or the other Node version management tools.

Run arbitrary code snippets directly from a URL (Run arbitrary code snippets directly from a URL)

npx does not limit you to the packages published on the npm registry.

npx does not limit you to the packages published on the npm registry.

You can run code that sits in a GitHub gist, for example:

You can run code that sits in a GitHub gist, for example:

npx https://gist.github.com/zkat/4bc19503fe9e9309e2bfaa2c58074d32

Of course, you need to be careful when running code that you do not control, as with great power comes great responsibility.

Of course, you need to be careful when running code that you do not control, as with great power comes great responsibility.

事件循环 (The Event Loop)

The Event Loop is one of the most important aspects to understand about JavaScript. This section explains the inner details of how JavaScript works with a single thread, and how it handles asynchronous functions.

The Event Loop is one of the most important aspects to understand about JavaScript. This section explains the inner details of how JavaScript works with a single thread, and how it handles asynchronous functions.

I’ve programmed for years with JavaScript, yet I’ve never fully understood how things work under the hoods. It’s completely fine to not know this concept in detail. But as usual, it’s helpful to know how it works, and also you might just be a little curious at this point.

I've programmed for years with JavaScript, yet I've never fully understood how things work under the hoods. It's completely fine to not know this concept in detail. But as usual, it's helpful to know how it works, and also you might just be a little curious at this point.

Your JavaScript code runs single threaded. There is just one thing happening at a time.

Your JavaScript code runs single threaded. There is just one thing happening at a time.

This is a limitation that’s actually very helpful, as it simplifies a lot of how you program without worrying about concurrency issues.

This is a limitation that's actually very helpful, as it simplifies a lot of how you program without worrying about concurrency issues.

You just need to pay attention to how you write your code and avoid anything that could block the thread, like synchronous network calls or infinite loops.

You just need to pay attention to how you write your code and avoid anything that could block the thread, like synchronous network calls or infinite loops .

Generally, in most browsers there is an event loop for every browser tab, to make every process isolated and avoid a web page with infinite loops or heavy processing to block your entire browser.

Generally, in most browsers there is an event loop for every browser tab, to make every process isolated and avoid a web page with infinite loops or heavy processing to block your entire browser.

The environment manages multiple concurrent event loops, to handle API calls for example. Web Workers run in their own event loop as well.

The environment manages multiple concurrent event loops, to handle API calls for example. Web Workers run in their own event loop as well.

You mainly need to be concerned that your code will run on a single event loop, and write code with this thing in mind to avoid blocking it.

You mainly need to be concerned that your code will run on a single event loop, and write code with this thing in mind to avoid blocking it.

Blocking the event loop (Blocking the event loop)

Any JavaScript code that takes too long to return back control to the event loop will block the execution of any JavaScript code in the page — even block the UI thread — and the user cannot click around, scroll the page, and so on.

Any JavaScript code that takes too long to return back control to the event loop will block the execution of any JavaScript code in the page — even block the UI thread — and the user cannot click around, scroll the page, and so on.

Almost all the I/O primitives in JavaScript are non-blocking. Network requests, Node.js file system operations, and so on. Being blocking is the exception, and this is why JavaScript is based so much on callbacks, and more recently on promises and async/await.

Almost all the I/O primitives in JavaScript are non-blocking. Network requests, Node.js file system operations, and so on. Being blocking is the exception, and this is why JavaScript is based so much on callbacks, and more recently on promises and async/await.

调用栈 (The call stack)

The call stack is a LIFO queue (Last In, First Out).

The call stack is a LIFO queue (Last In, First Out).

The event loop continuously checks the call stack to see if there’s any function that needs to run.

The event loop continuously checks the call stack to see if there's any function that needs to run.

While doing so, it adds any function call it finds to the call stack and executes each one in order.

While doing so, it adds any function call it finds to the call stack and executes each one in order.

You know the error stack trace you might be familiar with, in the debugger or in the browser console?

You know the error stack trace you might be familiar with, in the debugger or in the browser console?

The browser looks up the function names in the call stack to inform you which function originates the current call:

The browser looks up the function names in the call stack to inform you which function originates the current call:

A simple event loop explanation (A simple event loop explanation)

Let’s pick an example:

Let's pick an example:

const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {  console.log('foo')  bar()  baz()}
foo()

This code prints:

This code prints:

foobarbaz

as expected.

as expected.

When this code runs, first foo() is called. Inside foo() we first call bar(), then we call baz().

When this code runs, first foo() is called. Inside foo() we first call bar() , then we call baz() .

At this point the call stack looks like this:

At this point the call stack looks like this:

The event loop on every iteration looks if there’s something in the call stack, and executes it:

The event loop on every iteration looks if there's something in the call stack, and executes it:

until the call stack is empty.

until the call stack is empty.

Queuing function execution (Queuing function execution)

The above example looks normal, there’s nothing special about it: JavaScript finds things to execute, runs them in order.

The above example looks normal, there's nothing special about it: JavaScript finds things to execute, runs them in order.

Let’s see how to defer a function until the stack is clear.

Let's see how to defer a function until the stack is clear.

The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other function in the code has executed.

The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other function in the code has executed.

Take this example:

举个例子:

const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {  console.log('foo')  setTimeout(bar, 0)  baz()}
foo()

This code prints, maybe surprisingly:

This code prints, maybe surprisingly:

foobazbar

When this code runs, first foo() is called. Inside foo() we first call setTimeout, passing bar as an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer. Then we call baz().

When this code runs, first foo() is called. Inside foo() we first call setTimeout , passing bar as an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer. Then we call baz() .

At this point the call stack looks like this:

At this point the call stack looks like this:

Here is the execution order for all the functions in our program:

Here is the execution order for all the functions in our program:

Why is this happening?

Why is this happening?

The Message Queue (The Message Queue)

When setTimeout() is called, the Browser or Node.js starts the timer. Once the timer expires, in this case immediately as we put 0 as the timeout, the callback function is put in the Message Queue.

When setTimeout() is called, the Browser or Node.js starts the timer. Once the timer expires, in this case immediately as we put 0 as the timeout, the callback function is put in the Message Queue .

The Message Queue is also where user-initiated events like click and keyboard events or fetch responses are queued before your code has the opportunity to react to them. Or also DOM events like onLoad.

The Message Queue is also where user-initiated events like click and keyboard events or fetch responses are queued before your code has the opportunity to react to them. Or also DOM events like onLoad .

The loop gives priority to the call stack. It first processes everything it finds in the call stack, and once there’s nothing in there, it goes to pick up things in the message queue.

The loop gives priority to the call stack. It first processes everything it finds in the call stack, and once there's nothing in there, it goes to pick up things in the message queue.

We don’t have to wait for functions like setTimeout, fetch or other things to do their own work, because they are provided by the browser, and they live on their own threads. For example, if you set the setTimeout timeout to 2 seconds, you don’t have to wait 2 seconds - the wait happens elsewhere.

We don't have to wait for functions like setTimeout , fetch or other things to do their own work, because they are provided by the browser, and they live on their own threads. For example, if you set the setTimeout timeout to 2 seconds, you don't have to wait 2 seconds - the wait happens elsewhere.

ES6 Job Queue (ES6 Job Queue)

ECMAScript 2015 introduced the concept of the Job Queue, which is used by Promises (also introduced in ES6/ES2015). It’s a way to execute the result of an async function as soon as possible, rather than being put at the end of the call stack.

ECMAScript 2015 introduced the concept of the Job Queue, which is used by Promises (also introduced in ES6/ES2015). It's a way to execute the result of an async function as soon as possible, rather than being put at the end of the call stack.

Promises that resolve before the current function ends will be executed right after the current function.

Promises that resolve before the current function ends will be executed right after the current function.

I find nice the analogy of a rollercoaster ride at an amusement park: the message queue puts you back in queue with after all the other people in the queue, while the job queue is the fastpass ticket that lets you take another ride right after you finished the previous one.

I find nice the analogy of a rollercoaster ride at an amusement park: the message queue puts you back in queue with after all the other people in the queue, while the job queue is the fastpass ticket that lets you take another ride right after you finished the previous one.

Example:

例:

const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {  console.log('foo')  setTimeout(bar, 0)  new Promise((resolve, reject) =>    resolve('should be right after baz, before bar')  ).then(resolve => console.log(resolve))  baz()}
foo()

This prints:

打印:

foobazshould be right after foo, before barbar

That’s a big difference between Promises (and async/await, which is built on promises) and plain old asynchronous functions through setTimeout() or other platform APIs.

That's a big difference between Promises (and async/await , which is built on promises) and plain old asynchronous functions through setTimeout() or other platform APIs.

Understanding process.nextTick() (Understanding process.nextTick())

As you try to understand the Node.js event loop, one important part of it is process.nextTick(). It interacts with the event loop in a special way.

As you try to understand the Node.js event loop, one important part of it is process.nextTick() . It interacts with the event loop in a special way.

Every time the event loop takes a full trip, we call it a tick.

Every time the event loop takes a full trip, we call it a tick.

When we pass a function to process.nextTick(), we instruct the engine to invoke this function at the end of the current operation, before the next event loop tick starts:

When we pass a function to process.nextTick() , we instruct the engine to invoke this function at the end of the current operation, before the next event loop tick starts:

process.nextTick(() => {  //do something})

The event loop is busy processing the current function code.

The event loop is busy processing the current function code.

When this operation ends, the JavaScript engine runs all the functions passed to nextTick calls during that operation.

When this operation ends, the JavaScript engine runs all the functions passed to nextTick calls during that operation.

It’s the way we can tell the JavaScript engine to process a function asynchronously (after the current function), but as soon as possible, not queue it.

It's the way we can tell the JavaScript engine to process a function asynchronously (after the current function), but as soon as possible, not queue it.

Calling setTimeout(() => {}, 0) will execute the function in the next tick, much later than when using nextTick().

Calling setTimeout(() => {}, 0) will execute the function in the next tick, much later than when usi ng nextTic k().

Use nextTick() when you want to make sure that in the next event loop iteration that code is already executed.

Use nextTick() when you want to make sure that in the next event loop iteration that code is already executed.

Understanding setImmediate() (Understanding setImmediate())

When you want to execute some piece of code asynchronously, but as soon as possible, one option is to use the setImmediate() function provided by Node.js:

When you want to execute some piece of code asynchronously, but as soon as possible, one option is to use the setImmediate() function provided by Node.js:

setImmediate(() => {  //run something})

Any function passed as the setImmediate() argument is a callback that’s executed in the next iteration of the event loop.

Any function passed as the setImmediate() argument is a callback that's executed in the next iteration of the event loop.

How is setImmediate() different from setTimeout(() => {}, 0) (passing a 0ms timeout), and from process.nextTick()?

How is setImmediate() different from setTimeout(() => {}, 0) (passing a 0ms timeout), and fr om process.nextTic k()?

A function passed to process.nextTick() is going to be executed on the current iteration of the event loop, after the current operation ends. This means it will always execute before setTimeout() and setImmediate().

A function passed to process.nextTick() is going to be executed on the current iteration of the event loop, after the current operation ends. This means it will always execute before setTimeout() and setImmediate() .

A setTimeout() callback with a 0ms delay is very similar to setImmediate(). The execution order will depend on various factors, but they will be both run in the next iteration of the event loop.

A setTimeout() callback with a 0ms delay is very similar to setImmediate() . The execution order will depend on various factors, but they will be both run in the next iteration of the event loop.

计时器 (Timers)

When writing JavaScript code, you might want to delay the execution of a function. Learn how to use setTimeout()and setInterval() to schedule functions in the future.

When writing JavaScript code, you might want to delay the execution of a function. Learn how to use setTimeout() and setInterval() to schedule functions in the future.

setTimeout() (setTimeout())

When writing JavaScript code, you might want to delay the execution of a function. This is the job of setTimeout.

When writing JavaScript code, you might want to delay the execution of a function. This is the job of setTimeout .

You can specify a callback function to execute later, and a value expressing how much later you want it to run, in milliseconds:

You can specify a callback function to execute later, and a value expressing how much later you want it to run, in milliseconds:

setTimeout(() => {  // runs after 2 seconds}, 2000)
setTimeout(() => {  // runs after 50 milliseconds}, 50)

This syntax defines a new function. You can call whatever other function you want in there, or you can pass an existing function name, and a set of parameters:

This syntax defines a new function. You can call whatever other function you want in there, or you can pass an existing function name, and a set of parameters:

const myFunction = (firstParam, secondParam) => {  // do something}
// runs after 2 secondssetTimeout(myFunction, 2000, firstParam, secondParam)

setTimeout() returns the timer id. This is generally not used, but you can store this id, and clear it if you want to delete this scheduled function execution:

setTimeout() returns the timer id. This is generally not used, but you can store this id, and clear it if you want to delete this scheduled function execution:

const id = setTimeout(() => {  // should run after 2 seconds}, 2000)
// I changed my mindclearTimeout(id)
Zero delay (Zero delay)

If you specify the timeout delay to 0, the callback function will be executed as soon as possible, but after the current function execution:

If you specify the timeout delay to 0 , the callback function will be executed as soon as possible, but after the current function execution:

setTimeout(() => {  console.log('after ')}, 0)
console.log(' before ')

will print before after.

will print before after .

This is especially useful to avoid blocking the CPU on intensive tasks and let other functions be executed while performing a heavy calculation, by queuing functions in the scheduler.

This is especially useful to avoid blocking the CPU on intensive tasks and let other functions be executed while performing a heavy calculation, by queuing functions in the scheduler.

Some browsers (IE and Edge) implement a setImmediate() method that does this same exact functionality, but it’s not standard and unavailable on other browsers. But it’s a standard function in Node.js.

Some browsers (IE and Edge) implement a setImmediate() method that does this same exact functionality, but it's not standard and unavailable on other browsers . But it's a standard function in Node.js.

setInterval() (setInterval())

setInterval() is a function similar to setTimeout() with a difference. Instead of running the callback function once, it will run it forever, at the specific time interval you specify (in milliseconds):

setInterval() is a function similar to setTimeout() with a difference. Instead of running the callback function once, it will run it forever, at the specific time interval you specify (in milliseconds):

setInterval(() => {  // runs every 2 seconds}, 2000)

The function above runs every 2 seconds unless you tell it to stop, using clearInterval, passing it the interval id that setInterval returned:

The function above runs every 2 seconds unless you tell it to stop, using clearInterval , passing it the interval id that setInterval returned:

const id = setInterval(() => {  // runs every 2 seconds}, 2000)
clearInterval(id)

It’s common to call clearInterval inside the setInterval callback function, to let it auto-determine if it should run again or stop. For example this code runs something unless App.somethingIWait has the value arrived:

It's common to call clearInterval inside the setInterval callback function, to let it auto-determine if it should run again or stop. For example this code runs something unless App.somethingIWait has the value arrived :

const interval = setInterval(function() {  if (App.somethingIWait === 'arrived') {    clearInterval(interval)
// otherwise do things  }}, 100)
Recursive setTimeout (Recursive setTimeout)

setInterval starts a function every n milliseconds, without any consideration about when a function finished its execution.

setInterval starts a function every n milliseconds, without any consideration about when a function finished its execution.

If a function takes always the same amount of time, it’s all fine:

If a function takes always the same amount of time, it's all fine:

Maybe the function takes different execution times, depending on network conditions for example:

Maybe the function takes different execution times, depending on network conditions for example:

And maybe one long execution overlaps the next one:

And maybe one long execution overlaps the next one:

To avoid this, you can schedule a recursive setTimeout to be called when the callback function finishes:

To avoid this, you can schedule a recursive setTimeout to be called when the callback function finishes:

const myFunction = () => {  // do something
setTimeout(myFunction, 1000)}
setTimeout(  myFunction()}, 1000)

to achieve this scenario:

to achieve this scenario:

setTimeout and setInterval are also available in Node.js, through the Timers module.

setTimeout and setInterval are also available in Node.js, through the Timers module .

Node.js also provides setImmediate(), which is equivalent to using setTimeout(() => {}, 0), mostly used to work with the Node.js Event Loop.

Node.js also provides setImmediate() , which is equivalent to using setTimeout(() => {}, 0), mostly used to work with the Node.js Event Loop.

异步编程和回调 (Asynchronous Programming and Callbacks)

JavaScript is synchronous by default, and is single threaded. This means that code cannot create new threads and run in parallel.

JavaScript is synchronous by default, and is single threaded. 这意味着代码无法创建新线程并不能并行运行。

Asynchronicity in Programming Languages (Asynchronicity in Programming Languages)

Computers are asynchronous by design.

计算机在设计上是异步的。

Asynchronous means that things can happen independently of the main program flow.

异步意味着事情可以独立于主程序流发生。

In the current consumer computers, every program runs for a specific time slot, and then it stops its execution to let another program continue its execution. This thing runs in a cycle so fast that’s impossible to notice, and we think our computers run many programs simultaneously, but this is an illusion (except on multiprocessor machines).

在当前的消费计算机中,每个程序都运行特定的时间段,然后停止执行,让另一个程序继续执行。 这件事的运行周期如此之快,以至于无法察觉,我们认为我们的计算机可以同时运行许多程序,但这只是一种幻想(在多处理器计算机上除外)。

Programs internally use interrupts, a signal that’s emitted to the processor to gain the attention of the system.

程序在内部使用中断 ,该信号被发送到处理器以引起系统的注意。

I won’t go into the internals of this, but just keep in mind that it’s normal for programs to be asynchronous, and halt their execution until they need attention, and the computer can execute other things in the meantime. When a program is waiting for a response from the network, it cannot halt the processor until the request finishes.

我不会深入探讨这一点,只是要记住,异步执行程序是正常的,并且在需要注意之前停止执行,并且计算机可以同时执行其他操作。 当程序正在等待来自网络的响应时,它无法在请求完成之前停止处理器。

Normally, programming languages are synchronous, and some provide a way to manage asynchronicity, in the language or through libraries. C, Java, C#, PHP, Go, Ruby, Swift, Python, they are all synchronous by default. Some of them handle asynchronicity by using threads, spawning a new process.

通常,编程语言是同步的,并且某些编程语言提供了一种通过语言或通过库来管理异步性的方法。 C,Java,C#,PHP,Go,Ruby,Swift,Python,它们在默认情况下都是同步的。 Some of them handle asynchronicity by using threads, spawning a new process.

JavaScript (JavaScript)

JavaScript is synchronous by default and is single threaded. This means that code cannot create new threads and run in parallel.

JavaScript默认情况下是同步的 ,并且是单线程的。 这意味着代码无法创建新线程并不能并行运行。

Lines of code are executed in series, one after another.

Lines of code are executed in series, one after another.

For example:

例如:

const a = 1const b = 2const c = a * bconsole.log(c)doSomething()

But JavaScript was born inside the browser. Its main job, in the beginning, was to respond to user actions like onClick, onMouseOver, onChange, onSubmit and so on. How could it do this with a synchronous programming model?

But JavaScript was born inside the browser. Its main job, in the beginning, was to respond to user actions like onClick , onMouseOver , onChange , onSubmit and so on. 如何使用同步编程模型来做到这一点?

The answer was in its environment. The browser provides a way to do it by providing a set of APIs that can handle this kind of functionality.

答案在于它的环境。 浏览器通过提供一组可以处理这种功能的API提供了一种实现方法。

More recently, Node.js introduced a non-blocking I/O environment to extend this concept to file access, network calls and so on.

最近,Node.js引入了非阻塞I / O环境,以将该概念扩展到文件访问,网络调用等。

回呼 (Callbacks)

You can’t know when a user is going to click a button, so what you do is define an event handler for the click event.

You can't know when a user is going to click a button, so what you do is define an event handler for the click event .

This event handler accepts a function, which will be called when the event is triggered:

该事件处理程序接受一个函数,该函数将在触发事件时被调用:

document.getElementById('button').addEventListener('click', () => {  //item clicked})

This is the so-called callback.

这就是所谓的callback

A callback is a simple function that’s passed as a value to another function, and will only be executed when the event happens. We can do this because JavaScript has first-class functions, which can be assigned to variables and passed around to other functions (called higher-order functions)

回调是一个简单的函数,将其作为值传递给另一个函数,并且仅在事件发生时才执行。 我们之所以这样做,是因为JavaScript具有一流的函数,这些函数可以分配给变量并传递给其他函数(称为高阶函数 )。

It’s common to wrap all your client code in a load event listener on the window object, which runs the callback function only when the page is ready:

通常将所有客户端代码包装在window对象的load事件监听器中,该对象仅在页面准备就绪时才运行回调函数:

window.addEventListener('load', () => {  //window loaded  //do what you want})

Callbacks are used everywhere, not just in DOM events.

回调无处不在,不仅在DOM事件中使用。

One common example is by using timers:

一个常见的示例是使用计时器:

setTimeout(() => {  // runs after 2 seconds}, 2000)

XHR requests also accept a callback, in this example by assigning a function to a property that will be called when a particular event occurs (in this case, the state of the request changes):

XHR requests also accept a callback, in this example by assigning a function to a property that will be called when a particular event occurs (in this case, the state of the request changes):

const xhr = new XMLHttpRequest()xhr.onreadystatechange = () => {  if (xhr.readyState === 4) {    xhr.status === 200 ? console.log(xhr.responseText) : console.error('error')  }}xhr.open('GET', 'https://yoursite.com')xhr.send()
处理回调中的错误 (Handling errors in callbacks)

How do you handle errors with callbacks? One very common strategy is to use what Node.js adopted: the first parameter in any callback function is the error object — error-first callbacks.

您如何处理回调错误? One very common strategy is to use what Node.js adopted: the first parameter in any callback function is the error object — error-first callbacks.

If there is no error, the object is null. If there is an error, it contains some description of the error and other information.

如果没有错误,则该对象为null 。 如果有错误,它将包含对该错误的一些描述以及其他信息。

fs.readFile('/file.json', (err, data) => {  if (err !== null) {    //handle error    console.log(err)    return  }
//no errors, process data  console.log(data)})
回调的问题 (The problem with callbacks)

Callbacks are great for simple cases!

回调适用于简单情况!

However, every callback adds a level of nesting. When you have lots of callbacks, the code starts to be complicated very quickly:

However, every callback adds a level of nesting. When you have lots of callbacks, the code starts to be complicated very quickly:

window.addEventListener('load', () => {  document.getElementById('button').addEventListener('click', () => {    setTimeout(() => {      items.forEach(item => {        //your code here      })    }, 2000)  })})

This is just a simple 4-levels code, but I’ve seen much more levels of nesting and it’s not fun.

这只是一个简单的4级代码,但是我看到了更多的嵌套级别,这很不好玩。

How do we solve this?

我们该如何解决呢?

Alternatives to callbacks (Alternatives to callbacks)

Starting with ES6, JavaScript introduced several features that help us with asynchronous code that do not involve using callbacks:

从ES6开始,JavaScript引入了一些功能,这些功能可以帮助我们处理不涉及使用回调的异步代码:

  • Promises (ES6)

    承诺(ES6)
  • Async/Await (ES8)

    异步/等待(ES8)

承诺 (Promises)

Promises are one way to deal with asynchronous code in JavaScript, without writing too many callbacks in your code.

Promises are one way to deal with asynchronous code in JavaScript, without writing too many callbacks in your code.

Introduction to promises (Introduction to promises)

A promise is commonly defined as a proxy for a value that will eventually become available.

通常将promise定义为最终将变为可用值的代理

Although being around for years, they have been standardized and introduced in ES2015, and now they have been superseded in ES2017 by async functions.

Although being around for years, they have been standardized and introduced in ES2015, and now they have been superseded in ES2017 by async functions.

Async functions use the promises API as their building block, so understanding them is fundamental even if in newer code you’ll likely use async functions instead of promises.

异步函数将promise API用作其构建块,因此即使在较新的代码中您可能会使用异步函数代替promise,对它们的理解也是基础。

简而言之,诺言如何运作 (How promises work, in brief)

Once a promise has been called, it will start in pending state. This means that the caller function continues the execution, while it waits for the promise to do its own processing, and give the caller function some feedback.

承诺被调用后,它将以待处理状态开始。 这意味着调用方函数继续执行,同时等待promise进行自己的处理,并向调用方函数提供一些反馈。

At this point, the caller function waits for it to either return the promise in a resolved state, or in a rejected state, but as you know JavaScript is asynchronous — so the function continues its execution while the promise does it work.

At this point, the caller function waits for it to either return the promise in a resolved state , or in a rejected state , but as you know JavaScript is asynchronous — so the function continues its execution while the promise does it work.

哪个JS API使用承诺? (Which JS API use promises?)

In addition to your own code and libraries code, promises are used by standard modern Web APIs such as:

In addition to your own code and libraries code, promises are used by standard modern Web APIs such as:

It’s unlikely that in modern JavaScript you’ll find yourself not using promises, so let’s start diving right into them.

在现代JavaScript中,您不太可能会发现自己没有使用Promise,因此让我们开始深入研究它们。

创造承诺 (Creating a promise)

The Promise API exposes a Promise constructor, which you initialize using new Promise():

Promise API公开了一个Promise构造函数,您可以使用new Promise()初始化:

let done = true
const isItDoneYet = new Promise(  (resolve, reject) => {    if (done) {      const workDone = 'Here is the thing I built'      resolve(workDone)    } else {      const why = 'Still working on something else'      reject(why)    }  })

As you can see the promise checks the done global constant, and if that’s true, we return a resolved promise, otherwise a rejected promise.

如您所见,promise检查已done全局常量,如果是这样,我们将返回已解决的Promise,否则将返回被拒绝的Promise。

Using resolve and reject we can communicate back a value, in the above case we just return a string, but it could be an object as well.

使用resolvereject我们可以返回一个值,在上述情况下,我们只返回一个字符串,但是它也可以是一个对象。

兑现承诺 (Consuming a promise)

In the last section, we introduced how a promise is created.

在上一节中,我们介绍了如何创建承诺。

Now let’s see how the promise can be consumed or used:

Now let's see how the promise can be consumed or used:

const isItDoneYet = new Promise(  //...)
const checkIfItsDone = () => {  isItDoneYet    .then((ok) => {      console.log(ok)    })    .catch((err) => {      console.error(err)    })}

Running checkIfItsDone() will execute the isItDoneYet() promise and will wait for it to resolve, using the then callback, and if there is an error, it will handle it in the catch callback.

运行checkIfItsDone()将执行isItDoneYet() ,并使用then回调等待其解决,如果有错误,它将在catch回调中对其进行处理。

连锁承诺 (Chaining promises)

A promise can be returned to another promise, creating a chain of promises.

一个承诺可以返回到另一个承诺,从而创建一个承诺链。

A great example of chaining promises is given by the Fetch API, a layer on top of the XMLHttpRequest API, which we can use to get a resource and queue a chain of promises to execute when the resource is fetched.

A great example of chaining promises is given by the Fetch API , a layer on top of the XMLHttpRequest API, which we can use to get a resource and queue a chain of promises to execute when the resource is fetched.

The Fetch API is a promise-based mechanism, and calling fetch() is equivalent to defining our own promise using new Promise().

Fetch API是一种基于承诺的机制,调用fetch()等效于使用new Promise()定义我们自己的承诺。

链接承诺的示例 (Example of chaining promises)
const status = (response) => {  if (response.status >= 200 && response.status < 300) {    return Promise.resolve(response)  }  return Promise.reject(new Error(response.statusText))}
const json = (response) => response.json()
fetch('/todos.json')  .then(status)  .then(json)  .then((data) => { console.log('Request succeeded with JSON response', data) })  .catch((error) => { console.log('Request failed', error) })

In this example, we call fetch() to get a list of TODO items from the todos.json file found in the domain root, and we create a chain of promises.

在此示例中,我们调用fetch()从域根目录中的todos.json文件中获取TODO项目列表,并创建一个Promise链。

Running fetch() returns a response, which has many properties, and within those we reference:

运行fetch()返回一个response ,它具有许多属性,在我们引用的属性内:

  • status, a numeric value representing the HTTP status code

    status ,一个表示HTTP状态代码的数值

  • statusText, a status message, which is OK if the request succeeded

    statusText ,状态消息,如果请求成功,则OK

response also has a json() method, which returns a promise that will resolve with the content of the body processed and transformed into JSON.

response也有一个json()方法,该方法返回一个promise,该promise将与处理并转换为JSON的正文内容一起解析。

So given those premises, this is what happens: the first promise in the chain is a function that we defined, called status(), that checks the response status and if it’s not a success response (between 200 and 299), it rejects the promise.

因此,考虑到这些前提,就会发生这种情况:链中的第一个promise是我们定义的函数,即status() ,它检查响应状态,如果不是成功响应(介于200和299之间),它将拒绝诺言。

This operation will cause the promise chain to skip all the chained promises listed and will skip directly to the catch() statement at the bottom, logging the Request failed text along with the error message.

此操作将导致promise链跳过列出的所有链接的promise,并将直接跳到底部的catch()语句,并记录Request failed文本和错误消息。

If that succeeds instead, it calls the json() function we defined. Since the previous promise, when successful, returned the response object, we get it as an input to the second promise.

如果成功,它将调用我们定义的json()函数。 由于上一个承诺成功后返回了response对象,因此我们将其作为第二个承诺的输入。

In this case, we return the data JSON processed, so the third promise receives the JSON directly:

在这种情况下,我们返回经过JSON处理的数据,因此第三个promise直接接收JSON:

.then((data) => {  console.log('Request succeeded with JSON response', data)})

and we simply log it to the console.

我们只需将其记录到控制台即可。

处理错误 (Handling errors)

In the example, in the previous section, we had a catch that was appended to the chain of promises.

In the example, in the previous section, we had a catch that was appended to the chain of promises.

When anything in the chain of promises fails and raises an error or rejects the promise, the control goes to the nearest catch() statement down the chain.

当promise链中的任何内容失败并且引发错误或拒绝promise时,控件将转到链中最近的catch()语句。

new Promise((resolve, reject) => {  throw new Error('Error')})  .catch((err) => { console.error(err) })
// or
new Promise((resolve, reject) => {  reject('Error')})  .catch((err) => { console.error(err) })
级联错误 (Cascading errors)

If inside the catch() you raise an error, you can append a second catch() to handle it, and so on.

如果在catch()内部引发错误,则可以附加第二个catch()来处理它,依此类推。

new Promise((resolve, reject) => {  throw new Error('Error')})  .catch((err) => { throw new Error('Error') })  .catch((err) => { console.error(err) })

编排承诺 (Orchestrating promises)

Promise.all() (Promise.all())

If you need to synchronize different promises, Promise.all() helps you define a list of promises, and execute something when they are all resolved.

如果您需要同步不同的Promise.all()Promise.all()可帮助您定义一个Promise.all()列表,并在它们全部解决后执行一些操作。

Example:

例:

const f1 = fetch('/something.json')const f2 = fetch('/something2.json')
Promise.all([f1, f2]).then((res) => {    console.log('Array of results', res)}).catch((err) => {  console.error(err)})

The ES2015 destructuring assignment syntax allows you to also do:

The ES2015 destructuring assignment syntax allows you to also do:

Promise.all([f1, f2]).then(([res1, res2]) => {    console.log('Results', res1, res2)})

You are not limited to using fetch of course, any promise is good to go.

当然,您不仅限于使用fetch任何承诺都是可以的

Promise.race() (Promise.race())

Promise.race() runs when the first of the promises you pass to it resolves, and it runs the attached callback just once, with the result of the first promise resolved.

Promise.race() runs when the first of the promises you pass to it resolves, and it runs the attached callback just once, with the result of the first promise resolved.

Example:

例:

const first = new Promise((resolve, reject) => {    setTimeout(resolve, 500, 'first')})const second = new Promise((resolve, reject) => {    setTimeout(resolve, 100, 'second')})
Promise.race([first, second]).then((result) => {  console.log(result) // second})
Common error, Uncaught TypeError: undefined is not a promise (Common error, Uncaught TypeError: undefined is not a promise)

If you get the Uncaught TypeError: undefined is not a promise error in the console, make sure you use new Promise() instead of just Promise().

If you get the Uncaught TypeError: undefined is not a promise error in the console, make sure you use new Promise() instead of just Promise() .

异步并等待 (Async and Await)

Discover the modern approach to asynchronous functions in JavaScript.

Discover the modern approach to asynchronous functions in JavaScript.

JavaScript evolved in a very short time from callbacks to promises (ES2015), and since ES2017 asynchronous JavaScript is even simpler with the async/await syntax.

JavaScript在很短的时间内从回调发展到了承诺(ES2015),并且由于ES2017,异步JavaScript使用async / await语法更加简单。

Async functions are a combination of promises and generators, and basically, they are a higher level abstraction over promises. Let me repeat: async/await is built on promises.

异步函数是promise和生成器的组合,基本上,它们是promise的更高级别的抽象。 Let me repeat: async/await is built on promises.

为什么引入异步/等待? (Why were async/await introduced?)

They reduce the boilerplate around promises, and the “don’t break the chain” limitation of chaining promises.

它们减少了兑现承诺的样板,并降低了兑现承诺的“不破坏链条”的局限性。

When Promises were introduced in ES2015, they were meant to solve a problem with asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it was clear that promises could not be the final solution.

When Promises were introduced in ES2015, they were meant to solve a problem with asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it was clear that promises could not be the final solution.

Promises were introduced to solve the famous callback hell problem, but they introduced complexity on their own, and syntax complexity.

引入了承诺来解决著名的回调地狱问题,但是它们自己引入了复杂性以及语法复杂性。

They were good primitives around which a better syntax could be exposed to the developers, so when the time was right we got async functions.

They were good primitives around which a better syntax could be exposed to the developers, so when the time was right we got async functions .

They make the code look like it’s synchronous, but it’s asynchronous and non-blocking behind the scenes.

它们使代码看起来像是同步的,但在后台却是异步且无阻塞的。

这个怎么运作 (How it works)

An async function returns a promise, like in this example:

An async function returns a promise, like in this example:

const doSomethingAsync = () => {    return new Promise((resolve) => {        setTimeout(() => resolve('I did something'), 3000)    })}

When you want to call this function you prepend await, and the calling code will stop until the promise is resolved or rejected. One caveat: the client function must be defined as async.

When you want to call this function you prepend await , and the calling code will stop until the promise is resolved or rejected . 一个警告:客户端功能必须定义为async

Here’s an example:

这是一个例子:

const doSomething = async () => {    console.log(await doSomethingAsync())}
一个简单的例子 (A quick example)

This is a simple example of async/await used to run a function asynchronously:

This is a simple example of async/await used to run a function asynchronously:

const doSomethingAsync = () => {    return new Promise((resolve) => {        setTimeout(() => resolve('I did something'), 3000)    })}
const doSomething = async () => {    console.log(await doSomethingAsync())}
console.log('Before')doSomething()console.log('After')

The above code will print the following to the browser console:

上面的代码会将以下内容打印到浏览器控制台:

BeforeAfterI did something //after 3s
承诺一切 (Promise all the things)

Prepending the async keyword to any function means that the function will return a promise.

在任何函数之前加上async关键字意味着该函数将返回一个Promise。

Even if it’s not doing so explicitly, it will internally make it return a promise.

即使没有明确地这样做,它也会在内部使它返回承诺。

This is why this code is valid:

这就是为什么此代码有效的原因:

const aFunction = async () => {  return 'test'}
aFunction().then(alert) // This will alert 'test'

and it’s the same as:

它与:

const aFunction = async () => {  return Promise.resolve('test')}
aFunction().then(alert) // This will alert 'test'
该代码更容易阅读 (The code is much simpler to read)

As you can see in the example above, our code looks very simple. Compare it to code using plain promises, with chaining and callback functions.

如您在上面的示例中看到的,我们的代码看起来非常简单。 将其与使用简单承诺,链接和回调函数的代码进行比较。

And this is a very simple example, the major benefits will arise when the code is much more complex.

这是一个非常简单的示例,当代码复杂得多时,将会带来主要的好处。

For example, here’s how you would get a JSON resource and parse it, using promises:

For example, here's how you would get a JSON resource and parse it, using promises:

const getFirstUserData = () => {  return fetch('/users.json') // get users list    .then(response => response.json()) // parse JSON    .then(users => users[0]) // pick first user    .then(user => fetch(`/users/${user.name}`)) // get user data    .then(userResponse => response.json()) // parse JSON}
getFirstUserData()

And here is the same functionality provided using await/async:

And here is the same functionality provided using await/async :

const getFirstUserData = async () => {  const response = await fetch('/users.json') // get users list  const users = await response.json() // parse JSON  const user = users[0] // pick first user  const userResponse = await fetch(`/users/${user.name}`) // get user data  const userData = await user.json() // parse JSON  return userData}
getFirstUserData()
多个异步功能串联 (Multiple async functions in series)

async functions can be chained very easily, and the syntax is much more readable than with plain promises:

async functions can be chained very easily, and the syntax is much more readable than with plain promises:

const promiseToDoSomething = () => {    return new Promise(resolve => {        setTimeout(() => resolve('I did something'), 10000)    })}
const watchOverSomeoneDoingSomething = async () => {    const something = await promiseToDoSomething()    return something + ' and I watched'}
const watchOverSomeoneWatchingSomeoneDoingSomething = async () => {    const something = await watchOverSomeoneDoingSomething()    return something + ' and I watched as well'}
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {    console.log(res)})

Will print:

将打印:

I did something and I watched and I watched as well
更容易调试 (Easier debugging)

Debugging promises is hard because the debugger will not step over asynchronous code.

调试承诺很难,因为调试器不会跳过异步代码。

async/await makes this very easy because to the compiler it’s just like synchronous code.

async/await makes this very easy because to the compiler it's just like synchronous code.

The Node.js Event Emitter (The Node.js Event Emitter)

You can work with custom events in Node.js.

You can work with custom events in Node.js.

If you worked with JavaScript in the browser, you know how much of the interaction of the user is handled through events: mouse clicks, keyboard button presses, reacting to mouse movements, and so on.

If you worked with JavaScript in the browser, you know how much of the interaction of the user is handled through events: mouse clicks, keyboard button presses, reacting to mouse movements, and so on.

On the back-end side, Node.js offers us the option to build a similar system using the eventsmodule.

On the back-end side, Node.js offers us the option to build a similar system using the events module .

This module, in particular, offers the EventEmitter class, which we’ll use to handle our events.

This module, in particular, offers the EventEmitter class, which we'll use to handle our events.

You initialize that using:

You initialize that using:

const eventEmitter = require('events').EventEmitter()

This object exposes, among many others, the on and emit methods.

This object exposes, among many others, the on and emit methods.

  • emit is used to trigger an event

    emit is used to trigger an event

  • on is used to add a callback function that’s going to be executed when the event is triggered

    on is used to add a callback function that's going to be executed when the event is triggered

For example, let’s create a start event, and as a matter of providing a sample, we react to that by just logging to the console:

For example, let's create a start event, and as a matter of providing a sample, we react to that by just logging to the console:

eventEmitter.on('start', () => {  console.log('started')})

When we run:

When we run:

eventEmitter.emit('start')

The event handler function is triggered, and we get the console log.

The event handler function is triggered, and we get the console log.

You can pass arguments to the event handler by passing them as additional arguments to emit():

You can pass arguments to the event handler by passing them as additional arguments to emit() :

eventEmitter.on('start', (number) => {  console.log(`started ${number}`)})
eventEmitter.emit('start', 23)

Multiple arguments:

Multiple arguments:

eventEmitter.on('start', (start, end) => {  console.log(`started from ${start} to ${end}`)})
eventEmitter.emit('start', 1, 100)

The EventEmitter object also exposes several other methods to interact with events, like:

The EventEmitter object also exposes several other methods to interact with events, like:

  • once(): add a one-time listener

    once() : add a one-time listener

  • removeListener() / off(): remove an event listener from an event

    removeListener() / off() : remove an event listener from an event

  • removeAllListeners(): remove all listeners for an event

    removeAllListeners() : remove all listeners for an event

HTTP请求如何工作 (How HTTP requests work)

What happens when you type an URL in the browser, from start to finish?

What happens when you type an URL in the browser, from start to finish?

This section describes how browsers perform page requests using the HTTP/1.1 protocol.

This section describes how browsers perform page requests using the HTTP/1.1 protocol.

If you ever did an interview, you might have been asked: “What happens when you type something into the Google search box and press enter?”.

If you ever did an interview, you might have been asked: “What happens when you type something into the Google search box and press enter?”.

It’s one of the most popular questions you get asked. People just want to see if you can explain some rather basic concepts and if you have any clue how the internet actually works.

It's one of the most popular questions you get asked. People just want to see if you can explain some rather basic concepts and if you have any clue how the internet actually works.

In this section, I’ll analyze what happens when you type an URL in the address bar of your browser and press enter.

In this section, I'll analyze what happens when you type an URL in the address bar of your browser and press enter.

It’s a very interesting topic to dissect in this handbook, as it touches many technologies I can dive into in separate articles.

It's a very interesting topic to dissect in this handbook, as it touches many technologies I can dive into in separate articles.

This is tech that is very rarely changed, and powers one the most complex and wide ecosystems ever built by humans.

This is tech that is very rarely changed, and powers one the most complex and wide ecosystems ever built by humans.

The HTTP protocol (The HTTP protocol)

I analyze URL requests only.

I analyze URL requests only.

Modern browsers have the capability of knowing if the thing you wrote in the address bar is an actual URL or a search term, and they will use the default search engine if it’s not a valid URL.

Modern browsers have the capability of knowing if the thing you wrote in the address bar is an actual URL or a search term, and they will use the default search engine if it's not a valid URL.

I assume you type an actual URL.

I assume you type an actual URL.

When you enter the URL and press enter, the browser first builds the full URL.

When you enter the URL and press enter, the browser first builds the full URL.

If you just entered a domain, like flaviocopes.com, the browser by default will prepend HTTP:// to it, defaulting to the HTTP protocol.

If you just entered a domain, like flaviocopes.com , the browser by default will prepend HTTP:// to it, defaulting to the HTTP protocol.

Things relate to macOS / Linux (Things relate to macOS / Linux)

Just FYI. Windows might do some things slightly differently.

Just FYI. Windows might do some things slightly differently.

DNS Lookup phase (DNS Lookup phase)

The browser starts the DNS lookup to get the server IP address.

The browser starts the DNS lookup to get the server IP address.

The domain name is a handy shortcut for us humans, but the internet is organized in such a way that computers can look up the exact location of a server through its IP address, which is a set of numbers like 222.324.3.1 (IPv4).

The domain name is a handy shortcut for us humans, but the internet is organized in such a way that computers can look up the exact location of a server through its IP address, which is a set of numbers like 222.324.3.1 (IPv4).

First, it checks the DNS local cache, to see if the domain has already been resolved recently.

First, it checks the DNS local cache, to see if the domain has already been resolved recently.

Chrome has a handy DNS cache visualizer you can see at this URL: chrome://net-internals/#dns (copy and paste it in the Chrome browser address bar)

Chrome has a handy DNS cache visualizer you can see at this URL: chrome://net-internals/#dns (copy and paste it in the Chrome browser address bar)

If nothing is found there, the browser uses the DNS resolver, using the gethostbyname POSIX system call to retrieve the host information.

If nothing is found there, the browser uses the DNS resolver, using the gethostbyname POSIX system call to retrieve the host information.

gethostbyname (gethostbyname)

gethostbyname first looks in the local hosts file, which on macOS or Linux is located in /etc/hosts, to see if the system provides the information locally.

gethostbyname first looks in the local hosts file, which on macOS or Linux is located in /etc/hosts , to see if the system provides the information locally.

If this does not give any information about the domain, the system makes a request to the DNS server.

If this does not give any information about the domain, the system makes a request to the DNS server.

The address of the DNS server is stored in the system preferences.

The address of the DNS server is stored in the system preferences.

Those are 2 popular DNS servers:

Those are 2 popular DNS servers:

  • 8.8.8.8: the Google public DNS server

    8.8.8.8 : the Google public DNS server

  • 1.1.1.1: the CloudFlare DNS server

    1.1.1.1 : the CloudFlare DNS server

Most people use the DNS server provided by their internet provider.

Most people use the DNS server provided by their internet provider.

The browser performs the DNS request using the UDP protocol.

The browser performs the DNS request using the UDP protocol.

TCP and UDP are two of the foundational protocols of computer networking. They sit at the same conceptual level, but TCP is connection-oriented, while UDP is a connectionless protocol, more lightweight, used to send messages with little overhead.

TCP and UDP are two of the foundational protocols of computer networking. They sit at the same conceptual level, but TCP is connection-oriented, while UDP is a connectionless protocol, more lightweight, used to send messages with little overhead.

How the UDP request is performed is not in the scope of this handbook.

How the UDP request is performed is not in the scope of this handbook.

The DNS server might have the domain IP in the cache. It not, it will ask the root DNS server. That’s a system (composed of 13 actual servers, distributed across the planet) that drives the entire internet.

The DNS server might have the domain IP in the cache. It not, it will ask the root DNS server . That's a system (composed of 13 actual servers, distributed across the planet) that drives the entire internet.

The DNS server does not know the address of each and every domain name on the planet.

The DNS server does not know the address of each and every domain name on the planet.

What it knows is where the top-level DNS resolvers are.

What it knows is where the top-level DNS resolvers are.

A top-level domain is the domain extension: .com, .it, .pizza and so on.

A top-level domain is the domain extension: .com , .it , .pizza and so on.

Once the root DNS server receives the request, it forwards the request to that top-level domain (TLD) DNS server.

Once the root DNS server receives the request, it forwards the request to that top-level domain (TLD) DNS server.

Say you are looking for flaviocopes.com. The root domain DNS server returns the IP of the .com TLD server.

Say you are looking for flaviocopes.com . The root domain DNS server returns the IP of the .com TLD server.

Now our DNS resolver will cache the IP of that TLD server, so it does not have to ask the root DNS server again for it.

Now our DNS resolver will cache the IP of that TLD server, so it does not have to ask the root DNS server again for it.

The TLD DNS server will have the IP addresses of the authoritative Name Servers for the domain we are looking for.

The TLD DNS server will have the IP addresses of the authoritative Name Servers for the domain we are looking for.

How? When you buy a domain, the domain registrar sends the appropriate TDL the name servers. When you update the name servers (for example, when you change the hosting provider), this information will be automatically updated by your domain registrar.

怎么样? When you buy a domain, the domain registrar sends the appropriate TDL the name servers. When you update the name servers (for example, when you change the hosting provider), this information will be automatically updated by your domain registrar.

Those are the DNS servers of the hosting provider. They are usually more than 1, to serve as backup.

Those are the DNS servers of the hosting provider. They are usually more than 1, to serve as backup.

For example:

例如:

  • ns1.dreamhost.com

    ns1.dreamhost.com

  • ns2.dreamhost.com

    ns2.dreamhost.com

  • ns3.dreamhost.com

    ns3.dreamhost.com

The DNS resolver starts with the first, and tries to ask the IP of the domain (with the subdomain, too) you are looking for.

The DNS resolver starts with the first, and tries to ask the IP of the domain (with the subdomain, too) you are looking for.

That is the ultimate source of truth for the IP address.

That is the ultimate source of truth for the IP address.

Now that we have the IP address, we can go on in our journey.

Now that we have the IP address, we can go on in our journey.

TCP request handshaking (TCP request handshaking)

With the server IP address available, now the browser can initiate a TCP connection to that.

With the server IP address available, now the browser can initiate a TCP connection to that.

A TCP connection requires a bit of handshaking before it can be fully initialized and you can start sending data.

A TCP connection requires a bit of handshaking before it can be fully initialized and you can start sending data.

Once the connection is established, we can send the request

Once the connection is established, we can send the request

Sending the request (Sending the request)

The request is a plain text document structured in a precise way determined by the communication protocol.

The request is a plain text document structured in a precise way determined by the communication protocol.

It’s composed of 3 parts:

It's composed of 3 parts:

  • the request line

    the request line
  • the request header

    the request header
  • the request body

    the request body
The request line (The request line)

The request line sets, on a single line:

The request line sets, on a single line:

  • the HTTP method

    the HTTP method
  • the resource location

    the resource location
  • the protocol version

    the protocol version

Example:

例:

GET / HTTP/1.1
The request header (The request header)

The request header is a set of field: value pairs that set certain values.

The request header is a set of field: value pairs that set certain values.

There are 2 mandatory fields, one of which is Host, and the other is Connection, while all the other fields are optional:

There are 2 mandatory fields, one of which is Host , and the other is Connection , while all the other fields are optional:

Host: flaviocopes.comConnection: close

Host indicates the domain name which we want to target, while Connection is always set to close unless the connection must be kept open.

Host indicates the domain name which we want to target, while Connection is always set to close unless the connection must be kept open.

Some of the most used header fields are:

Some of the most used header fields are:

  • Origin

    Origin

  • Accept

    Accept

  • Accept-Encoding

    Accept-Encoding

  • Cookie

    Cookie

  • Cache-Control

    Cache-Control

  • Dnt

    Dnt

but many more exist.

but many more exist.

The header part is terminated by a blank line.

The header part is terminated by a blank line.

The request body (The request body)

The request body is optional, not used in GET requests but very much used in POST requests and sometimes in other verbs too, and it can contain data in JSON format.

The request body is optional, not used in GET requests but very much used in POST requests and sometimes in other verbs too, and it can contain data in JSON format.

Since we’re now analyzing a GET request, the body is blank and we’ll not look more into it.

Since we're now analyzing a GET request, the body is blank and we'll not look more into it.

The response (The response)

Once the request is sent, the server processes it and sends back a response.

Once the request is sent, the server processes it and sends back a response.

The response starts with the status code and the status message. If the request is successful and returns a 200, it will start with:

The response starts with the status code and the status message. If the request is successful and returns a 200, it will start with:

200 OK

The request might return a different status code and message, like one of these:

The request might return a different status code and message, like one of these:

404 Not Found403 Forbidden301 Moved Permanently500 Internal Server Error304 Not Modified401 Unauthorized

The response then contains a list of HTTP headers and the response body (which, since we’re making the request in the browser, is going to be HTML).

The response then contains a list of HTTP headers and the response body (which, since we're making the request in the browser, is going to be HTML).

Parse the HTML (Parse the HTML)

The browser now has received the HTML and starts to parse it, and will repeat the exact same process we did not for all the resources required by the page:

The browser now has received the HTML and starts to parse it, and will repeat the exact same process we did not for all the resources required by the page:

  • CSS files

    CSS files
  • images

    图片
  • the favicon

    the favicon
  • JavaScript files

    JavaScript files

How browsers render the page then is out of the scope, but it’s important to understand that the process I described is not just for the HTML pages, but for any item that’s served over HTTP.

How browsers render the page then is out of the scope, but it's important to understand that the process I described is not just for the HTML pages, but for any item that's served over HTTP.

Build an HTTP Server with Node.js (Build an HTTP Server with Node.js)

Here is the HTTP web server we used as the Node.js Hello World application in the introduction:

Here is the HTTP web server we used as the Node.js Hello World application in the introduction:

const http = require('http')
const port = 3000
const server = http.createServer((req, res) => {  res.statusCode = 200  res.setHeader('Content-Type', 'text/plain')  res.end('Hello World\n')})
server.listen(port, () => {  console.log(`Server running at http://${hostname}:${port}/`)})

Let’s analyze it briefly. We include the http module.

Let's analyze it briefly. We include the http module .

We use the module to create an HTTP server.

We use the module to create an HTTP server.

The server is set to listen on the specified port, 3000. When the server is ready, the listencallback function is called.

The server is set to listen on the specified port, 3000 . When the server is ready, the listen callback function is called.

The callback function we pass is the one that’s going to be executed upon every request that comes in. Whenever a new request is received, the request event is called, providing two objects: a request (an http.IncomingMessageobject) and a response (an http.ServerResponseobject).

The callback function we pass is the one that's going to be executed upon every request that comes in. Whenever a new request is received, the request event is called, providing two objects: a request (an http.IncomingMessage object) and a response (an http.ServerResponse object).

request provides the request details. Through it, we access the request headers and request data.

request provides the request details. Through it, we access the request headers and request data.

response is used to populate the data we’re going to return to the client.

response is used to populate the data we're going to return to the client.

In this case with:

In this case with:

res.statusCode = 200

We set the statusCode property to 200, to indicate a successful response.

We set the statusCode property to 200 , to indicate a successful response.

We also set the Content-Type header:

We also set the Content-Type header:

res.setHeader('Content-Type', 'text/plain')

and we end close the response, adding the content as an argument to end():

and we end close the response, adding the content as an argument to end() :

res.end('Hello World\n')

Making HTTP requests with Node.js (Making HTTP requests with Node.js)

How to perform HTTP requests with Node.js using GET, POST, PUT and DELETE.

How to perform HTTP requests with Node.js using GET, POST, PUT and DELETE.

I use the term HTTP, but HTTPS is what should be used everywhere, therefore these examples use HTTPS instead of HTTP.

I use the term HTTP, but HTTPS is what should be used everywhere, therefore these examples use HTTPS instead of HTTP.

Perform a GET Request (Perform a GET Request)
const https = require('https')const options = {  hostname: 'flaviocopes.com',  port: 443,  path: '/todos',  method: 'GET'}
const req = https.request(options, (res) => {  console.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {    process.stdout.write(d)  })})
req.on('error', (error) => {  console.error(error)})
req.end()
Perform a POST Request (Perform a POST Request)
const https = require('https')
const data = JSON.stringify({  todo: 'Buy the milk'})
const options = {  hostname: 'flaviocopes.com',  port: 443,  path: '/todos',  method: 'POST',  headers: {    'Content-Type': 'application/json',    'Content-Length': data.length  }}
const req = https.request(options, (res) => {  console.log(`statusCode: ${res.statusCode}`)
res.on('data', (d) => {    process.stdout.write(d)  })})
req.on('error', (error) => {  console.error(error)})
req.write(data)req.end()
PUT and DELETE (PUT and DELETE)

PUT and DELETE requests use the same POST request format, and just change the options.method value.

PUT and DELETE requests use the same POST request format, and just change the options.method value.

HTTP requests in Node.js using Axios (HTTP requests in Node.js using Axios)

Axios is a very popular JavaScript library you can use to perform HTTP requests, that works in both Browser and Node.js platforms.

Axios is a very popular JavaScript library you can use to perform HTTP requests, that works in both Browser and Node.js platforms.

It supports all modern browsers, including support for IE8 and higher.

It supports all modern browsers, including support for IE8 and higher.

It is promise-based, and this lets us write async/await code to perform XHR requests very easily.

It is promise-based, and this lets us write async/await code to perform XHR requests very easily.

Using Axios has quite a few advantages over the native Fetch API:

Using Axios has quite a few advantages over the native Fetch API:

  • supports older browsers (Fetch needs a polyfill)

    supports older browsers (Fetch needs a polyfill)
  • has a way to abort a request

    has a way to abort a request
  • has a way to set a response timeout

    has a way to set a response timeout
  • has built-in CSRF protection

    has built-in CSRF protection
  • supports upload progress

    supports upload progress
  • performs automatic JSON data transformation

    performs automatic JSON data transformation
  • works in Node.js

    works in Node.js
安装 (Installation)

Axios can be installed using npm:

Axios can be installed using npm:

npm install axios

or yarn:

or yarn:

yarn add axios

or simply include it in your page using unpkg.com:

or simply include it in your page using unpkg.com:

<script src="https://unpkg.com/axios/dist/axios.min.js"><;/script>
The Axios API (The Axios API)

You can start an HTTP request from the axios object:

You can start an HTTP request from the axios object:

axios({  url: 'https://dog.ceo/api/breeds/list/all',  method: 'get',  data: {    foo: 'bar'  }})

but for convenience, you will generally use:

but for convenience, you will generally use:

  • axios.get()

    axios.get()

  • axios.post()

    axios.post()

(like in jQuery you would use $.get() and $.post() instead of $.ajax())

(like in jQuery you would use $.get() and $.post() instead of $.ajax() )

Axios offers methods for all the HTTP verbs, which are less popular but still used:

Axios offers methods for all the HTTP verbs, which are less popular but still used:

  • axios.delete()

    axios.delete()

  • axios.put()

    axios.put()

  • axios.patch()

    axios.patch()

  • axios.options()

    axios.options()

and a method to get the HTTP headers of a request, discarding the body:

and a method to get the HTTP headers of a request, discarding the body:

  • axios.head()

    axios.head()

GET requests (GET requests)

One convenient way to use Axios is to use the modern (ES2017) async/await syntax.

One convenient way to use Axios is to use the modern (ES2017) async/await syntax.

This Node.js example queries the Dog API to retrieve a list of all the dog breeds, using axios.get(), and it counts them:

This Node.js example queries the Dog API to retrieve a list of all the dog breeds, using axios.get() , and it counts them:

const axios = require('axios')
const getBreeds = async () => {  try {    return await axios.get('https://dog.ceo/api/breeds/list/all')  } catch (error) {    console.error(error)  }}
const countBreeds = async () => {  const breeds = await getBreeds()
if (breeds.data.message) {    console.log(`Got ${Object.entries(breeds.data.message).length} breeds`)  }}
countBreeds()

If you don’t want to use async/await you can use the Promises syntax:

If you don't want to use async/await you can use the Promises syntax:

const axios = require('axios')
const getBreeds = () => {  try {    return axios.get('https://dog.ceo/api/breeds/list/all')  } catch (error) {    console.error(error)  }}
const countBreeds = async () => {  const breeds = getBreeds()    .then(response => {      if (response.data.message) {        console.log(          `Got ${Object.entries(response.data.message).length} breeds`        )      }    })    .catch(error => {      console.log(error)    })}
countBreeds()
Add parameters to GET requests (Add parameters to GET requests)

A GET response can contain parameters in the URL, like this: https://site.com/?foo=bar

A GET response can contain parameters in the URL, like this: https://site.com/?foo=bar

With Axios you can perform this by simply using that URL:

With Axios you can perform this by simply using that URL:

axios.get('https://site.com/?foo=bar')

or you can use a params property in the options:

or you can use a params property in the options:

axios.get('https://site.com/', {  params: {    foo: 'bar'  }})
POST Requests (POST Requests)

Performing a POST request is just like doing a GET request, but instead of axios.get, you use axios.post:

Performing a POST request is just like doing a GET request, but instead of axios.get , you use axios.post :

axios.post('https://site.com/')

An object containing the POST parameters is the second argument:

An object containing the POST parameters is the second argument:

axios.post('https://site.com/', {  foo: 'bar'})

在Node.js中使用WebSockets (Using WebSockets in Node.js)

WebSockets are an alternative to HTTP communication in Web Applications.

WebSockets are an alternative to HTTP communication in Web Applications.

They offer a long lived, bidirectional communication channel between client and server.

They offer a long lived, bidirectional communication channel between client and server.

Once established, the channel is kept open, offering a very fast connection with low latency and overhead.

Once established, the channel is kept open, offering a very fast connection with low latency and overhead.

Browser support for WebSockets (Browser support for WebSockets)

WebSockets are supported by all modern browsers.

WebSockets are supported by all modern browsers.

How WebSockets differ from HTTP (How WebSockets differ from HTTP)

HTTP is a very different protocol, and has a different way of communicating.

HTTP is a very different protocol, and has a different way of communicating.

HTTP is a request/response protocol: the server returns some data when the client requests it.

HTTP is a request/response protocol: the server returns some data when the client requests it.

With WebSockets:

With WebSockets:

  • the server can send a message to the client without the client explicitly requesting something

    the server can send a message to the client without the client explicitly requesting something

  • the client and the server can talk to each other simultaneously

    the client and the server can talk to each other simultaneously

  • very little data overhead needs to be exchanged to send messages. This means a low latency communication.

    very little data overhead needs to be exchanged to send messages. This means a low latency communication .

WebSockets are great for real-time and long-lived communications.

WebSockets are great for real-time and long-lived communications.

HTTP is great for occasional data exchange and interactions initiated by the client.

HTTP is great for occasional data exchange and interactions initiated by the client.

HTTP is much simpler to implement, while WebSockets require a bit more overhead.

HTTP is much simpler to implement, while WebSockets require a bit more overhead.

Secured WebSockets (Secured WebSockets)

Always use the secure, encrypted protocol for WebSockets, wss://.

Always use the secure, encrypted protocol for WebSockets, wss:// .

ws:// refers to the unsafe WebSockets version (the http:// of WebSockets), and should be avoided for obvious reasons.

ws:// refers to the unsafe WebSockets version (the http:// of WebSockets), and should be avoided for obvious reasons.

Create a new WebSockets connection (Create a new WebSockets connection)

const url = 'wss://myserver.com/something'const connection = new WebSocket(url)

connection is a WebSocket object.

connection is a WebSocket object.

When the connection is successfully established, the openevent is fired.

When the connection is successfully established, the open event is fired.

Listen for it by assigning a callback function to the onopenproperty of the connection object:

Listen for it by assigning a callback function to the onopen property of the connection object:

connection.onopen = () => {  //...}

If there’s any error, the onerror function callback is fired:

If there's any error, the onerror function callback is fired:

connection.onerror = error => {  console.log(`WebSocket error: ${error}`)}

Sending data to the server using WebSockets (Sending data to the server using WebSockets)

Once the connection is open, you can send data to the server.

Once the connection is open, you can send data to the server.

You can do so conveniently inside the onopen callback function:

You can do so conveniently inside the onopen callback function:

connection.onopen = () => {  connection.send('hey')}

Receiving data from the server using WebSockets (Receiving data from the server using WebSockets)

Listen with a callback function on onmessage, which is called when the message event is received:

Listen with a callback function on onmessage , which is called when the message event is received:

connection.onmessage = e => {  console.log(e.data)}

Implement a WebSockets server in Node.js (Implement a WebSockets server in Node.js)

ws is a popular WebSockets library for Node.js.

ws is a popular WebSockets library for Node.js.

We’ll use it to build a WebSockets server. It can also be used to implement a client, and use WebSockets to communicate between two backend services.

We'll use it to build a WebSockets server. It can also be used to implement a client, and use WebSockets to communicate between two backend services.

Easily install it using:

Easily install it using:

yarn inityarn add ws

The code you need to write is very little:

The code you need to write is very little:

const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
wss.on('connection', ws => {  ws.on('message', message => {    console.log(`Received message => ${message}`)  })  ws.send('ho!')})

This code creates a new server on port 8080 (the default port for WebSockets), and adds a callback function when a connection is established, sending ho! to the client, and logging the messages it receives.

This code creates a new server on port 8080 (the default port for WebSockets), and adds a callback function when a connection is established, sending ho! to the client, and logging the messages it receives.

See a live example on Glitch (See a live example on Glitch)

Here is a live example of a WebSockets server.

Here is a live example of a WebSockets server.

Here is a WebSockets client that interacts with the server.

Here is a WebSockets client that interacts with the server.

Working with file descriptors in Node.js (Working with file descriptors in Node.js)

Before you’re able to interact with a file that sits in your file system, you must get a file descriptor.

Before you're able to interact with a file that sits in your file system, you must get a file descriptor.

A file descriptor is what’s returned by opening the file using the open() method offered by the fs module:

A file descriptor is what's returned by opening the file using the open() method offered by the fs module:

const fs = require('fs')
fs.open('/Users/flavio/test.txt', 'r', (err, fd) => {  //fd is our file descriptor})

Notice the r we used as the second parameter to the fs.open() call.

Notice the r we used as the second parameter to the fs.open() call.

That flag means we open the file for reading.

That flag means we open the file for reading.

Other flags you’ll commonly use are

Other flags you'll commonly use are

  • r+ open the file for reading and writing

    r+ open the file for reading and writing

  • w+ open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

    w+ open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

  • a open the file for writing, positioning the stream at the end of the file. The file is created if not existing

    a open the file for writing, positioning the stream at the end of the file. The file is created if not existing

  • a+ open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing

    a+ open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing

You can also open the file by using the fs.openSync method, which instead of providing the file descriptor object in a callback, it returns it:

You can also open the file by using the fs.openSync method, which instead of providing the file descriptor object in a callback, it returns it:

const fs = require('fs')
try {  const fd = fs.openSync('/Users/flavio/test.txt', 'r')} catch (err) {  console.error(err)}

Once you get the file descriptor, in whatever way you choose, you can perform all the operations that require it, like calling fs.open() and many other operations that interact with the file system.

Once you get the file descriptor, in whatever way you choose, you can perform all the operations that require it, like calling fs.open() and many other operations that interact with the file system.

Node.js file stats (Node.js file stats)

Every file comes with a set of details that we can inspect using Node.js.

Every file comes with a set of details that we can inspect using Node.js.

In particular, using the stat() method provided by the fs module.

In particular, using the stat() method provided by the fs module.

You call it passing a file path, and once Node.js gets the file details it will call the callback function you pass with 2 parameters: an error message, and the file stats:

You call it passing a file path, and once Node.js gets the file details it will call the callback function you pass with 2 parameters: an error message, and the file stats:

const fs = require('fs')fs.stat('/Users/flavio/test.txt', (err, stats) => {  if (err) {    console.error(err)    return  }  //we have access to the file stats in `stats`})

Node.js provides also a sync method, which blocks the thread until the file stats are ready:

Node.js provides also a sync method, which blocks the thread until the file stats are ready:

const fs = require('fs')try {  const stats = fs.stat('/Users/flavio/test.txt')} catch (err) {  console.error(err)}

The file information is included in the stats variable. What kind of information can we extract using the stats?

The file information is included in the stats variable. What kind of information can we extract using the stats?

A lot, including:

A lot, including:

  • if the file is a directory or a file, using stats.isFile() and stats.isDirectory()

    if the file is a directory or a file, using stats.isFile() and stats.isDirectory()

  • if the file is a symbolic link using stats.isSymbolicLink()

    if the file is a symbolic link using stats.isSymbolicLink()

  • the file size in bytes using stats.size.

    the file size in bytes using stats.size .

There are other advanced methods, but the bulk of what you’ll use in your day-to-day programming is this:

There are other advanced methods, but the bulk of what you'll use in your day-to-day programming is this:

const fs = require('fs')fs.stat('/Users/flavio/test.txt', (err, stats) => {  if (err) {    console.error(err)    return  }
stats.isFile() //true  stats.isDirectory() //false  stats.isSymbolicLink() //false  stats.size //1024000 //= 1MB})

Node.js File Paths (Node.js File Paths)

Every file in the system has a path.

Every file in the system has a path.

On Linux and macOS, a path might look like:

On Linux and macOS, a path might look like:

/users/flavio/file.txt

/users/flavio/file.txt

While Windows computers are different, and have a structure such as:

While Windows computers are different, and have a structure such as:

C:\users\flavio\file.txt

C:\users\flavio\file.txt

You need to pay attention when using paths in your applications, as this difference must be taken into account.

You need to pay attention when using paths in your applications, as this difference must be taken into account.

You include this module in your files using:

You include this module in your files using:

const path = require('path')

and you can start using its methods.

and you can start using its methods.

Getting information out of a path (Getting information out of a path)

Given a path, you can extract information out of it using those methods:

Given a path, you can extract information out of it using those methods:

  • dirname: get the parent folder of a file

    dirname : get the parent folder of a file

  • basename: get the filename part

    basename : get the filename part

  • extname: get the file extension

    extname : get the file extension

Example:

例:

const notes = '/users/flavio/notes.txt'
path.dirname(notes) // /users/flaviopath.basename(notes) // notes.txtpath.extname(notes) // .txt

You can get the file name without the extension by specifying a second argument to basename:

You can get the file name without the extension by specifying a second argument to basename :

path.basename(notes, path.extname(notes)) //notes
Working with paths (Working with paths)

You can join two or more parts of a path by using path.join():

You can join two or more parts of a path by using path.join() :

const name = 'flavio'path.join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt'

You can get the absolute path calculation of a relative path using path.resolve():

You can get the absolute path calculation of a relative path using path.resolve() :

path.resolve('flavio.txt') //'/Users/flavio/flavio.txt' if run from my home folder

In this case Node.js will simply append /flavio.txt to the current working directory. If you specify a second parameter folder, resolve will use the first as a base for the second:

In this case Node.js will simply append /flavio.txt to the current working directory. If you specify a second parameter folder, resolve will use the first as a base for the second:

path.resolve('tmp', 'flavio.txt')// '/Users/flavio/tmp/flavio.txt' if run from my home folder

If the first parameter starts with a slash, that means it’s an absolute path:

If the first parameter starts with a slash, that means it's an absolute path:

path.resolve('/etc', 'flavio.txt')// '/etc/flavio.txt'

path.normalize() is another useful function, that will try and calculate the actual path, when it contains relative specifiers like . or .., or double slashes:

path.normalize() is another useful function, that will try and calculate the actual path, when it contains relative specifiers like . or .. , or double slashes:

path.normalize('/users/flavio/..//test.txt') // /users/test.txt

But resolve and normalize will not check if the path exists. They just calculate a path based on the information they got.

But resolve and normalize will not check if the path exists. They just calculate a path based on the information they got.

Reading files with Node.js (Reading files with Node.js)

The simplest way to read a file in Node.js is to use the fs.readFile() method, passing it the file path and a callback function that will be called with the file data (and the error):

The simplest way to read a file in Node.js is to use the fs.readFile() method, passing it the file path and a callback function that will be called with the file data (and the error):

const fs = require('fs')
fs.readFile('/Users/flavio/test.txt', (err, data) => {  if (err) {    console.error(err)    return  }  console.log(data)})

Alternatively, you can use the synchronous version fs.readFileSync():

Alternatively, you can use the synchronous version fs.readFileSync() :

const fs = require('fs')
try {  const data = fs.readFileSync('/Users/flavio/test.txt')  console.log(data)} catch (err) {  console.error(err)}

The default encoding is utf8, but you can specify a custom encoding using a a second parameter.

The default encoding is u tf8 , but you can specify a custom encoding using aa second parameter.

Both fs.readFile() and fs.readFileSync() read the full content of the file in memory before returning the data.

Both fs.readFile() and fs.readFileSync() read the full content of the file in memory before returning the data.

This means that big files are going to have a major impact on your memory consumption and speed of execution of the program.

This means that big files are going to have a major impact on your memory consumption and speed of execution of the program.

In this case, a better option is to read the file content using streams.

In this case, a better option is to read the file content using streams.

Writing files with Node.js (Writing files with Node.js)

The easiest way to write to files in Node.js is to use the fs.writeFile() API.

The easiest way to write to files in Node.js is to use the fs.writeFile() API.

Example:

例:

const fs = require('fs')
const content = 'Some content!'
fs.writeFile('/Users/flavio/test.txt', content, (err) => {  if (err) {    console.error(err)    return  }  //file written successfully})

Alternatively, you can use the synchronous version fs.writeFileSync():

Alternatively, you can use the synchronous version fs.writeFileSync() :

const fs = require('fs')
const content = 'Some content!'
try {  const data = fs.writeFileSync('/Users/flavio/test.txt', content)  //file written successfully} catch (err) {  console.error(err)}

By default, this API will replace the contents of the file if it does already exist.

By default, this API will replace the contents of the file if it does already exist.

You can modify the default by specifying a flag:

You can modify the default by specifying a flag:

fs.writeFile('/Users/flavio/test.txt', content, { flag: 'a+' }, (err) => {})

The flags you’ll likely use are:

The flags you'll likely use are:

  • r+ open the file for reading and writing

    r+ open the file for reading and writing

  • w+ open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

    w+ open the file for reading and writing, positioning the stream at the beginning of the file. The file is created if not existing

  • a open the file for writing, positioning the stream at the end of the file. The file is created if not existing

    a open the file for writing, positioning the stream at the end of the file. The file is created if not existing

  • a+ open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing

    a+ open the file for reading and writing, positioning the stream at the end of the file. The file is created if not existing

You can find more about flags.

You can find more about flags .

Append to a file (Append to a file)

A handy method to append content to the end of a file is fs.appendFile() (and its fs.appendFileSync() counterpart):

A handy method to append content to the end of a file is fs.appendFile() (and its fs.appendFileSync() counterpart):

const content = 'Some content!'
fs.appendFile('file.log', content, (err) => {  if (err) {    console.error(err)    return  }  //done!})
Using streams (Using streams)

All those methods write the full content to the file before returning the control back to your program (in the async version, this means executing the callback)

All those methods write the full content to the file before returning the control back to your program (in the async version, this means executing the callback)

In this case, a better option is to write the file content using streams.

In this case, a better option is to write the file content using streams.

Working with folders in Node.js (Working with folders in Node.js)

The Node.js fs core module provides many handy methods you can use to work with folders.

The Node.js fs core module provides many handy methods you can use to work with folders.

Check if a folder exists (Check if a folder exists)

Use fs.access() to check if the folder exists and Node.js can access it with its permissions.

Use fs.access() to check if the folder exists and Node.js can access it with its permissions.

Create a new folder (Create a new folder)

Use fs.mkdir() or fs.mkdirSync() to create a new folder:

Use fs.mkdir() or fs.mkdirSync() to create a new folder:

const fs = require('fs')
const folderName = '/Users/flavio/test'
try {  if (!fs.existsSync(dir)){    fs.mkdirSync(dir)  }} catch (err) {  console.error(err)}
Read the content of a directory (Read the content of a directory)

Use fs.readdir() or fs.readdirSync to read the contents of a directory.

Use fs.readdir() or fs.readdirSync to read the contents of a directory.

This piece of code reads the content of a folder, both files and subfolders, and returns their relative path:

This piece of code reads the content of a folder, both files and subfolders, and returns their relative path:

const fs = require('fs')const path = require('path')
const folderPath = '/Users/flavio'
fs.readdirSync(folderPath)

You can get the full path:

You can get the full path:

fs.readdirSync(folderPath).map(fileName => {  return path.join(folderPath, fileName)}

You can also filter the results to only return the files, and exclude the folders:

You can also filter the results to only return the files, and exclude the folders:

const isFile = fileName => {  return fs.lstatSync(fileName).isFile()}
fs.readdirSync(folderPath).map(fileName => {  return path.join(folderPath, fileName)).filter(isFile)}
Rename a folder (Rename a folder)

Use fs.rename() or fs.renameSync() to rename folder.

Use fs.rename() or fs.renameSync() to rename folder.

The first parameter is the current path, the second the new path:

The first parameter is the current path, the second the new path:

const fs = require('fs')
fs.rename('/Users/flavio', '/Users/roger', (err) => {  if (err) {    console.error(err)    return  }  //done})

fs.renameSync() is the synchronous version:

fs.renameSync() is the synchronous version:

const fs = require('fs')
try {  fs.renameSync('/Users/flavio', '/Users/roger')} catch (err) {  console.error(err)}
Remove a folder (Remove a folder)

Use fs.rmdir() or fs.rmdirSync() to remove a folder.

Use fs.rmdir() or fs.rmdirSync() to remove a folder.

Removing a folder that has content can be more complicated than you need.

Removing a folder that has content can be more complicated than you need.

In this case I recommend installing the fs-extra module, which is very popular and well maintained, and it’s a drop-in replacement of the fs module, providing more features on top of it.

In this case I recommend installing the fs-extra module, which is very popular and well maintained, and it's a drop-in replacement of the fs module, providing more features on top of it.

In this case the remove() method is what you want.

In this case the remove() method is what you want.

Install it using:

Install it using:

npm install fs-extra

npm install fs-extra

and use it like this:

and use it like this:

const fs = require('fs-extra')
const folder = '/Users/flavio'
fs.remove(folder, err => {  console.error(err)})

It can also be used with promises:

It can also be used with promises:

fs.remove(folder).then(() => {  //done}).catch(err => {  console.error(err)})

or with async/await:

or with async/await :

async function removeFolder(folder) {  try {    await fs.remove(folder)    //done  } catch (err) {    console.error(err)  }}
const folder = '/Users/flavio'removeFolder(folder)

The Node.js fs module (The Node.js fs module)

The fs module provides a lot of very useful functionality to access and interact with the file system.

The fs module provides a lot of very useful functionality to access and interact with the file system.

There is no need to install it. Being part of the Node.js core, it can be used by simply requiring it:

There is no need to install it. Being part of the Node.js core, it can be used by simply requiring it:

const fs = require('fs')

Once you do so, you have access to all its methods, which include:

Once you do so, you have access to all its methods, which include:

  • fs.access(): check if the file exists and Node can access it with its permissions

    fs.access() : check if the file exists and Node can access it with its permissions

  • fs.appendFile(): append data to a file. If the file does not exist, it’s created

    fs.appendFile() : append data to a file. If the file does not exist, it's created

  • fs.chmod(): change the permissions of a file specified by the filename passed. Related: fs.lchmod(), fs.fchmod()

    fs.chmod() : change the permissions of a file specified by the filename passed. Related: fs.lchmod() , fs.fchmod()

  • fs.chown(): change the owner and group of a file specified by the filename passed. Related: fs.fchown(), fs.lchown()

    fs.chown() : change the owner and group of a file specified by the filename passed. Related: fs.fchown() , fs.lchown()

  • fs.close(): close a file descriptor

    fs.close() : close a file descriptor

  • fs.copyFile(): copies a file

    fs.copyFile() : copies a file

  • fs.createReadStream(): create a readable file stream

    fs.createReadStream() : create a readable file stream

  • fs.createWriteStream(): create a writable file stream

    fs.createWriteStream() : create a writable file stream

  • fs.link(): create a new hard link to a file

    fs.link() : create a new hard link to a file

  • fs.mkdir(): create a new folder

    fs.mkdir() : create a new folder

  • fs.mkdtemp(): create a temporary directory

    fs.mkdtemp() : create a temporary directory

  • fs.open(): set the file mode

    fs.open() : set the file mode

  • fs.readdir(): read the contents of a directory

    fs.readdir() : read the contents of a directory

  • fs.readFile(): read the content of a file. Related: fs.read()

    fs.readFile() : read the content of a file. Related: fs.read()

  • fs.readlink(): read the value of a symbolic link

    fs.readlink() : read the value of a symbolic link

  • fs.realpath(): resolve relative file path pointers (., ..) to the full path

    fs.realpath() : resolve relative file path pointers ( . , .. ) to the full path

  • fs.rename(): rename a file or folder

    fs.rename() : rename a file or folder

  • fs.rmdir(): remove a folder

    fs.rmdir() : remove a folder

  • fs.stat(): returns the status of the file identified by the filename passed. Related: fs.fstat(), fs.lstat()

    fs.stat() : returns the status of the file identified by the filename passed. Related: fs.fstat() , fs.lstat()

  • fs.symlink(): create a new symbolic link to a file

    fs.symlink() : create a new symbolic link to a file

  • fs.truncate(): truncate to the specified length the file identified by the filename passed. Related: fs.ftruncate()

    fs.truncate() : truncate to the specified length the file identified by the filename passed. Related: fs.ftruncate()

  • fs.unlink(): remove a file or a symbolic link

    fs.unlink() : remove a file or a symbolic link

  • fs.unwatchFile(): stop watching for changes on a file

    fs.unwatchFile() : stop watching for changes on a file

  • fs.utimes(): change the timestamp of the file identified by the filename passed. Related: fs.futimes()

    fs.utimes() : change the timestamp of the file identified by the filename passed. Related: fs.futimes()

  • fs.watchFile(): start watching for changes on a file. Related: fs.watch()

    fs.watchFile() : start watching for changes on a file. Related: fs.watch()

  • fs.writeFile(): write data to a file. Related: fs.write()

    fs.writeFile() : write data to a file. Related: fs.write()

One peculiar thing about the fs module is that all the methods are asynchronous by default, but they can also work synchronously by appending Sync.

One peculiar thing about the fs module is that all the methods are asynchronous by default, but they can also work synchronously by appending Sync .

For example:

例如:

  • fs.rename()

    fs.rename()

  • fs.renameSync()

    fs.renameSync()

  • fs.write()

    fs.write()

  • fs.writeSync()

    fs.writeSync()

This makes a huge difference in your application flow.

This makes a huge difference in your application flow.

Node 10 includes experimental support for a promise based API.

Node 10 includes experimental support for a promise based API.

For example let’s examine the fs.rename() method. The asynchronous API is used with a callback:

For example let's examine the fs.rename() method. The asynchronous API is used with a callback:

const fs = require('fs')
fs.rename('before.json', 'after.json', (err) => {  if (err) {    return console.error(err)  }
//done})

A synchronous API can be used like this, with a try/catch block to handle errors:

A synchronous API can be used like this, with a try/catch block to handle errors:

const fs = require('fs')
try {  fs.renameSync('before.json', 'after.json')  //done} catch (err) {  console.error(err)}

The key difference here is that the execution of your script will block in the second example, until the file operation succeeded.

The key difference here is that the execution of your script will block in the second example, until the file operation succeeded.

The Node.js path module (The Node.js path module)

The path module provides a lot of very useful functionality to access and interact with the file system.

The path module provides a lot of very useful functionality to access and interact with the file system.

There is no need to install it. Being part of the Node.js core, it can be used by simply requiring it:

There is no need to install it. Being part of the Node.js core, it can be used by simply requiring it:

const path = require('path')

This module provides path.sep which provides the path segment separator (\ on Windows, and / on Linux / macOS), and path.delimiter which provides the path delimiter (; on Windows, and : on Linux / macOS).

This module provides path.sep which provides the path segment separator ( \ on Windows, and / on Linux / macOS), and path.delimiter which provides the path delimiter ( ; on Windows, and : on Linux / macOS).

These are the path methods.

These are the path methods.

path.basename() (path.basename())

Return the last portion of a path. A second parameter can filter out the file extension:

Return the last portion of a path. A second parameter can filter out the file extension:

require('path').basename('/test/something') //somethingrequire('path').basename('/test/something.txt') //something.txtrequire('path').basename('/test/something.txt', '.txt') //something
path.dirname() (path.dirname())

Return the directory part of a path:

Return the directory part of a path:

require('path').dirname('/test/something') // /testrequire('path').dirname('/test/something/file.txt') // /test/something
path.extname() (path.extname())

Return the extension part of a path:

Return the extension part of a path:

require('path').dirname('/test/something') // ''require('path').dirname('/test/something/file.txt') // '.txt'
path.isAbsolute() (path.isAbsolute())

Returns true if it’s an absolute path:

Returns true if it's an absolute path:

require('path').isAbsolute('/test/something') // truerequire('path').isAbsolute('./test/something') // false
path.join() (path.join())

Joins two or more parts of a path:

Joins two or more parts of a path:

const name = 'flavio'require('path').join('/', 'users', name, 'notes.txt') //'/users/flavio/notes.txt'
path.normalize() (path.normalize())

Tries to calculate the actual path when it contains relative specifiers like . or .., or double slashes:

Tries to calculate the actual path when it contains relative specifiers like . or .. , or double slashes:

require('path').normalize('/users/flavio/..//test.txt') ///users/test.txt
path.parse() (path.parse())

Parses a path to an object with the segments that compose it:

Parses a path to an object with the segments that compose it:

  • root: the root

    root : the root

  • dir: the folder path starting from the root

    dir : the folder path starting from the root

  • base: the file name + extension

    base : the file name + extension

  • name: the file name

    name : the file name

  • ext: the file extension

    ext : the file extension

Example:

例:

require('path').parse('/users/test.txt')

results in:

results in:

{  root: '/',  dir: '/users',  base: 'test.txt',  ext: '.txt',  name: 'test'}
path.relative() (path.relative())

Accepts 2 paths as arguments. Returns the the relative path from the first path to the second, based on the current working directory.

Accepts 2 paths as arguments. Returns the the relative path from the first path to the second, based on the current working directory.

Example:

例:

require('path').relative('/Users/flavio', '/Users/flavio/test.txt') //'test.txt'require('path').relative('/Users/flavio', '/Users/flavio/something/test.txt') //'something/test.txt'
path.resolve() (path.resolve())

You can get the absolute path calculation of a relative path using path.resolve():

You can get the absolute path calculation of a relative path using path.resolve() :

path.resolve('flavio.txt') //'/Users/flavio/flavio.txt' if run from my home folder

By specifying a second parameter, resolve will use the first as a base for the second:

By specifying a second parameter, resolve will use the first as a base for the second:

path.resolve('tmp', 'flavio.txt')//'/Users/flavio/tmp/flavio.txt' if run from my home folder

If the first parameter starts with a slash, that means it’s an absolute path:

If the first parameter starts with a slash, that means it's an absolute path:

path.resolve('/etc', 'flavio.txt')//'/etc/flavio.txt'

The Node.js os module (The Node.js os module)

This module provides many functions that you can use to retrieve information from the underlying operating system and the computer the program runs on, and interact with it.

This module provides many functions that you can use to retrieve information from the underlying operating system and the computer the program runs on, and interact with it.

const os = require('os')

There are a few useful properties that tell us some key things related to handling files:

There are a few useful properties that tell us some key things related to handling files:

os.EOL gives the line delimiter sequence. It's \n on Linux and macOS, and \r\n on Windows.

os.EOL gives the line delimiter sequence. It's \n on Linux and macOS, and \r\n on Windows.

When I say Linux and macOS I mean POSIX platforms. For simplicity I exclude other less popular operating systems Node can run on.

When I say Linux and macOS I mean POSIX platforms. For simplicity I exclude other less popular operating systems Node can run on.

os.constants.signals tells us all the constants related to handling process signals, like SIGHUP, SIGKILL and so on.

os.constants.signals tells us all the constants related to handling process signals, like SIGHUP, SIGKILL and so on.

os.constants.errno sets the constants for error reporting, like EADDRINUSE, EOVERFLOW and more.

os.constants.errno sets the constants for error reporting, like EADDRINUSE, EOVERFLOW and more.

You can read them all here.

You can read them all here .

Let’s now see the main methods that os provides:

Let's now see the main methods that os provides:

  • os.arch()

    os.arch()

  • os.cpus()

    os.cpus()

  • os.endianness()

    os.endianness()

  • os.freemem()

    os.freemem()

  • os.homedir()

    os.homedir()

  • os.hostname()

    os.hostname()

  • os.loadavg()

    os.loadavg()

  • os.networkInterfaces()

    os.networkInterfaces()

  • os.platform()

    os.platform()

  • os.release()

    os.release()

  • os.tmpdir()

    os.tmpdir()

  • os.totalmem()

    os.totalmem()

  • os.type()

    os.type()

  • os.uptime()

    os.uptime()

  • os.userInfo()

    os.userInfo()

os.arch() (os.arch())

Return the string that identifies the underlying architecture, like arm, x64, arm64.

Return the string that identifies the underlying architecture, like arm , x64 , arm64 .

os.cpus() (os.cpus())

Return information on the CPUs available on your system.

Return information on the CPUs available on your system.

Example:

例:

[ { model: 'Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz',    speed: 2400,    times:     { user: 281685380,       nice: 0,       sys: 187986530,       idle: 685833750,       irq: 0 } },  { model: 'Intel(R) Core(TM)2 Duo CPU     P8600  @ 2.40GHz',    speed: 2400,    times:     { user: 282348700,       nice: 0,       sys: 161800480,       idle: 703509470,       irq: 0 } } ]
os.endianness() (os.endianness())

Return BE or LE depending if Node.js was compiled with Big Endian or Little Endian.

Return BE or LE depending if Node.js was compiled with Big Endian or Little Endian .

os.freemem() (os.freemem())

Return the number of bytes that represent the free memory in the system.

Return the number of bytes that represent the free memory in the system.

os.homedir() (os.homedir())

Return the path to the home directory of the current user.

Return the path to the home directory of the current user.

Example:

例:

'/Users/flavio'
os.hostname() (os.hostname())

Return the hostname.

Return the hostname.

os.loadavg() (os.loadavg())

Return the calculation made by the operating system on the load average.

Return the calculation made by the operating system on the load average.

It only returns a meaningful value on Linux and macOS.

It only returns a meaningful value on Linux and macOS.

Example:

例:

[ 3.68798828125, 4.00244140625, 11.1181640625 ]
os.networkInterfaces() (os.networkInterfaces())

Returns the details of the network interfaces available on your system.

Returns the details of the network interfaces available on your system.

Example:

例:

{ lo0:   [ { address: '127.0.0.1',       netmask: '255.0.0.0',       family: 'IPv4',       mac: 'fe:82:00:00:00:00',       internal: true },     { address: '::1',       netmask: 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff',       family: 'IPv6',       mac: 'fe:82:00:00:00:00',       scopeid: 0,       internal: true },     { address: 'fe80::1',       netmask: 'ffff:ffff:ffff:ffff::',       family: 'IPv6',       mac: 'fe:82:00:00:00:00',       scopeid: 1,       internal: true } ],  en1:   [ { address: 'fe82::9b:8282:d7e6:496e',       netmask: 'ffff:ffff:ffff:ffff::',       family: 'IPv6',       mac: '06:00:00:02:0e:00',       scopeid: 5,       internal: false },     { address: '192.168.1.38',       netmask: '255.255.255.0',       family: 'IPv4',       mac: '06:00:00:02:0e:00',       internal: false } ],  utun0:   [ { address: 'fe80::2513:72bc:f405:61d0',       netmask: 'ffff:ffff:ffff:ffff::',       family: 'IPv6',       mac: 'fe:80:00:20:00:00',       scopeid: 8,       internal: false } ] }
os.platform() (os.platform())

Return the platform that Node.js was compiled for:

Return the platform that Node.js was compiled for:

  • darwin

    darwin

  • freebsd

    freebsd

  • linux

    linux

  • openbsd

    openbsd

  • win32

    win32

  • …more

    …更多
os.release() (os.release())

Returns a string that identifies the operating system release number.

Returns a string that identifies the operating system release number.

os.tmpdir() (os.tmpdir())

Returns the path to the assigned temp folder.

Returns the path to the assigned temp folder.

os.totalmem() (os.totalmem())

Returns the number of bytes that represent the total memory available in the system.

Returns the number of bytes that represent the total memory available in the system.

os.type() (os.type())

Identifies the operating system:

Identifies the operating system:

  • Linux

    Linux

  • Darwin on macOS

    Darwin on macOS

  • Windows_NT on Windows

    Windows_NT on Windows

os.uptime() (os.uptime())

Returns the number of seconds the computer has been running since it was last rebooted.

Returns the number of seconds the computer has been running since it was last rebooted.

The Node.js events module (The Node.js events module)

The events module provides us the EventEmitter class, which is key to working with events in Node.js.

The events module provides us the EventEmitter class, which is key to working with events in Node.js.

I published a full article on that, so here I will just describe the API without further examples on how to use it.

I published a full article on that, so here I will just describe the API without further examples on how to use it.

const EventEmitter = require('events')const door = new EventEmitter()

The event listener eats its own dog food and uses these events:

The event listener eats its own dog food and uses these events:

  • newListener when a listener is added

    newListener when a listener is added

  • removeListener when a listener is removed

    removeListener when a listener is removed

Here’s a detailed description of the most useful methods:

Here's a detailed description of the most useful methods:

  • emitter.addListener()

    emitter.addListener()

  • emitter.emit()

    emitter.emit()

  • emitter.eventNames()

    emitter.eventNames()

  • emitter.getMaxListeners()

    emitter.getMaxListeners()

  • emitter.listenerCount()

    emitter.listenerCount()

  • emitter.listeners()

    emitter.listeners()

  • emitter.off()

    emitter.off()

  • emitter.on()

    emitter.on()

  • emitter.once()

    emitter.once()

  • emitter.prependListener()

    emitter.prependListener()

  • emitter.prependOnceListener()

    emitter.prependOnceListener()

  • emitter.removeAllListeners()

    emitter.removeAllListeners()

  • emitter.removeListener()

    emitter.removeListener()

  • emitter.setMaxListeners()

    emitter.setMaxListeners()

emitter.addListener() (emitter.addListener())

Alias for emitter.on().

Alias for emitter.on() .

emitter.emit() (emitter.emit())

Emits an event. It synchronously calls every event listener in the order they were registered.

Emits an event. It synchronously calls every event listener in the order they were registered.

emitter.eventNames() (emitter.eventNames())

Return an array of strings that represent the events registered on the current EventListener:

Return an array of strings that represent the events registered on the current EventListener:

door.eventNames()
emitter.getMaxListeners() (emitter.getMaxListeners())

Get the maximum amount of listeners one can add to an EventListener object, which defaults to 10 but can be increased or lowered by using setMaxListeners():

Get the maximum amount of listeners one can add to an EventListener object, which defaults to 10 but can be increased or lowered by using setMaxListeners() :

door.getMaxListeners()
emitter.listenerCount() (emitter.listenerCount())

Get the count of listeners of the event passed as parameter:

Get the count of listeners of the event passed as parameter:

door.listenerCount('open')
emitter.listeners() (emitter.listeners())

Gets an array of listeners of the event passed as parameter:

Gets an array of listeners of the event passed as parameter:

door.listeners('open')
emitter.off() (emitter.off())

Alias for emitter.removeListener() added in Node 10.

Alias for emitter.removeListener() added in Node 10.

emitter.on() (emitter.on())

Adds a callback function that’s called when an event is emitted.

Adds a callback function that's called when an event is emitted.

Usage:

用法:

door.on('open', () => {  console.log('Door was opened')})
emitter.once() (emitter.once())

Adds a callback function that’s called when an event is emitted for the first time after registering this. This callback is only going to be called once, never again.

Adds a callback function that's called when an event is emitted for the first time after registering this. This callback is only going to be called once, never again.

const EventEmitter = require('events')const ee = new EventEmitter()
ee.once('my-event', () => {  //call callback function once})
emitter.prependListener() (emitter.prependListener())

When you add a listener using on or addListener, it's added last in the queue of listeners, and called last. Using prependListener it's added, and called, before other listeners.

When you add a listener using on or addListener , it's added last in the queue of listeners, and called last. Using prependListener it's added, and called, before other listeners.

emitter.prependOnceListener() (emitter.prependOnceListener())

When you add a listener using once, it's added last in the queue of listeners, and called last. Using prependOnceListener it's added, and called, before other listeners.

When you add a listener using once , it's added last in the queue of listeners, and called last. Using prependOnceListener it's added, and called, before other listeners.

emitter.removeAllListeners() (emitter.removeAllListeners())

Removes all listeners of an event emitter object listening to a specific event:

Removes all listeners of an event emitter object listening to a specific event:

door.removeAllListeners('open')
emitter.removeListener() (emitter.removeListener())

Remove a specific listener. You can do this by saving the callback function to a variable, when added, so you can reference it later:

Remove a specific listener. You can do this by saving the callback function to a variable, when added, so you can reference it later:

const doSomething = () => {}door.on('open', doSomething)door.removeListener('open', doSomething)
emitter.setMaxListeners() (emitter.setMaxListeners())

Sets the maximum amount of listeners one can add to an EventListener object, which defaults to 10 but can be increased or lowered:

Sets the maximum amount of listeners one can add to an EventListener object, which defaults to 10 but can be increased or lowered:

door.setMaxListeners(50)

The Node.js http module (The Node.js http module)

The http module of Node.js provides useful functions and classes to build an HTTP server. It is a key module to Node.js networking.

The http module of Node.js provides useful functions and classes to build an HTTP server. It is a key module to Node.js networking.

It can be included using:

It can be included using:

const http = require('http')

The module provides some properties and methods, and some classes.

The module provides some properties and methods, and some classes.

物产 (Properties)
http.METHODS (http.METHODS)

This property lists all the HTTP methods supported:

This property lists all the HTTP methods supported:

> require('http').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',  'SUBSCRIBE',  'TRACE',  'UNBIND',  'UNLINK',  'UNLOCK',  'UNSUBSCRIBE' ]
http.STATUS_CODES (http.STATUS_CODES)

This property lists all the HTTP status codes and their description:

This property lists all the HTTP status codes and their description:

> require('http').STATUS_CODES{ '100': 'Continue',  '101': 'Switching Protocols',  '102': 'Processing',  '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' }
http.globalAgent (http.globalAgent)

Points to the global instance of the Agent object, which is an instance of the http.Agentclass.

Points to the global instance of the Agent object, which is an instance of the http.Agent class.

It’s used to manage connections persistence and reuse for HTTP clients, and it’s a key component of Node.js HTTP networking.

It's used to manage connections persistence and reuse for HTTP clients, and it's a key component of Node.js HTTP networking.

More in the http.Agent class description later on.

More in the http.Agent class description later on.

方法 (Methods)
http.createServer() (http.createServer())

Return a new instance of the http.Server class.

Return a new instance of the http.Server class.

Usage:

用法:

const server = http.createServer((req, res) => {  //handle every single request with this callback})
http.request() (http.request())

Makes an HTTP request to a server, creating an instance of the http.ClientRequest class.

Makes an HTTP request to a server, creating an instance of the http.ClientRequest class.

http.get() (http.get())

Similar to http.request(), but automatically sets the HTTP method to GET, and calls req.end() automatically.

Similar to http.request() , but automatically sets the HTTP method to GET, and calls req.end() automatically.

班级 (Classes)

The HTTP module provides 5 classes:

The HTTP module provides 5 classes:

  • http.Agent

    http.Agent

  • http.ClientRequest

    http.ClientRequest

  • http.Server

    http.Server

  • http.ServerResponse

    http.ServerResponse

  • http.IncomingMessage

    http.IncomingMessage

http.Agent (http.Agent)

Node creates a global instance of the http.Agent class to manage connections persistance and reuse for HTTP clients, a key component of Node HTTP networking.

Node creates a global instance of the http.Agent class to manage connections persistance and reuse for HTTP clients, a key component of Node HTTP networking.

This object makes sure that every request made to a server is queued and a single socket is reused.

This object makes sure that every request made to a server is queued and a single socket is reused.

It also maintains a pool of sockets. This is key for performance reasons.

It also maintains a pool of sockets. This is key for performance reasons.

http.ClientRequest (http.ClientRequest)

An http.ClientRequest object is created when http.request() or http.get() is called.

An http.ClientRequest object is created when http.request() or http.get() is called.

When a response is received, the response event is called with the response, with an http.IncomingMessage instance as argument.

When a response is received, the response event is called with the response, with an http.IncomingMessage instance as argument.

The returned data of a response can be read in 2 ways:

The returned data of a response can be read in 2 ways:

  • you can call the response.read() method

    you can call the response.read() method

  • in the response event handler you can setup an event listener for the data event, so you can listen for the data streamed into.

    in the response event handler you can setup an event listener for the data event, so you can listen for the data streamed into.

http.Server (http.Server)

This class is commonly instantiated and returned when creating a new server using http.createServer().

This class is commonly instantiated and returned when creating a new server using http.createServer() .

Once you have a server object, you have access to its methods:

Once you have a server object, you have access to its methods:

  • close() stops the server from accepting new connections

    close() stops the server from accepting new connections

  • listen() starts the HTTP server and listens for connections

    listen() starts the HTTP server and listens for connections

http.ServerResponse (http.ServerResponse)

Created by an http.Server and passed as the second parameter to the request event it fires.

Created by an http.Server and passed as the second parameter to the request event it fires.

Commonly known and used in code as res:

Commonly known and used in code as res :

const server = http.createServer((req, res) => {  //res is an http.ServerResponse object})

The method you’ll always call in the handler is end(), which closes the response, the message is complete and the server can send it to the client. It must be called on each response.

The method you'll always call in the handler is end() , which closes the response, the message is complete and the server can send it to the client. It must be called on each response.

These methods are used to interact with HTTP headers:

These methods are used to interact with HTTP headers:

  • getHeaderNames() get the list of the names of the HTTP headers already set

    getHeaderNames() get the list of the names of the HTTP headers already set

  • getHeaders() get a copy of the HTTP headers already set

    getHeaders() get a copy of the HTTP headers already set

  • setHeader('headername', value) sets an HTTP header value

    setHeader('headername', value) sets an HTTP header value

  • getHeader('headername') gets an HTTP header already set

    getHeader('headername') gets an HTTP header already set

  • removeHeader('headername') removes an HTTP header already set

    removeHeader('headername') removes an HTTP header already set

  • hasHeader('headername') return true if the response has that header set

    hasHeader('headername') return true if the response has that header set

  • headersSent() return true if the headers have already been sent to the client

    headersSent() return true if the headers have already been sent to the client

After processing the headers you can send them to the client by calling response.writeHead(), which accepts the statusCode as the first parameter, the optional status message, and the headers object.

After processing the headers you can send them to the client by calling response.writeHead() , which accepts the statusCode as the first parameter, the optional status message, and the headers object.

To send data to the client in the response body, you use write(). It will send buffered data to the HTTP response stream.

To send data to the client in the response body, you use write() . It will send buffered data to the HTTP response stream.

If the headers were not sent yet using response.writeHead(), it will send the headers first, with the status code and message that’s set in the request, which you can edit by setting the statusCode and statusMessage properties values:

If the headers were not sent yet using response.writeHead() , it will send the headers first, with the status code and message that's set in the request, which you can edit by setting the statusCode and statusMessage properties values:

response.statusCode = 500response.statusMessage = 'Internal Server Error'
http.IncomingMessage (http.IncomingMessage)

An http.IncomingMessage object is created by:

An http.IncomingMessage object is created by:

  • http.Server when listening to the request event

    http.Server when listening to the request event

  • http.ClientRequest when listening to the response event

    http.ClientRequest when listening to the response event

It can be used to access the response:

It can be used to access the response:

  • status using its statusCode and statusMessage methods

    status using its statusCode and statusMessage methods

  • headers using its headers method or rawHeaders

    headers using its headers method or rawHeaders

  • HTTP method using its method method

    HTTP method using its method method

  • HTTP version using the httpVersion method

    HTTP version using the httpVersion method

  • URL using the url method

    URL using the url method

  • underlying socket using the socket method

    underlying socket using the socket method

The data is accessed using streams, since http.IncomingMessage implements the Readable Stream interface.

The data is accessed using streams, since http.IncomingMessage implements the Readable Stream interface.

Node.js流 (Node.js Streams)

Streams are one of the fundamental concepts that power Node.js applications.

Streams are one of the fundamental concepts that power Node.js applications.

They are a way to handle reading/writing files, network communications, or any kind of end-to-end information exchange in an efficient way.

They are a way to handle reading/writing files, network communications, or any kind of end-to-end information exchange in an efficient way.

Streams are not a concept unique to Node.js. They were introduced in the Unix operating system decades ago, and programs can interact with each other passing streams through the pipe operator (|).

Streams are not a concept unique to Node.js. They were introduced in the Unix operating system decades ago, and programs can interact with each other passing streams through the pipe operator ( | ).

For example, in the traditional way, when you tell the program to read a file, the file is read into memory, from start to finish, and then you process it.

For example, in the traditional way, when you tell the program to read a file, the file is read into memory, from start to finish, and then you process it.

Using streams you read it piece by piece, processing its content without keeping it all in memory.

Using streams you read it piece by piece, processing its content without keeping it all in memory.

The Node.js stream module provides the foundation upon which all streaming APIs are build.

The Node.js stream module provides the foundation upon which all streaming APIs are build.

Why streams? (Why streams?)

Streams basically provide two major advantages using other data handling methods:

Streams basically provide two major advantages using other data handling methods:

  • Memory efficiency: you don’t need to load large amounts of data in memory before you are able to process it

    Memory efficiency : you don't need to load large amounts of data in memory before you are able to process it

  • Time efficiency: it takes way less time to start processing data as soon as you have it, rather than waiting till the whole data payload is available to start

    Time efficiency : it takes way less time to start processing data as soon as you have it, rather than waiting till the whole data payload is available to start

An example of a stream (An example of a stream)

A typical example is the one of reading files from a disk.

A typical example is the one of reading files from a disk.

Using the Node.js fs module you can read a file, and serve it over HTTP when a new connection is established to your http server:

Using the Node.js fs module you can read a file, and serve it over HTTP when a new connection is established to your http server:

const http = require('http')const fs = require('fs')
const server = http.createServer(function (req, res) {  fs.readFile(__dirname + '/data.txt', (err, data) => {    res.end(data)  })})server.listen(3000)

readFile() reads the full contents of the file, and invokes the callback function when it’s done.

readFile() reads the full contents of the file, and invokes the callback function when it's done.

res.end(data) in the callback will return the file contents to the HTTP client.

res.end(data) in the callback will return the file contents to the HTTP client.

If the file is big, the operation will take quite a bit of time. Here is the same thing written using streams:

If the file is big, the operation will take quite a bit of time. Here is the same thing written using streams:

const http = require('http')const fs = require('fs')
const server = http.createServer((req, res) => {  const stream = fs.createReadStream(__dirname + '/data.txt')  stream.pipe(res)})server.listen(3000)

Instead of waiting until the file is fully read, we start streaming it to the HTTP client as soon as we have a chunk of data ready to be sent.

Instead of waiting until the file is fully read, we start streaming it to the HTTP client as soon as we have a chunk of data ready to be sent.

pipe() (pipe())

The above example uses the line stream.pipe(res): the pipe() method is called on the file stream.

The above example uses the line stream.pipe(res) : the pipe() method is called on the file stream.

What does this code do? It takes the source, and pipes it into a destination.

What does this code do? It takes the source, and pipes it into a destination.

You call it on the source stream, so in this case, the file stream is piped to the HTTP response.

You call it on the source stream, so in this case, the file stream is piped to the HTTP response.

The return value of the pipe() method is the destination stream, which is a very convenient thing that lets us chain multiple pipe() calls, like this:

The return value of the pipe() method is the destination stream, which is a very convenient thing that lets us chain multiple pipe() calls, like this:

src.pipe(dest1).pipe(dest2)

This construct is the same as doing:

This construct is the same as doing:

src.pipe(dest1)dest1.pipe(dest2)
Streams-powered Node.js APIs (Streams-powered Node.js APIs)

Due to their advantages, many Node.js core modules provide native stream handling capabilities, most notably:

Due to their advantages, many Node.js core modules provide native stream handling capabilities, most notably:

  • process.stdin returns a stream connected to stdin

    process.stdin returns a stream connected to stdin

  • process.stdout returns a stream connected to stdout

    process.stdout returns a stream connected to stdout

  • process.stderr returns a stream connected to stderr

    process.stderr returns a stream connected to stderr

  • fs.createReadStream() creates a readable stream to a file

    fs.createReadStream() creates a readable stream to a file

  • fs.createWriteStream() creates a writable stream to a file

    fs.createWriteStream() creates a writable stream to a file

  • net.connect() initiates a stream-based connection

    net.connect() initiates a stream-based connection

  • http.request() returns an instance of the http.ClientRequest class, which is a writable stream

    http.request() returns an instance of the http.ClientRequest class, which is a writable stream

  • zlib.createGzip() compress data using gzip (a compression algorithm) into a stream

    zlib.createGzip() compress data using gzip (a compression algorithm) into a stream

  • zlib.createGunzip() decompress a gzip stream.

    zlib.createGunzip() decompress a gzip stream.

  • zlib.createDeflate() compress data using deflate (a compression algorithm) into a stream

    zlib.createDeflate() compress data using deflate (a compression algorithm) into a stream

  • zlib.createInflate() decompress a deflate stream

    zlib.createInflate() decompress a deflate stream

Different types of streams (Different types of streams)

There are four classes of streams:

There are four classes of streams:

  • Readable: a stream you can pipe from, but not pipe into (you can receive data, but not send data to it). When you push data into a readable stream, it is buffered, until a consumer starts to read the data.

    Readable : a stream you can pipe from, but not pipe into (you can receive data, but not send data to it). When you push data into a readable stream, it is buffered, until a consumer starts to read the data.

  • Writable: a stream you can pipe into, but not pipe from (you can send data, but not receive from it)

    Writable : a stream you can pipe into, but not pipe from (you can send data, but not receive from it)

  • Duplex: a stream you can both pipe into and pipe from, basically a combination of a Readable and Writable stream

    Duplex : a stream you can both pipe into and pipe from, basically a combination of a Readable and Writable stream

  • Transform: a Transform stream is similar to a Duplex, but the output is a transform of its input

    Transform : a Transform stream is similar to a Duplex, but the output is a transform of its input

How to create a readable stream (How to create a readable stream)

We get the Readable stream from the stream module, and we initialize it:

We get the Readable stream from the stream module, and we initialize it:

const Stream = require('stream')const readableStream = new Stream.Readable()

Now that the stream is initialized, we can send data to it:

Now that the stream is initialized, we can send data to it:

readableStream.push('hi!')readableStream.push('ho!')
How to create a writable stream (How to create a writable stream)

To create a writable stream we extend the base Writable object, and we implement its _write() method.

To create a writable stream we extend the base Writable object, and we implement its _write() method.

First create a stream object:

First create a stream object:

const Stream = require('stream')const writableStream = new Stream.Writable()

then implement _write:

then implement _write :

writableStream._write = (chunk, encoding, next) => {    console.log(chunk.toString())    next()}

You can now pipe a readable stream in:

You can now pipe a readable stream in:

process.stdin.pipe(writableStream)
How to get data from a readable stream (How to get data from a readable stream)

How do we read data from a readable stream? Using a writable stream:

How do we read data from a readable stream? Using a writable stream:

const Stream = require('stream')
const readableStream = new Stream.Readable()const writableStream = new Stream.Writable()
writableStream._write = (chunk, encoding, next) => {    console.log(chunk.toString())    next()}
readableStream.pipe(writableStream)
readableStream.push('hi!')readableStream.push('ho!')

You can also consume a readable stream directly, using the readable event:

You can also consume a readable stream directly, using the readable event:

readableStream.on('readable', () => {  console.log(readableStream.read())})
How to send data to a writable stream (How to send data to a writable stream)

Using the stream write() method:

Using the stream write() method:

writableStream.write('hey!\n')
Signaling a writable stream that you ended writing (Signaling a writable stream that you ended writing)

Use the end() method:

Use the end() method:

const Stream = require('stream')
const readableStream = new Stream.Readable()const writableStream = new Stream.Writable()
writableStream._write = (chunk, encoding, next) => {    console.log(chunk.toString())    next()}
readableStream.pipe(writableStream)
readableStream.push('hi!')readableStream.push('ho!')
writableStream.end()

The basics of working with MySQL and Node.js (The basics of working with MySQL and Node.js)

MySQL is one of the most popular relational databases in the world.

MySQL is one of the most popular relational databases in the world.

The Node.js ecosystem has several different packages that allow you to interface with MySQL, store data, retrieve data, and so on.

The Node.js ecosystem has several different packages that allow you to interface with MySQL, store data, retrieve data, and so on.

We’ll use mysqljs/mysql, a package that has over 12,000 GitHub stars and has been around for years.

We'll use mysqljs/mysql , a package that has over 12,000 GitHub stars and has been around for years.

Installing the Node.js MySql package (Installing the Node.js MySql package)

You install it using:

You install it using:

npm install mysql
Initializing the connection to the database (Initializing the connection to the database)

You first include the package:

You first include the package:

const mysql = require('mysql')

and you create a connection:

and you create a connection:

const options = {  user: 'the_mysql_user_name',  password: 'the_mysql_user_password',  database: 'the_mysql_database_name'}const connection = mysql.createConnection(options)

You initiate a new connection by calling:

You initiate a new connection by calling:

connection.connect(err => {  if (err) {    console.error('An error occurred while connecting to the DB')    throw err  }})
The connection options (The connection options)

In the above example, the options object contained 3 options:

In the above example, the options object contained 3 options:

const options = {  user: 'the_mysql_user_name',  password: 'the_mysql_user_password',  database: 'the_mysql_database_name'}

There are many more you can use, including:

There are many more you can use, including:

  • host, the database hostname, defaults to localhost

    host , the database hostname, defaults to localhost

  • port, the MySQL server port number, defaults to 3306

    port , the MySQL server port number, defaults to 3306

  • socketPath, used to specify a unix socket instead of host and port

    socketPath , used to specify a unix socket instead of host and port

  • debug, by default disabled, can be used for debugging

    debug , by default disabled, can be used for debugging

  • trace, by default enabled, prints stack traces when errors occur

    trace , by default enabled, prints stack traces when errors occur

  • ssl, used to setup an SSL connection to the server (out of the scope of this tutorial)

    ssl , used to setup an SSL connection to the server (out of the scope of this tutorial)

Perform a SELECT query (Perform a SELECT query)

Now you are ready to perform an SQL query on the database. The query once executed will invoke a callback function which contains an eventual error, the results and the fields:

Now you are ready to perform an SQL query on the database. The query once executed will invoke a callback function which contains an eventual error, the results and the fields:

connection.query('SELECT * FROM todos', (error, todos, fields) => {  if (error) {    console.error('An error occurred while executing the query')    throw error  }  console.log(todos)})

You can pass in values which will be automatically escaped:

You can pass in values which will be automatically escaped:

const id = 223connection.query('SELECT * FROM todos WHERE id = ?', [id], (error, todos, fields) => {  if (error) {    console.error('An error occurred while executing the query')    throw error  }  console.log(todos)})

To pass multiple values, just put more elements in the array you pass as the second parameter:

To pass multiple values, just put more elements in the array you pass as the second parameter:

const id = 223const author = 'Flavio'connection.query('SELECT * FROM todos WHERE id = ? AND author = ?', [id, author], (error, todos, fields) => {  if (error) {    console.error('An error occurred while executing the query')    throw error  }  console.log(todos)})
Perform an INSERT query (Perform an INSERT query)

You can pass an object:

You can pass an object:

const todo = {  thing: 'Buy the milk'  author: 'Flavio'}connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => {  if (error) {    console.error('An error occurred while executing the query')    throw error  }})

If the table has a primary key with auto_increment, the value of that will be returned in the results.insertIdvalue:

If the table has a primary key with auto_increment , the value of that will be returned in the results.insertId value:

const todo = {  thing: 'Buy the milk'  author: 'Flavio'}connection.query('INSERT INTO todos SET ?', todo, (error, results, fields) => {  if (error) {    console.error('An error occurred while executing the query')    throw error  }}  const id = results.resultId  console.log(id))
Close the connection (Close the connection)

When you need to terminate the connection to the database you can call the end() method:

When you need to terminate the connection to the database you can call the end() method:

connection.end()

This makes sure any pending query gets sent, and the connection is gracefully terminated.

This makes sure any pending query gets sent, and the connection is gracefully terminated.

The difference between development and production (The difference between development and production)

You can have different configurations for production and development environments.

You can have different configurations for production and development environments.

Node.js assumes it’s always running in a development environment. You can signal Node.js that you are running in production by setting the NODE_ENV=productionenvironment variable.

Node.js assumes it's always running in a development environment. You can signal Node.js that you are running in production by setting the NODE_ENV=production environment variable.

This is usually done by executing the command:

This is usually done by executing the command:

export NODE_ENV=production

in the shell, but it’s better to put it in your shell configuration file (like .bash_profile with the Bash shell) because otherwise the setting does not persist in case of a system restart.

in the shell, but it's better to put it in your shell configuration file (like .bash_profile with the Bash shell) because otherwise the setting does not persist in case of a system restart.

You can also apply the environment variable by prepending it to your application initialization command:

You can also apply the environment variable by prepending it to your application initialization command:

NODE_ENV=production node app.js

This environment variable is a convention that is widely used in external libraries as well.

This environment variable is a convention that is widely used in external libraries as well.

Setting the environment to production generally ensures that:

Setting the environment to production generally ensures that:

  • logging is kept to a minimum, essential level

    logging is kept to a minimum, essential level
  • more caching levels take place to optimize performance

    more caching levels take place to optimize performance

For example Pug, the templating library used by Express, compiles in debug mode if NODE_ENV is not set to production. Express views are compiled in every request in development mode, while in production they are cached. There are many more examples.

For example Pug , the templating library used by Express, compiles in debug mode if NODE_ENV is not set to production . Express views are compiled in every request in development mode, while in production they are cached. There are many more examples.

Express provides configuration hooks specific to the environment, which are automatically called based on the NODE_ENV variable value:

Express provides configuration hooks specific to the environment, which are automatically called based on the NODE_ENV variable value:

app.configure('development', () => {  //...})app.configure('production', () => {  //...})app.configure('production', 'staging', () => {  //...})

For example you can use this to set different error handlers for different modes:

For example you can use this to set different error handlers for different modes:

app.configure('development', () => {  app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));})
app.configure('production', () => {  app.use(express.errorHandler())})

结束语 (Closing words)

I hope this introduction to Node.js will help you get started using it, or help you grasp some of its concepts. And hopefully now you’ll know enough to start building some great things!

I hope this introduction to Node.js will help you get started using it, or help you grasp some of its concepts. And hopefully now you'll know enough to start building some great things!

翻译自: https://www.freecodecamp.org/news/the-definitive-node-js-handbook-6912378afc6e/

node.js使用手册

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值