二:用 JavaScript 写后端——Node.js 入门

一、Node.js 到底是什么

上一篇我们写了一段简短的代码,用 Node.js 启动了一个 HTTP 服务,浏览器访问 http://localhost:3000 就能看到一段文字。

但你可能会有一个疑问:JavaScript 不是浏览器里跑的吗?为什么在终端里敲 node app.js 也能跑 JavaScript?

要回答这个问题,需要理解一个概念:运行时

编程语言本身只是一套语法规范,需要一个程序来真正“执行”它。浏览器就是一个 JavaScript 的运行时——它内置了 JS 引擎(Chrome 用的是 V8 引擎),负责解析和执行 JavaScript 代码。同时,浏览器还提供了很多 JS 可以调用的能力,比如操作 DOM、发送网络请求、处理定时器等。

Node.js 是另一个运行时。它把 Chrome 的 V8 引擎单独拿出来,加上了一套服务器端需要的 API——比如读写文件、创建 HTTP 服务、操作数据库。所以,JavaScript 代码在 Node.js 里能做的事情和浏览器里完全不同:

  • 浏览器里的 JS:操作页面 DOM、监听点击事件、alert 弹窗、localStorage
  • Node.js 里的 JS:读写文件、创建 HTTP 服务、连接数据库、执行系统命令。

同一个 JavaScript 语言,两个不同的运行环境,两套不同的能力。

这也是为什么你用 JS 写前端已经会了语法、变量、函数、对象,现在学后端只需要学“在 Node.js 环境下能做什么新的事情”,而不需要重新学一门语言。

二、安装 Node.js

如果你还没装,现在装。

第一步:下载

打开浏览器,访问 https://nodejs.org。你会看到两个版本:

  • LTS(Long Term Support,长期支持版):稳定可靠,适合学习和生产。推荐装这个。
  • Current(最新版):功能最新,但可能有未发现的 bug。新手不用选这个。

点击左边的 LTS 按钮,下载适合你操作系统的安装包。

第二步:安装

双击下载好的安装包,一路点“下一步”,全部用默认选项即可。Mac 用户拖动 Node.js 图标到 Applications 文件夹。

第三步:验证安装是否成功

安装完成后,打开终端:

  • Windows:按键盘上的 Win + R,输入 cmd,回车。黑色窗口就是终端。
  • Mac:在“启动台”里找到“终端”应用,或者按 Cmd + 空格 搜索“终端”。

在终端里输入以下命令并回车:

node -v

如果出现类似 v20.10.0v18.17.0 这样的版本号,说明 Node.js 安装成功。

再输入:

npm -v

npm 是 Node.js 自带的包管理工具,用来安装第三方的代码库(就像手机上的应用商店)。如果出现版本号,说明 npm 也正常。

三、写出你的第一个 Node.js 程序

现在我们来正式写第一段后端代码,并逐行拆解。

第 1 步:新建文件

在电脑上任意地方新建一个文件夹,比如桌面上新建一个叫 backend-learn 的文件夹。

用 VS Code 打开这个文件夹(可以直接把文件夹拖到 VS Code 图标上)。

在 VS Code 左侧文件列表里,右键 → 新建文件,命名为 app.js

第 2 步:写代码

app.js 里输入以下代码:

const http = require('http');

const server = http.createServer(function (request, response) {
  response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  response.end('你好,这是你的第一个后端程序!');
});

server.listen(3000, function () {
  console.log('服务已启动,在浏览器中访问 http://localhost:3000');
});

第 3 步:运行

在 VS Code 里按 Ctrl + `(注意是键盘左上角数字 1 左边的那个键),打开 VS Code 内置的终端。

在终端里输入:

node app.js

终端会显示:

服务已启动,在浏览器中访问 http://localhost:3000

这时候不要关闭终端。终端里的这个程序正在持续运行,等待浏览器的请求。

第 4 步:访问

打开浏览器,在地址栏输入 http://localhost:3000,回车。

你会看到页面显示:你好,这是你的第一个后端程序!

这就是你的第一个后端程序。虽然只有 7 行代码,但它是一个货真价实的 HTTP 服务器。

四、逐行拆解这段代码

现在一行一行来。这是本篇最核心的部分,仔细看。

第 1 行:const http = require('http');

  • require 是 Node.js 用来加载模块的函数。模块就是别人写好的、封装了特定功能的代码包。
  • 'http' 是 Node.js 内置的模块,专门用来创建 HTTP 服务和处理 HTTP 请求。因为它是内置的,所以不需要额外安装,直接 require 就能用。
  • const http 把加载进来的 http 模块赋值给一个常量 http。之后要使用 http 模块的功能,就通过这个 http 变量来调用。
  • 整行翻译:“帮我把 Node.js 内置的 http 模块加载进来,存到 http 这个变量里,后面要用。”

第 2 行:空行

这是一行空行,纯粹为了让代码看起来不拥挤。没有实际功能。

第 3 行:const server = http.createServer(function (request, response) {

这行比较长,拆开来看:

  • http.createServer() 是 http 模块提供的一个方法,用来创建一个 HTTP 服务对象。调用之后,你就有了一个服务对象,可以监听端口、接收请求、返回响应。
  • function (request, response) { ... } 是一个回调函数。这个函数会在每次有请求进来的时候被自动调用。不请求就不调用,来一次请求就调用一次。
  • request:这个参数包含了请求的所有信息——谁发的、请求的路径是什么、带了什么参数、用了什么方法。
  • response:这个参数是一个对象,用来构建你要返回给客户端的响应——设置状态码、设置响应头、写响应体。
  • const server:把创建好的服务对象存到 server 变量里,后面要告诉它监听哪个端口。
  • 整行翻译:“创建一个 HTTP 服务。每次有人发请求过来,就执行这个回调函数,把请求信息放在 request 里,把用来构建响应的工具放在 response 里。服务对象存到 server 变量里。”

第 4 行:response.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });

  • response.writeHead() 用来设置响应头。响应头就是告诉浏览器“我返回给你的东西是什么类型、有多少字节、状态怎么样”等描述性信息。
  • 200:HTTP 状态码。200 的意思是“成功,一切正常”。浏览器看到 200 就知道“请求被正常处理了,返回的数据可以用”。
  • 'Content-Type':告诉浏览器“我返回给你的内容是什么类型”。这里设为 'text/plain; charset=utf-8',意思是“纯文本,字符编码是 UTF-8(支持中文)”。
  • 为什么要有 Content-Type:如果不告诉浏览器内容类型,浏览器可能把 HTML 代码当纯文本显示,或者把图片数据当乱码显示。我们这里返回的是纯文本,所以设 text/plain。如果返回的是网页(HTML),应该设 text/html。如果返回的是 JSON 数据,应该设 application/json
  • 整行翻译:“把响应状态设为 200(成功),内容类型设为纯文本(支持中文)。”

第 5 行:response.end('你好,这是你的第一个后端程序!');

  • response.end() 做了两件事:第一,把括号里的内容(这里是那段中文字符串)写入响应体,作为返回给浏览器的实际内容;第二,结束本次响应,告诉服务器“我写完了,你可以发送给客户端了”。
  • 注意end() 必须调用,否则浏览器会一直等,直到超时。每个请求必须对应一次 end()
  • 整行翻译:“把我给你的这段文字返回给浏览器,然后结束本次响应。”

第 6 行:});

这是第 3 行 http.createServer(function (request, response) { 的闭合花括号和括号。表示回调函数定义结束,createServer 方法调用结束。

第 7 行:空行

同样是为了让代码看起来有呼吸感。

第 8 行:server.listen(3000, function () {

  • server.listen() 是服务对象的一个方法,用来让服务开始监听某个端口,等待请求进来。
  • 3000:端口号。一台服务器上可以同时运行很多个服务——Web 服务、邮件服务、数据库服务——端口号就是用来区分它们的。你可以把端口想象成快递柜的格子编号:快递员(客户端)送到某个格子号(端口号),对应的服务就在那个格子里取件。常见默认端口:HTTP 网站通常用 80 端口,HTTPS 用 443,MySQL 数据库用 3306。这里我们用 3000,因为它没有被其他软件占用,且是 Node.js 教程的惯例。
  • function () { ... } 是一个回调函数,在服务成功启动并开始监听后执行一次。用于通知你“服务已经准备好了”。
  • 整行翻译:“服务准备好了之后,在 3000 端口上等待请求,启动成功后执行回调函数通知我。”

第 9 行:console.log('服务已启动,在浏览器中访问 http://localhost:3000');

  • console.log() 在 Node.js 里的用法和浏览器里一样——在终端打印一行文字。
  • localhost:这是你电脑自己的网络名。无论你有没有联网,localhost 都指向你正在使用的这台电脑。访问 localhost 就等于访问你自己的电脑。当你开发一个后端程序时,你就是用自己的电脑模拟服务器,所以通过 localhost 来测试。
  • 3000:端口号,和上面 listen 里设置的一致。
  • 整行翻译:“在终端打印一行提示,告诉开发者服务已经启动,可以去浏览器访问了。”

第 10 行:});

闭合第 8 行的函数体和 listen 调用。

五、你可能会遇到的两个问题

问题一:终端里光标一直在闪,什么也没发生?

这是正常的。当你执行 node app.js 后,Node.js 就开始持续运行了。它在等请求进来,不会自动退出。光标闪烁就是程序在运行的表现。只要不关终端,服务就一直活着。

要停止服务,在终端里按 Ctrl + C(Mac 也是 Control + C)。这会终止正在运行的程序,终端光标回到正常的可输入状态。

每当你修改了 app.js 的代码,都需要先 Ctrl + C 停止服务,再重新执行 node app.js,修改的代码才会生效。

问题二:端口被占用了?

如果你忘了停止上一次启动的服务,又开了一个新终端执行 node app.js,可能会看到类似 Error: listen EADDRINUSE :::3000 的错误。意思是 3000 端口已经被占用了。

解决:找到上一次运行的终端窗口,按 Ctrl + C 停掉它。或者换一个端口号,比如把代码里的 3000 改成 3001,访问的时候也改成 http://localhost:3001

六、改造你的第一个程序

光是看一遍不够。现在请打开你的 app.js,做以下三次改造,每改一次就重启服务、刷新浏览器观察效果。这才是真正掌握的过程。

改造一:把返回的文字换成你自己的

response.end('欢迎来到我的后端世界!');

改造二:把端口号从 3000 改成 8080

server.listen(8080, function () {
  console.log('服务已启动,在浏览器中访问 http://localhost:8080');
});

注意:listen 里的端口号和 console.log 里的端口号必须一致。

改造三:返回一段 HTML 而不是纯文本

Content-Type 改成 text/htmlend 里的内容写成 HTML 标签:

response.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
response.end('<h1>你好</h1><p>这是一段 HTML 内容。</p>');

刷新浏览器,你会看到标题和段落被浏览器渲染出来了,而不是显示原始代码。因为你告诉浏览器“我返回的是 HTML”,浏览器就把这些标签解析成可视化的页面元素。

七、本篇小结

这一篇你学会了:

  • Node.js 是什么:一个把 Chrome V8 引擎拿出来、加上服务器端 API 的 JavaScript 运行时。
  • 运行时是什么:执行代码的程序环境。浏览器是一个运行时,Node.js 是另一个。
  • require 是什么:加载模块的函数。http 是 Node.js 内置模块,专门处理 HTTP。
  • createServer 的作用:创建一个 HTTP 服务对象,每次请求进来会执行回调函数。
  • requestresponse:前者包含请求信息,后者用来构建并发送响应。
  • writeHead:设置响应状态码和头部信息(如内容类型)。
  • end:写入响应体内容并结束响应。必须调用。
  • listen:让服务在指定端口上开始监听,等待请求。
  • localhost 和端口号localhost 就是你自己电脑,端口号用来区分同一台电脑上不同的服务。
  • Content-Type:告诉浏览器返回的是什么类型的内容,text/plain 是纯文本,text/html 是网页。

你现在已经能写一个可以返回文字、返回 HTML 的 HTTP 服务了。虽然简陋,但它包含了后端程序最核心的骨架。下一篇,我们会学模块系统——怎么把自己的代码拆成多个文件,以及 Node.js 内置的其他常用模块。

下一篇预告

下一篇——《模块系统——拆开你的代码》:require 的完整用法、module.exports 怎么导出自己的函数、内置模块 fs 怎么读写文件、path 怎么处理文件路径。你会写出第一个可以读取本地文件并返回内容的接口。

后端零基础入门,每周更新。

© 版权声明
THE END
喜欢就支持一下吧
点赞10 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容