开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 20 天,点击查看活动详情
Node.js是一种开源、跨平台的 JavaScript 运行时环境,可在 Web 浏览器之外执行 JavaScript 代码。Node.js 是一种流行的、适合初学者的轻量级 Web 框架,许多大公司都在使用它,例如 Netflix 和 Uber。
当我们通常想到 JavaScript 时,我们的思绪往往会转到 Web 开发。在 Node.js 出现之前,真的没有办法在浏览器之外运行 JavaScript。当我们编写后端服务器和数据库时,Node.js 是一个流行的选择,因为我们可以将我们的代码作为一个独立的应用程序运行,而不是只能在浏览器环境中评估的东西。
Node.js 是任何 JavaScript 开发人员都需要了解的重要工具。所以,今天,我们将向您介绍 Node.js,并向您展示如何开始一个项目。
什么是 Node.js?
Node.js 是一种开源、跨平台的 JavaScript 运行时环境,用于在 Web 浏览器之外执行 JavaScript 代码。
Node.js 对于初学者来说是一个很棒的 Web 框架,因为它非常适合数据密集型应用程序,例如流媒体和实时应用程序,并且 Node.js 可以轻松开始构建后端。
Node.js 允许我们在任何地方和任何浏览器上使用 JavaScript,包括 macOS、Linux 和 Windows。当我们说无处不在时,我们指的是前端、中间件和后端。因此,Node.js 是一些非常流行的 Web 开发堆栈的一部分,例如 MERN 堆栈、MEVN 堆栈和 MEAN 堆栈。
有许多特性使 Node.js 成为现在的样子:
- Google Chrome V8 JavaScript 引擎: 此运行时环境基于 Google Chrome V8 JavaScript 运行时引擎构建。同样,Java 虚拟机翻译字节码,而 Chrome V8 JavaScript 引擎采用 JavaScript 并使其可读。
- 模块/包: Node.js 有 npm,一个节点包管理器,拥有超过 350,000 个包的库,可帮助您高效轻松地启动项目或应用程序。
- 事件驱动的单线程 I/O 模型: JavaScript 依赖于用户交互或事件来运行。在大多数情况下,代码是同步运行的。服务器请求和其他此类异步任务依赖于承诺系统或异步/等待函数来处理这些输入和输出。
Node.js 基础知识
现在我们知道了 Node.js 是什么,让我们来探索这个工具的基础知识。
控制台
控制台是 Node.js 提供的一个模块,当您检查网页时 , 它类似于浏览器中的 JavaScript 控制台。控制台具有可供我们用于调试目的的方法。
console.log():经常用于记录某种输出。console.warn():明确向控制台发出警告。console.error(): 明确地向控制台发送一条错误消息。您可以将错误记录为字符串或对象。如果记录为新的Error(),回溯将作为消息的一部分包含在内。console.trace():在代码中发生错误时记录回溯。给出可能发生错误的文件的行号和列号。
Buffer
Node.js 中的Buffer 类的核心是文件系统的临时存储解决方案。由于其低级特性,作为 Web 开发人员,我们很少会真正直接使用 Buffer 类。这个类的主要目的是分配内存。
下面我们就来看看Buffer类提供的几个方法。
const buf1 = Buffer.alloc(10);
console.log(buf1);
const buf2 = Buffer.alloc(5, 15);
console.log(buf2);
const buf3 = Buffer.allocUnsafe(10);
console.log(buf3);
buf3.fill(1);
console.log(buf3);
buf2.write("abcedf");
console.log(buf2);
const buf4 = Buffer.from([265, 6.5, -255, '7']);
console.log(buf4);
const buf5 = Buffer.from('Hello world');
console.log(buf5);
console.log(buf5.toString());
文件系统
文件系统 (fs) 模块允许我们与 Node.js 中的文件进行交互。有同步和异步方法可用于使用 fs 模块读取或写入文件。与使用控制台或 Buffer 类相比,我们需要将 fs 模块导入到我们想要使用的文件中才能使其工作。
下面的代码示例显示了readFile异步方法的工作原理。请注意,该方法中的最后一个参数是一个回调函数,其第一个参数是一个错误。根据定义,此回调将始终在传递数据之前先传递错误。
异步示例:
该readFile方法的异步特性不会阻止其他函数或代码行运行。
但是,同步方法readFileSync会阻止其他代码行运行,直到文件读取完毕。
这是一个例子:
const fs = require('fs')
fs.readFile('data.txt', 'utf-8', (err, data) => {
if (err) {
console.error(err)
return
}
let array = data.split('\n')
//props are id, first_name, last_name, email, gender and ip_address
let mapped = array.map(person => {
let new_person = person.split(',');
return new Object({
id: new_person[0],
first_name: new_person[1],
last_name: new_person[2],
email: new_person[3],
gender: new_person[4],
ip: new_person[5]
})
});
console.log(mapped)
});
console.log("Hit!")
同步示例:
const fs = require('fs')
try {
const data = fs.readFileSync('data.txt', 'utf-8')
let array = data.split('\n')
//props are id, first_name, last_name, email, gender and ip_address
let mapped = array.map(person => {
let new_person = person.split(',');
return new Object({
id: new_person[0],
first_name: new_person[1],
last_name: new_person[2],
email: new_person[3],
gender: new_person[4],
ip: new_person[5]
})
});
console.log(mapped)
} catch (err) {
console.error(err)
}
console.log("Hit!")
数据.txt:
1,Annabell,Cicconetti,acicconetti0@example.com,Female,219.207.16.2
2,Silvana,Glasman,sglasman1@house.gov,Female,233.214.135.18
3,Rebeka,Redmile,rredmile2@utexas.edu,Female,121.106.165.14
4,Veriee,Jovis,vjovis3@japanpost.jp,Female,43.217.63.173
5,Melitta,Hanburry,mhanburry4@aboutads.info,Female,42.204.168.27
6,Alethea,Webben,awebben5@hatena.ne.jp,Female,189.126.43.49
7,Saxe,Leary,sleary6@behance.net,Male,95.156.25.21
8,Ario,Brabyn,abrabyn7@pcworld.com,Male,220.226.164.176
9,Marybeth,Ughelli,mughelli8@mit.edu,Female,251.234.218.207
10,Gothart,Tassell,gtassell9@freewebs.com,Male,247.146.121.230
11,Fairlie,Beevis,fbeevisa@stanford.edu,Male,161.219.190.148
12,Skippie,Station,sstationb@wikipedia.org,Male,178.184.167.113
13,Ashla,Tett,atettc@nature.com,Female,209.125.39.161
14,Belinda,Olin,bolind@discovery.com,Female,222.234.181.134
15,Laurianne,Ledgerton,lledgertone@google.co.uk,Female,184.56.226.2
16,Angele,Rhodus,arhodusf@ca.gov,Female,112.66.128.23
17,Meridith,Pena,mpenag@biblegateway.com,Female,163.227.38.120
18,Romola,Erbe,rerbeh@networksolutions.com,Female,184.50.183.25
19,Damien,Cominotti,dcominottii@naver.com,Male,122.62.139.51
20,Lou,Clemerson,lclemersonj@sfgate.com,Female,176.117.18.82
21,Donall,Lorence,dlorencek@shutterfly.com,Male,153.209.179.90
22,Maribeth,Sloam,msloaml@cbslocal.com,Female,177.119.164.156
23,Fowler,Pethybridge,fpethybridgem@vinaora.com,Male,58.228.162.249
24,Jarred,Haxley,jhaxleyn@ox.ac.uk,Male,26.74.46.200
25,Natalie,Outright,noutrighto@businessinsider.com,Female,181.218.16.217
26,Cloe,Devitt,cdevittp@gmpg.org,Female,109.68.184.211
27,Shane,Farmer,sfarmerq@ucsd.edu,Male,198.230.29.69
28,Iorgo,Thrower,ithrowerr@nsw.gov.au,Male,103.213.212.70
29,Letty,Dakhno,ldakhnos@pagesperso-orange.fr,Female,32.245.196.9
30,Woodrow,Flageul,wflageult@nsw.gov.au,Male,105.129.139.220
31,Franchot,Large,flargeu@statcounter.com,Male,219.98.60.51
32,Anna-diana,Callam,acallamv@51.la,Female,59.121.52.69
33,Benoit,Scallon,bscallonw@etsy.com,Male,227.53.63.103
34,Lavinie,Lovelady,lloveladyx@constantcontact.com,Female,227.20.131.192
35,Timoteo,Laurentino,tlaurentinoy@techcrunch.com,Male,149.251.44.30
36,Robers,Cassella,rcassellaz@google.pl,Male,179.219.60.199
37,Arnoldo,Eakly,aeakly10@live.com,Male,189.110.238.26
38,Janis,Didball,jdidball11@shinystat.com,Female,105.74.199.165
console.log(“Hit!”)尝试在函数之后放置一个以查看实际记录控制台的时间。这两个功能之间有区别吗?它是什么?
该readFile函数将启动函数,然后在函数完成并打印出数组内容之前立即移动到其余代码。所以,你应该看到这样的东西:
Hit!
[
{
id: '1',
first_name: 'Annabell',
last_name: 'Cicconetti',
email: 'acicconetti0@example.com',
gender: 'Female',
ip: '219.207.16.2'
},
etc…
]
相反,console.log(“Hit!”)直到readFileSync函数完成后才会运行:
[...{
id: '37',
first_name: 'Arnoldo',
last_name: 'Eakly',
email: 'aeakly10@live.com',
gender: 'Male',
ip: '189.110.238.26'
},
{
id: '38',
first_name: 'Janis',
last_name: 'Didball',
email: 'jdidball11@shinystat.com',
gender: 'Female',
ip: '105.74.199.165'
}
]
Hit!
写入文件的方式非常相似,但调用的函数是writeFile()和writeFileSync()。
事件循环
许多 Node.js 被构建为事件驱动的。当用户单击界面或在表单中键入时,将触发事件发生,然后发生某些结果。将一个函数或一组函数附加到特定事件就是发出一个事件。
这些函数称为事件侦听器,是称为事件循环的整个旅程的一部分。举个例子:
const EventEmitter = require('events');
const emitter = new EventEmitter();
const handleEvent = (str) => {
console.log(`================ ${str}`);
console.log("handleEvent fired! An event has happened!");
console.log("end of event")
}
emitter.on('load event', () => handleEvent("load"));
emitter.on('hello event', () => handleEvent("hello"));
emitter.emit('load event');
emitter.emit('hello event')
在此代码示例中,我们将导入事件模块。接下来,我们创建一个新的EventEmitter并将其分配给变量发射器。在这个特定的例子中,我们创建了一个handleEvent函数,它将作为一个回调函数,它将 console.log 一些东西。
该类EventEmitter有几个我们可以使用的方法。发出事件时运行的一种方法是函数EventEmitter.on()。
当发出事件时,将触发此特定方法。传递给函数的第一个参数是事件的名称,第二个参数是告诉我们如何处理事件的回调函数。
在此特定示例中,发出了两个事件。当我们运行上面的代码时,我们应该得到:
================ load
handleEvent fired! An event has happened!
end of event
================ hello
handleEvent fired! An event has happened!
end of event
true
emit 方法触发 on 方法,然后调用handleEvent回调函数。
全局变量
全局对象在每个模块中都可用,因此无需导入特定模块即可使用它们。类Buffer,例如 class 在 Node.js 中被定义为一个全局的。其他一些常见的全局对象是:
- 该
console对象用于打印到stdout和stderr。 - 定时器,例如
setImmediate、setInterval和setTimeout,也是全局变量。 - 该
process对象也是全局的。
在浏览器中,顶级范围是全局范围。但是在 Node.js 中,顶级作用域不是全局作用域。
在 Node.js 中,全局对象可用于查看全局范围内可用的内容。查看下面的代码以查看示例。
console.log(global)
//output:
Object [global] {
global: [Circular],
process:
process {
title: 'node',
version: 'v10.17.0',
versions:
{ http_parser: '2.8.0',
node: '10.17.0',
v8: '6.8.275.32-node.54',
uv: '1.28.0',
zlib: '1.2.11',
brotli: '1.0.7',
ares: '1.15.0',
modules: '64',
nghttp2: '1.39.2',
napi: '5',
openssl: '1.1.1d',
icu: '64.2',
unicode: '12.1',
cldr: '35.1',
tz: '2019a' },
arch: 'x64',
platform: 'linux',
release:
{ name: 'node',
lts: 'Dubnium',
sourceUrl:
'https://nodejs.org/download/release/v10.17.0/node-v10.17.0.tar.gz',
headersUrl:
'https://nodejs.org/download/release/v10.17.0/node-v10.17.0-headers.tar.gz' },
argv: [ '/usr/local/bin/node', '/usercode/index.js' ],
execArgv: [],
env:
{ HOSTNAME: '52b6928cb0cb',
HOME: '/root',
OLDPWD: '/',
PATH:
'/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin',
LANG: 'C.UTF-8',
NODE_PATH: '/usr/local/lib/node_modules/',
PWD: '/usercode' },
pid: 9,
features:
{ debug: false,
uv: true,
ipv6: true,
tls_alpn: true,
tls_sni: true,
tls_ocsp: true,
tls: true },
ppid: 8,
execPath: '/usr/local/bin/node',
debugPort: 9229,
_debugProcess: [Function: _debugProcess],
_debugEnd: [Function: _debugEnd],
_startProfilerIdleNotifier: [Function: _startProfilerIdleNotifier],
_stopProfilerIdleNotifier: [Function: _stopProfilerIdleNotifier],
abort: [Function: abort],
chdir: [Function: chdir],
umask: [Function: umask],
_getActiveRequests: [Function: _getActiveRequests],
_getActiveHandles: [Function: _getActiveHandles],
_kill: [Function: _kill],
cwd: [Function: cwd],
dlopen: [Function: dlopen],
reallyExit: [Function: reallyExit],
uptime: [Function: uptime],
getuid: [Function: getuid],
geteuid: [Function: geteuid],
getgid: [Function: getgid],
getegid: [Function: getegid],
getgroups: [Function: getgroups],
_rawDebug: [Function],
moduleLoadList:
[ 'Internal Binding module_wrap',
'Binding contextify',
'Internal Binding worker',
'NativeModule events',
'NativeModule internal/async_hooks',
'NativeModule internal/errors',
'Binding uv',
'Binding buffer',
'Binding async_wrap',
'Internal Binding async_wrap',
'Binding config',
'Binding icu',
'NativeModule util',
'NativeModule internal/util/inspect',
'Binding util',
'NativeModule internal/util',
'Binding constants',
'Internal Binding types',
'NativeModule internal/util/types',
'NativeModule internal/validators',
'NativeModule internal/encoding',
'Internal Binding icu',
'NativeModule buffer',
'NativeModule internal/buffer',
'NativeModule internal/process/per_thread',
'NativeModule internal/process/main_thread_only',
'NativeModule internal/process/stdio',
'NativeModule assert',
'NativeModule internal/assert',
'NativeModule fs',
'NativeModule path',
'NativeModule internal/constants',
'Binding fs',
'NativeModule internal/fs/streams',
'NativeModule internal/fs/utils',
'NativeModule stream',
'NativeModule internal/streams/pipeline',
'NativeModule internal/streams/end-of-stream',
'NativeModule internal/streams/legacy',
'NativeModule _stream_readable',
'NativeModule internal/streams/buffer_list',
'NativeModule internal/streams/destroy',
'NativeModule internal/streams/state',
'NativeModule _stream_writable',
'NativeModule _stream_duplex',
'NativeModule _stream_transform',
'NativeModule _stream_passthrough',
'NativeModule internal/url',
'NativeModule internal/querystring',
'Binding url',
'NativeModule internal/process/warning',
'NativeModule internal/process/next_tick',
'NativeModule internal/process/promises',
'Internal Binding util',
'NativeModule internal/fixed_queue',
'Binding performance',
'Binding trace_events',
'NativeModule internal/inspector_async_hook',
'Binding inspector',
'NativeModule internal/options',
'Internal Binding options',
'NativeModule timers',
'Binding timer_wrap',
'NativeModule internal/linkedlist',
'NativeModule internal/timers',
'NativeModule console',
'Binding tty_wrap',
'Internal Binding tty_wrap',
'NativeModule internal/fs/sync_write_stream',
'NativeModule internal/modules/cjs/loader',
'NativeModule vm',
'NativeModule internal/modules/cjs/helpers',
'NativeModule url',
'NativeModule internal/safe_globals',
'Internal Binding contextify' ],
binding: [Function: binding],
_linkedBinding: [Function: _linkedBinding],
_events:
[Object: null prototype] {
newListener: [Function],
removeListener: [Function],
warning: [Function] },
_eventsCount: 3,
_maxListeners: undefined,
_fatalException: [Function],
domain: null,
_exiting: false,
assert: [Function: deprecated],
config: { target_defaults: [Object], variables: [Object] },
setUncaughtExceptionCaptureCallback: [Function],
hasUncaughtExceptionCaptureCallback: [Function],
emitWarning: [Function],
nextTick: [Function: nextTick],
_tickCallback: [Function: _tickCallback],
stdout: [Getter],
stderr: [Getter],
stdin: [Getter],
openStdin: [Function],
initgroups: [Function: initgroups],
setegid: [Function: setegid],
seteuid: [Function: seteuid],
setgid: [Function: setgid],
setuid: [Function: setuid],
setgroups: [Function: setgroups],
hrtime: { [Function: hrtime] bigint: [Function] },
cpuUsage: [Function: cpuUsage],
memoryUsage: [Function: memoryUsage],
exit: [Function],
kill: [Function],
argv0: 'node',
allowedNodeEnvironmentFlags: [Getter/Setter],
mainModule:
Module {
id: '.',
exports: {},
parent: null,
filename: '/usercode/index.js',
loaded: false,
children: [],
paths: [Array] } },
Buffer:
{ [Function: Buffer]
poolSize: 8192,
from: [Function: from],
of: [Function: of],
alloc: [Function: alloc],
allocUnsafe: [Function: allocUnsafe],
allocUnsafeSlow: [Function: allocUnsafeSlow],
isBuffer: [Function: isBuffer],
compare: [Function: compare],
isEncoding: [Function: isEncoding],
concat: [Function: concat],
byteLength: [Function: byteLength],
[Symbol(kIsEncodingSymbol)]: [Function: isEncoding] },
clearImmediate: [Function: clearImmediate],
clearInterval: [Function: clearInterval],
clearTimeout: [Function: clearTimeout],
setImmediate:
{ [Function: setImmediate] [Symbol(util.promisify.custom)]: [Function] },
setInterval: [Function: setInterval],
setTimeout:
{ [Function: setTimeout] [Symbol(util.promisify.custom)]: [Function] } }
如何构建一个基本的 Node.js 项目
让我们通过创建一个简单的 Node.js 文件来学习如何开始使用 Node.js。在这个例子中,我们将设置我们的计算机作为服务器工作!
安装 Node.js 和 NPM
首先,您需要转到站点Node.js 站点并下载文件。
按照安装提示重新启动机器以获得最佳效果。
另一种安装 Node.js 的方法是使用包管理器。
然后,通过使用以下命令打印版本来测试它是否正常工作:
> node -v
您还应该通过使用以下命令打印版本来测试 npm:
> npm -v
创建一个文件
正确安装 Node.js 后,创建一个 Node.js 文件。在此示例中,我们将其命名为“first.js”。然后我们添加以下代码并将文件保存在您的计算机上,如下所示:C:\Users\Your Name\first.js
var http = require('http');
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/html'});
res.end('Hello World!');
}).listen(8080);
这段代码本质上是告诉计算机打印“Hello World!” 在端口 8080 上访问时。
命令行界面
Node.js 文件必须在您计算机的“命令行界面”程序中启动。导航到包含文件“first.js”的文件夹。
C:\Users\Your Name>_
启动你的文件
然后需要由 Node.js 启动此文件。通过启动命令行界面、编写node first.js并单击 enter 来执行此操作:
C:\Users\Your Name>node myfirst.js
惊人的!现在您的计算机已设置为服务器,因此当您访问计算机的 8080 端口时,“Hello World!” 将打印消息。
要实时查看,请打开浏览器并输入:http://localhost:8080