Skip to content

Node.js 入门与 fs 模块 ​

本页对应 Node.js 入门与 fs 模块,共 8 段视频,约 1 小时 35 分。

学习目标 ​

  • 能够完成 Node.js 安装,并通过 node -v 验证版本,使用 node 文件名.js 在终端执行 JavaScript 文件。
  • 能够使用 fs 模块的 writeFile 和 readFile 完成文件写入与读取,并将读取到的 Buffer 数据转换为字符串。
  • 能够使用 path.join 配合 __dirname 拼接绝对路径,解决 Node.js 中相对路径依赖终端工作目录的问题。
  • 能够编写 Node.js 脚本读取 HTML/JS 文件,使用正则替换换行符、回车符及 console.log 语句,并输出压缩后的 HTML 文件。
  • 能够说明 URL 中端口号的作用及有效范围,并识别 Web 服务常用端口。
  • 能够使用 http 模块创建并启动 Web 服务,监听请求返回响应,并通过设置 Content-Type 响应头使中文字符正常显示。

知识点 ​

自测一:相对路径要先找“基准目录” ​

从 index.js 出发访问 student/data.json 的目录示意图

为什么需要:项目里文件一多,路径写错就会出现“找不到文件”。相对路径不是凭感觉写 ./ 或 ../,而是要先确定“从哪个文件、哪个目录出发”。本题要求从 index.js 出发访问目标 JSON,笔记给出的正确项是 A:../public/teacher/data.json。

怎么想:先看图中 index.js 所在目录,再判断目标文件相对它是“上一级再进入 public”还是“当前目录下进入 public”。./ 表示当前目录,../ 表示上一级目录。按笔记答案,应先回到上一级,再进入 public/teacher/data.json。这里要注意:题干文字与选项里的文件名存在差异,实际写路径时要以图中目录和你要访问的真实文件名为准,不能只背选项。

怎么做:在代码中写路径前,先画一遍目录层级:当前文件 → 上一级 → public → teacher → data.json。若在 Node.js 的 fs 读写中使用相对路径,还要记住转写稿里的调试结论:Node 执行代码中的相对路径是以“终端所在目录”为起点,不是想当然地从当前 JS 文件出发;因此视频三中写 ../test.txt 后,终端路径一变就报“no such file or directory”。更稳妥的做法是用 __dirname 拿到当前模块所在目录的绝对路径,再用 path.join(__dirname, '..', 'public', 'teacher', 'data.json') 拼接。

易错点:把 ./ 和 ../ 混淆;只看题干不看图;在 Node 中把“当前文件相对路径”和“终端工作目录”混为一谈。笔记答案是 A。

自测二:replace 加 g 才会全部替换 ​

js
const str = `我们都是好人,好人就是我`
const result = str.replace(/好/g, '')

为什么需要:处理字符串时经常要删除或替换指定字符,比如转写稿里压缩 HTML 要删除换行符、回车符,压缩 JS 要删除 console.log 语句。理解 replace 的匹配范围,才能避免“只替换了一处”。

怎么想:replace(匹配规则, 替换内容) 会用第二个参数替换第一个参数匹配到的内容。/好/g 中 g 表示全局匹配,因此字符串里所有“好”都会被替换成空字符串。原串是“我们都是好人,好人就是我”,删除两个“好”后得到“我们都是人,人就是我”,所以选 B。

怎么做:先写正则,再打印 result 确认结果;同时检查原 str 是否变化。replace 返回新字符串,不会原地修改原字符串。转写稿视频五中删除 console.log 时也反复打印替换结果,发现贪婪匹配吞掉了中间代码,后来在量词后加 ? 改成非贪婪,这说明正则替换一定要“先小范围验证,再全局处理”。

易错点:不加 g 只替换第一个“好”;误以为 replace 会改变原字符串;正则中特殊字符忘记转义。笔记答案是 B。

自测三:匹配小括号要转义 ​

为什么需要:正则里的 (、) 有特殊含义,表示分组。如果想匹配字符串里真实的小括号,就必须告诉正则“这里不是分组,而是普通字符”。本题问哪个正则能匹配字符串中的小括号,笔记答案是 B:/\(/。

怎么想:/(/ 中的左括号会被正则引擎当作分组开始,而不是去匹配字符“(”。加反斜杠后,\( 才表示字面量左括号。若要把一对小括号都匹配出来,应写成 /\(\)/。

怎么做:先明确你要匹配的是“分组语法”还是“字符本身”。匹配字符本身时,对正则元字符加 \ 转义。转写稿视频五删除 console.log(...) 时也强调:正则里直接写小括号是分组,要匹配代码里的左括号必须写成 \(,右括号写成 \)。

易错点:只写 /(/ 以为能匹配;只转义左括号却忘记右括号;在字符串里用 new RegExp 时还要注意反斜杠本身需要转义。笔记答案是 B。

自测四:startsWith 只判断“开头是否相同” ​

js
const str = '/api/hello?a=10&b=20'
const result = str.startsWith('/api/hello')

为什么需要:服务端经常要根据请求路径做判断,比如转写稿里创建 Web 服务、处理 localhost:3000 请求时,需要识别请求路径的前缀。startsWith 就是用来判断一个字符串是否以目标字符串开头。

怎么想:str.startsWith('/api/hello') 检查 str 的开头是不是 /api/hello。原串正是以 /api/hello 开头,后面的 ?a=10&b=20 不影响前缀判断,所以结果是 true,选 A。

怎么做:把目标前缀原样传入,注意大小写和字符完全一致。若只想判断“包含”而不是“开头”,不能用 startsWith;若要判断带查询参数的完整路径,也不能把查询串误当前缀。转写稿中浏览器访问 http://localhost:3000 时,服务端返回响应,本质上也是先收到请求再判断如何处理;实际项目里常先用前缀判断路由,再解析查询参数。

易错点:把 startsWith 当成 includes;传入 '/api/hello?' 导致结果变 false;忽略大小写敏感;忘记它返回布尔值而不是新字符串。笔记答案是 A。

本节目标:为什么上课前要先装好 Node.js ​

为什么需要:本节要提前准备 Node.js 环境。配套资料里已经给出安装程序和视频,课前装好,课上就能直接写代码、做案例,不必把时间花在等待安装和排错上。
怎么想:Node.js 是 JavaScript 的运行环境,基于 Chrome 的 V8 引擎封装,可以让 JS 脱离浏览器独立执行;后续学习前端工程化、读写文件、创建 Web 服务都要用它。
怎么做:打开配套资料文件夹,找到 Node.js 安装程序,按 PPT 和配套视频安装。转写稿中统一使用 16.19.0 版本,因为后续 Vue 项目对 Node.js 版本有要求,大家统一版本可以减少环境差异。
易错点:不要自行换成“最新版”或随意版本;不要装在中文路径下,否则 Node.js 执行时可能因编码转换找不到资源。

安装过程:默认下一步,但有两个关键选择 ​

为什么需要:安装程序里会出现协议、安装路径、功能组件等选项,选错可能影响后续使用。
怎么想:安装以“默认、统一、可复现”为原则。路径保持默认且不含中文;组件选择保持默认。
怎么做:双击安装程序,点击 Next;同意协议后继续 Next;安装路径保持默认,确认路径中没有中文;看到询问是否安装 C/C++、Python、Visual Studio 等依赖的界面时,不勾选,直接默认下一步;点击 Install 安装。若防火墙弹出提示,选择允许,让安装继续;完成后点击 Finish。
易错点:转写稿特别提醒,那个“某些功能需要 C 或 C++ 环境”的界面不要勾选,因为当前大部分学习功能不需要它;安装路径不要出现中文。

验证安装:为什么要用 CMD,以及怎么验证 ​

为什么需要:安装完成不等于一定能用,需要确认系统能找到 Node.js 程序。Node.js 没有图形界面,必须通过命令调度。
怎么想:用固定命令查看版本号,能输出版本号就说明环境可用。
怎么做:按 Windows 键打开开始菜单,搜索 CMD,打开“命令提示符”;输入下面的命令并回车:

bash
node -v

如果看到类似 v16.19.0 的版本号,就说明安装成功。
易错点:node 和 -v 之间要有空格;不要写成 node-v。如果命令无法识别,先确认安装是否完成,再重新打开终端;仍不行就按配套视频检查安装步骤,不要直接跳过后面的课程。

第一次运行 JS 文件:理解“终端路径”很重要 ​

为什么需要:装好 Node.js 后,要体验它独立执行 JS 文件的能力。
怎么想:浏览器里执行 JS 靠 V8 引擎;Node.js 也靠 V8 引擎,但没有页面,所以没有 document、window 这些浏览器对象,取而代之的是 fs、path、http 等模块。执行 JS 时要用终端调用 Node.js。
怎么做:在 VS Code 中打开目标 JS 文件所在文件夹,在文件上右键选择“在集成终端中打开”,然后输入:

bash
node 01.js

回车后即可看到 JS 文件中的打印结果。
易错点:终端要定位到目标文件所在目录;node 后面写的是要执行的文件名或相对路径。转写稿中强调,Node.js 代码中的相对路径是以终端所在目录为起点,而不是想当然地从当前文件出发,这一点在后续 fs、path 案例中会反复用到。

本节小结与检查清单 ​

为什么需要:把安装、验证、首次运行串起来,课上才能顺利进入后续内容。
怎么想:按“安装—验证—运行”三步检查,不把问题留到课堂。
怎么做:确认安装包版本为 16.19.0;确认安装路径无中文;确认没有勾选 C/C++ 等额外组件;在 CMD 中执行 node -v 能看到版本号;能在 VS Code 集成终端中用 node 文件名.js 执行代码。
易错点:安装完成后忘记重开终端;终端路径不对导致找不到文件;把 Node.js 当成浏览器环境,误用 document、window。本节先把环境准备好,后续再逐步学习 fs 读写文件、path 处理路径、http 创建 Web 服务等内容。

Node.js 安装与运行 ​

为什么需要:Node.js 是 JavaScript 的运行环境,能让 JS 脱离浏览器独立执行,从而编写后端程序,也支撑前端工程化,例如压缩、整合前端代码。
怎么想:浏览器能执行 JS 是因为 V8 引擎;Node.js 基于 V8 封装,所以能独立运行 JS。但 Node.js 没有 document、window、XHR,取而代之的是 fs、path、http 等模块。
怎么做:安装时默认下一步,不装中文路径,C/C++ 相关选项不勾;用 node -v 验证;在 VS Code 集成终端执行 node 文件名。

bash
node -v
node 01.js
js
console.log('hello world')
for (let i = 0; i < 3; i++) {
  console.log(6)
}

易错点:安装路径不要含中文;node 后要跟目标 JS 文件;终端所在目录会影响相对路径。
【笔记原图:Node.js 与浏览器执行 JS 的差异】

fs 模块读写文件 ​

为什么需要:前端工程化要读取本地源码、处理后写入新文件,fs 封装了与本机文件系统交互的方法。
怎么想:先 require('fs') 引入模块,再调用 writeFile 写入、readFile 读取;回调第一个参数是错误 err,读取结果 data 是 Buffer。
怎么做:

js
const fs = require('fs')

fs.writeFile('./test.txt', '欢迎使用fs模块读写文件的内容', (err) => {
  if (err) return console.log(err)
  console.log('写入成功')
})

fs.readFile('./test.txt', (err, data) => {
  if (err) return console.log(err)
  console.log(data.toString())
})

调试时,写入数组会报 TypeError,提示 data 必须是字符串或 Buffer;读取不存在的 test123.txt 会报“没有这样的文件或文件夹”。
易错点:写入内容建议为字符串;writeFile 会覆盖目标文件全部内容;readFile 得到 Buffer,要 toString() 才看到中文。

path 模块与 __dirname ​

为什么需要:Node.js 代码中的相对路径不是从当前文件出发,而是依赖终端启动目录,容易找不到文件;不同系统路径分隔符也不同。
怎么想:用 __dirname 获取当前模块所在目录的绝对路径,再用 path.join() 拼接,让 Node.js 自动适配 Windows/Mac 分隔符。
怎么做:

js
const fs = require('fs')
const path = require('path')

console.log(__dirname)
const filePath = path.join(__dirname, '../test.txt')
fs.readFile(filePath, (err, data) => {
  if (err) return console.log(err)
  console.log(data.toString())
})

调试过程:在 03/index.js 里写 ../test.txt,从代码目录执行 node 03/index.js 时,Node.js 按终端目录找文件而报错;改用 __dirname + path.join 后,即使切到 03 目录执行也能正确读取。
易错点:不要手写 \ 或 / 拼接;__dirname 是目录不是文件;相对路径不要误以为从当前 JS 文件出发。

案例:前端项目压缩 ​

为什么需要:压缩 HTML/JS 可减小体积、加快浏览器加载,体现前端工程化。
怎么想:读取源文件为字符串→用正则去掉换行符、回车符,JS 还要去掉 console.log→写入 dist 新文件;JS 代码要用 <script> 包裹后拼到 HTML 后。
怎么做:

js
const fs = require('fs')
const path = require('path')

const htmlPath = path.join(__dirname, 'public/index.html')
const jsPath = path.join(__dirname, 'public/index.js')
const outPath = path.join(__dirname, 'dist/index.html')

fs.readFile(htmlPath, (err, htmlData) => {
  if (err) return console.log(err)
  const htmlStr = htmlData.toString()

  fs.readFile(jsPath, (err, jsData) => {
    if (err) return console.log(err)
    const jsStr = jsData.toString()
      .replace(/[\r\n]/g, '')
      .replace(/console\.log\('.*?'\);?/g, '')

    const result = htmlStr + `<script>${jsStr}</script>`
    fs.writeFile(outPath, result, (err) => {
      if (err) return console.log(err)
      console.log('压缩成功')
    })
  })
})

调试中,console.log 正则若用贪婪匹配,会从第一句吞到很后面;在量词后加 ? 改为非贪婪,只匹配一句。
易错点:replace 全局替换要加 g;不要覆盖源码,写入新文件;注意正则转义和贪婪模式。

http 模块创建 Web 服务 ​

为什么需要:Web 服务用于向请求方返回资源,如字符串、接口数据、HTML 页面。URL 中端口号用于定位服务器上的不同服务程序。
怎么想:http.createServer() 创建服务对象→监听 request 事件→用 res.end() 结束响应→listen 监听端口。
怎么做:

js
const http = require('http')

const server = http.createServer()
server.on('request', (req, res) => {
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  res.end('你好,世界')
})
server.listen(3000, () => {
  console.log('Web服务已启动')
})

启动:node 08/index.js,浏览器访问 http://localhost:3000/。返回中文乱码时,加 Content-Type: text/html; charset=utf-8 后刷新正常;F12 网络面板可看响应头和响应体。
易错点:一次请求只能对应一次响应;改代码后要 Ctrl+C 停止旧进程再重启;端口避开 0–1023 等常用占用端口;本机域名是 localhost。
【笔记原图:URL 中协议、域名、端口号、资源路径】

省份列表接口与浏览时钟 ​

目录还列出“省份列表接口”和“浏览时钟”,但给定转写稿未包含这两个案例的具体推导和代码,因此这里不补写实现细节,避免编造。学习时仍沿用本节 http 模块思路:接口案例关注返回数据,时钟案例关注页面展示与刷新;具体代码以课堂素材为准。易错点仍是端口占用、响应头编码、一次请求一次响应,以及修改服务代码后重启进程。

1. Node.js的作用与使用 ​

为什么需要:浏览器能执行JS,是因为浏览器内核里有V8引擎;Node.js基于Chrome的V8引擎封装,因此可以脱离浏览器独立执行JS代码。学它既能编写后端接口程序,也能支撑前端工程化。

怎么想:先明确Node.js不是新的JS语法,仍遵守ECMAScript,数据类型、打印、定时器、Promise等都能用;区别是Node.js没有window、document、XHR,取而代之的是fs、path、http等模块。

怎么做:安装时统一使用转写稿指定的16.19.0版本,默认下一步;不要装在中文路径下;安装过程中出现需要C/C++、Python、Visual Studio的界面时不要勾选。安装后在CMD或VS Code集成终端验证:

bash
node -v

编写01.js后,在文件上右键“在集成终端中打开”,执行:

bash
node 01.js

易错点:Node.js没有图形界面,要靠终端命令调度;终端路径会影响后续相对路径查找。修改Node.js服务代码后,通常要Ctrl+C停止旧进程再重新执行。

2. 前端工程化:压缩与整合 ​

为什么需要:前端上线代码体积越小,浏览器加载通常越快。工程化可对HTML/JS做压缩、整合,把换行符、回车符、console.log等去掉,并把JS整合进HTML。

怎么想:压缩不是手删,而是用Node.js读文件→字符串替换→再写入新文件。修改源码仍改原文件,压缩产物放dist,浏览器运行压缩后的页面。

怎么做:用fs.readFile读取HTML/JS,用replace配合正则去掉\r、\n和打印语句;路径用path.join(__dirname, ...)拼绝对路径;JS内容外包<script>标签,再与HTML字符串整合,用fs.writeFile写入dist/index.html。关键思路如下:

js
const fs = require('fs')
const path = require('path')
// 读取、替换、写入;路径用 path.join(__dirname, ...) 拼接

易错点:Node.js代码中的相对路径以终端所在目录为起点,不是以当前JS文件为起点,容易报“找不到文件”;应使用绝对路径。正则量词默认贪婪,.*会尽可能多匹配,需在量词后加?变成非贪婪。写入内容建议为字符串;readFile读出的Buffer要用toString()转字符串。

3. 端口号的作用 ​

为什么需要:URL用于定位网络资源,通常包含协议、域名、端口号、资源路径。一台服务器里可能有多个服务程序,端口号用来区分它们。

怎么想:把服务器想成一栋房子,域名是地址,端口号是房间号,资源路径是房间内物品位置。HTTP默认访问80端口,所以:80可省略。

怎么做:端口号有效范围是0–65535;0–1023及一些特定端口常被系统或协议占用,如80被HTTP占用、3306被MySQL占用;自己写Web服务常用3000、8080等。

易错点:端口号不能乱写,也不能误以为一个端口能随意对应多个服务;访问本机服务时用localhost:端口号,且该域名只在本机使用。

4. Web服务的功能 ​

为什么需要:Web服务是提供网上信息浏览功能的程序。用Node.js的http模块可创建服务,向请求方返回字符串、接口数据或HTML等资源。

怎么想:核心是“创建服务对象→监听请求事件→响应请求→监听端口启动”。一次请求只能对应一次响应,res.end()只调用一次。

怎么做:引入http,调用createServer,监听request事件,在回调中用响应对象res.end('hello world')返回内容,再listen(3000)启动。浏览器访问http://localhost:3000/。若要返回中文,需在响应结束前设置响应头:

js
res.setHeader('Content-Type', 'text/html; charset=utf-8')

易错点:启动服务后终端不会立刻回到命令提示符,这是进程在监听端口;要停止按Ctrl+C。返回中文若不加charset=utf-8会乱码;可在开发者工具“网络→响应标头”中查看Content-Type,确认服务端返回的内容类型和编码。

先明确:Node.js 是什么 ​

为什么需要:以前我们写 JavaScript,通常要放进 HTML 的 <script> 里,由浏览器执行。想让 JS 脱离浏览器、独立运行,就需要一个 JavaScript 运行环境,Node.js 解决的就是这个问题。

怎么想:Node.js 不是一门新语言,而是一个独立的 JavaScript 运行环境。它能让 JS 代码独立执行,因此可以用来编写服务器后端程序;同时也能处理前端代码,做压缩、转译、整合等工程化工作。

怎么做:先记住两个关键词——“运行环境”和“独立执行”。浏览器里的 JS 依赖浏览器提供页面能力;Node.js 里仍然写 JS,但运行环境换成了 Node.js。

易错点:Node.js 里没有 document 和 window,不能直接操作页面 DOM;但它仍然遵守 ECMAScript 标准语法,常见 JS 语法、数据类型、打印、定时器、Promise 等仍可使用。不要把 Node.js 理解成“浏览器里的另一个 JS”。

Node.js 有什么用 ​

为什么需要:学 Node.js 不只是为了写后端,也是为了理解前端工程化。前端项目上线前常常要压缩、转译、整合代码,这些工作可以交给 Node.js 程序完成。

怎么想:后端方向,Node.js 可以编写接口、提供数据,也可以返回网页资源;前端方向,Node.js 可以作为工程化工具,提高开发和运行效率。

怎么做:本节先建立整体认识:后面会学习用 Node.js 读写文件、处理路径、压缩 HTML/JS、创建 Web 服务等。现在先把“能独立执行 JS”这件事跑通。

易错点:不要误以为学 Node.js 就必须先学后端全部知识;也不要误以为它能直接替代浏览器。它和浏览器是不同运行环境。

为什么能独立执行 JS:V8 引擎 ​

为什么需要:理解“为什么能执行”,才能理解 Node.js 和浏览器的联系与区别。

怎么想:浏览器能执行 JS,是因为浏览器内核里有 V8 引擎;V8 会读取 JS 代码,进行翻译并执行。Node.js 基于 Chrome 的 V8 引擎封装,所以也能独立执行 JS。

怎么做:记忆链条是:Chrome V8 引擎 → Node.js 封装 → 独立执行 JS 代码。

易错点:Node.js 基于 V8,不等于“Node.js 就是浏览器”。浏览器有 DOM、BOM、window、document、XMLHttpRequest 等页面相关能力;Node.js 没有这些,但提供文件、路径、HTTP 等能力,后续课程会逐步展开。

安装 Node.js ​

为什么需要:Node.js 没有图形化界面,要先安装软件,才能在电脑上使用 node 命令执行 JS 文件。

怎么想:安装程序在素材里,Windows 和 macOS 都有。课程统一使用指定版本,转写中演示的是 16.19.0,后续项目对版本有要求,所以统一安装这个版本即可。

怎么做:双击安装程序,按 PPT 和安装向导默认“下一步”;同意协议;安装路径保持默认,不要放在中文路径下;遇到询问是否安装 C/C++、Python、Visual Studio 等构建环境的界面,不需要勾选,继续默认下一步;安装完成后点击完成。若防火墙提示,选择允许。

易错点:安装路径不要含中文,否则 Node.js 执行时可能因编码转换找不到资源;不要随意勾选额外编译环境;安装过程中耐心等待。

验证安装:node -v ​

为什么需要:安装完成后必须确认命令可用,否则后面执行文件会直接失败。

怎么想:Node.js 没有图形界面,要通过终端命令调用。Windows 可以打开“命令提示符”(CMD):按开始菜单键,搜索 cmd 并打开。

怎么做:在 CMD 中输入下面命令并回车:

bash
node -v

如果能看到版本号,说明安装成功。

易错点:node 和 -v 之间有空格;不要输入成 node-v。如果提示“不是内部或外部命令”,优先检查是否安装成功、是否重新打开终端。

编写并执行第一个 JS 文件 ​

为什么需要:安装成功只是环境准备好,还要验证 Node.js 能执行目标 JS 文件。

怎么想:终端的作用是敲命令、调用程序执行。执行 Node.js 文件要用 node xxx.js,其中 xxx.js 是目标文件名。推荐使用 VSCode 集成终端,因为它能快速定位到目标文件所在目录,便于用相对路径找到文件。

怎么做:新建 index.js,保留笔记中的目标注释和代码:

js
/**
 * 目标:编写 js 代码,用 node 命令执行
 * 终端作用:敲击命令,调用对应程序执行
 * 终端打开:目标文件->右键->在集成终端中打开
 * 命令:node xxx.js (注意路径)
 */
console.log('Hello, World')
for (let i = 0; i < 3; i++) {
  console.log(6)
}

在目标文件上右键,选择“在集成终端中打开”,确认终端当前目录就是 index.js 所在目录,然后输入:

bash
node index.js

回车后,终端会输出 Hello, World 和三个 6。演示效果如下:

在 VSCode 集成终端中执行 Node.js 文件

易错点:命令里的文件名要和实际文件一致;注意终端当前路径,路径不对会找不到文件;普通脚本执行完会回到命令提示符,不会一直停住。修改代码后要重新执行 node index.js 才能看到新结果。

本节检查 ​

Node.js 是独立的 JavaScript 运行环境,可用于后端程序和前端工程化;它能执行 JS 是因为基于 Chrome V8 引擎封装;它与浏览器 JS 的最大区别是没有 DOM/BOM,没有 window、document;执行代码时在终端输入 node xxx.js,并注意终端路径和文件名。

模块与 fs 模块:先理解“为什么要引入” ​

为什么需要:写 Node.js 代码时,不可能所有能力都从零手写。模块就像插件,封装了方法和属性,供我们直接使用。
怎么想:本节要操作本机文件,而 Node.js 内置了 fs 模块,它封装了与本机文件系统交互的方法和属性。
怎么做:先知道 fs 是“文件系统模块”,再通过固定语法加载它。
易错点:不要把模块理解成页面里的某个标签;它是 Node.js 环境提供的能力集合。

加载 fs 模块:require 拿到对象 ​

为什么需要:要调用 writeFile、readFile,必须先拿到 fs 对象。
怎么想:Node.js 提供 require 方法,传入模块标识符即可加载内置模块。
怎么做:

js
const fs = require('fs')

转写稿中演示过:可以先 console.log(fs),运行后能看到一个对象,里面有很多属性和函数。这说明模块里确实封装了方法和属性,本节重点关注读写文件的方法。
易错点:require('fs') 中的 fs 是模块名,不要写成文件路径;变量名通常叫 fs,便于阅读。

写入文件:writeFile 的参数与回调 ​

为什么需要:我们需要把内容保存到本机文件中。
怎么想:writeFile 至少关注三件事:写到哪个文件、写什么内容、写完后怎么处理结果。
怎么做:

js
fs.writeFile('文件路径', '写入内容', err => {
  // 写入后的回调函数
})

回调里的 err 是错误信息:写入成功时通常为空;写入失败时会有错误原因。实际代码中常写成:

js
fs.writeFile('./text.txt', '欢迎使用 fs 模块读写文件内容', err => {
  if (err) console.log(err)
  else console.log('写入成功')
})

转写稿调试时,把写入内容换成数组,终端出现 TypeError,关键信息是 data 参数必须是字符串或 Buffer 类型。因此建议写入字符串内容。
易错点:writeFile 会覆盖目标文件原有内容;写入和读取的文件名要一致,否则读取时会找不到文件。

读取文件:readFile 与 Buffer ​

为什么需要:写入后还要把文件内容读回程序里,验证或继续处理。
怎么想:readFile 需要文件路径和读取后的回调;回调有两个常用参数:err 和 data。
怎么做:

js
fs.readFile('文件路径', (err, data) => {
  // 读取后的回调函数
  // data 是文件内容的 Buffer 数据流
})

转写稿中读取后直接打印 data,看到的是 Buffer 数据流,表现为十六进制形式;要看到熟悉的中文,需要调用 data.toString() 转成字符串。
易错点:读取不存在文件时,err 会有值,错误信息类似“没有这样的文件或文件夹”;成功时 err 为空。不要直接把 Buffer 当普通字符串拼接。

完整需求代码:写入并读取 text.txt ​

为什么需要:把“加载模块、写入、读取”串成完整流程。
怎么想:先引入 fs,再写入,再读取;每一步都判断 err。
怎么做:保留本节笔记代码:

js
/**
 * 目标:使用 fs 模块,读写文件内容
 * 语法:
 * 1. 引入 fs 模块
 * 2. 调用 writeFile 写入内容
 * 3. 调用 readFile  读取内容
 */
// 1. 引入 fs 模块
const fs = require('fs')
// 2. 调用 writeFile 写入内容
// 注意:建议写入字符串内容,会覆盖目标文件所有内容
fs.writeFile('./text.txt', '欢迎使用 fs 模块读写文件内容', err => {
  if (err) console.log(err)
  else console.log('写入成功')
})
// 3. 调用 readFile  读取内容
fs.readFile('./text.txt', (err, data) => {
  if (err) console.log(err)
  else console.log(data.toString()) // 把 Buffer 数据流转成字符串类型
})

易错点:笔记目标里提到 test.txt,而代码中使用的是 ./text.txt;实际运行时写入和读取必须使用同一个路径,否则读取会报错。

运行与调试:从打印对象到错误定位 ​

为什么需要:知道语法不等于会运行,需要通过终端观察结果。
怎么想:在 VS Code 中打开集成终端,用 node 文件名 执行;修改代码后重新执行。
怎么做:先 node 02.js 看是否打印“写入成功”,再观察是否生成 text.txt,最后看读取内容是否转成中文。转写稿中还演示了按方向键上快速找回上一条命令。
易错点:写入成功但读取失败时,优先检查路径和文件名;看到 TypeError 时,找 at 上方那一行关键信息;看到 Buffer 不要慌,它是文件内容的二进制数据表现形式,用 toString() 转换即可。

小结:本节要记住的判断 ​

为什么需要:把零散语法变成可复用的操作习惯。
怎么想:模块是封装好的方法和属性;fs 用来读写本机文件内容;写入用 writeFile,读取用 readFile。
怎么做:加载模块 → 写入并判断 err → 读取并判断 err → 用 toString() 转字符串。
易错点:写入内容建议用字符串;写入会覆盖原内容;读写路径要一致;data 默认是 Buffer,不是直接可读字符串。

课件图示 ​

课件第3页:定义: 概念:使用Node.js 编写后端程序/ 支持前端工程化  后端程序:

课件第8页:模块:类似插件,封装了方法/属性 fs 模块:封装了与本机文件系统进行交互的,方

课件第4页:首先:浏览器能执行JS 代码,依靠的是内核中的V8 引擎(C++ 程序) 其次:

课件第5页:要求:下载node-v16.19.0.msi 安装程序(指定版本:为了兼容后期学

课件第7页:总结 总结 1. Node.js 有什么用?  编写后端程序:提供数据和网页资

代码示例 ​

示例一:fs 模块读写文件 ​

js
/**
 * 目标:使用 fs 模块,读写文件内容
 * 语法:
 * 1. 引入 fs 模块
 * 2. 调用 writeFile 写入内容
 * 3. 调用 readFile  读取内容
 */
// 1. 引入 fs 模块
const fs = require('fs')

// 2. 调用 writeFile 写入内容
// 注意:建议写入字符串内容,会覆盖目标文件所有内容
fs.writeFile('./text.txt', '欢迎使用 fs 模块读写文件内容', err => {
  if (err) console.log(err)
  else console.log('写入成功')
})

// 3. 调用 readFile  读取内容
fs.readFile('./text.txt', (err, data) => {
  if (err) console.log(err)
  else console.log(data.toString()) // 把 Buffer 数据流转成字符串类型
})

关键行与易错点

  • require('fs') 是 Node.js 内置模块,模块名直接写 fs 即可,它返回的对象里封装了与本机文件系统交互的方法和属性。
  • writeFile 第三个参数是写入后的回调,形参只有一个 err:成功时 err 为空,失败时 err 里是失败原因;用 if (err) ... else ... 判断,不要忘了打印成功提示。
  • 易错点:写入内容建议为字符串类型(或 Buffer),若传入数组会报 TypeError,提示 data 参数必须是字符串或 Buffer 类型。
  • 易错点:writeFile 会覆盖目标文件的所有原有内容,不是追加。
  • 易错点:readFile 回调的 data 是 Buffer 数据流,直接打印看到的是十六进制内容,必须调用 data.toString() 才能得到中文/字符串。
  • 易错点:这里的 ./text.txt 是相对路径,Node 执行时是以终端所在目录为起点查找的,不是以 JS 文件所在目录为起点,换目录执行就会找不到文件。
  • 执行方式:在目标 JS 文件上右键 →「在集成终端中打开」,再敲 node 02.js 回车。

示例二:path 模块处理路径(配合 __dirname 使用绝对路径) ​

js
/**
 * 目标:使用 path 模块 + __dirname 内置变量,用绝对路径读取目标文件
 */
const fs = require('fs')
const path = require('path')

// 打印当前模块(JS 文件)所在文件夹的绝对路径
console.log(__dirname)

// 拼接目标文件的完整绝对路径
const filePath = path.join(__dirname, '../text.txt')
console.log(filePath)

// 用绝对路径读取文件内容
fs.readFile(filePath, (err, data) => {
  if (err) console.log(err)
  else console.log(data.toString())
})

关键行与易错点

  • __dirname 是 Node.js 的内置变量,注意是英文状态下两个下划线,它的值是动态获取到的「当前 JS 文件所在目录」的完整绝对路径。
  • path.join(...) 可以传入多个路径片段,它会自动识别当前是 Windows 还是 Mac 系统,用对应的分隔符把各段连接起来并返回,所以不要自己手写斜杠。
  • 易错点:直接用相对路径 ./text.txt 时会报「没有这样的文件或文件夹」,因为代码里的相对路径是依赖终端所在路径触发的,而不是像前端那样从当前文件出发。
  • 易错点:不要把 __dirname 打印出来的绝对路径直接复制粘贴进代码里当死路径,Windows 是反斜杠、Mac 是斜杠,代码换系统就跑不通,必须用 path.join 拼接。
  • 目标文件在上一级目录时,路径片段写 '../text.txt'(.. 代表上一级),再由 path.join 拼接,分隔符同样不用自己写。
  • 使用绝对路径后,无论终端切到哪个目录(如 cd 03 后再 node index.js)都能正确读到目标文件。

示例三:http 模块创建 Web 服务并返回中文 ​

js
/**
 * 目标:使用 http 模块创建一个 Web 服务,浏览器访问能返回中文内容
 * 1. 引入 http 模块
 * 2. 创建 Web 服务对象
 * 3. 监听 request 事件,做出响应
 * 4. 监听端口号,启动服务
 */
const http = require('http')

// 创建 Web 服务对象
const server = http.createServer()

// 监听 request 请求事件
server.on('request', (req, res) => {
  // 设置响应头:告诉请求方内容类型和编码格式(固定写法)
  res.setHeader('Content-Type', 'text/html; charset=utf-8')
  // 结束本次请求和响应,并返回内容给请求方
  res.end('你好,亲爱的世界')
})

// 监听 3000 端口号,启动服务
server.listen(3000, () => {
  console.log('web 服务启动了')
})

关键行与易错点

  • http.createServer() 方法名固定,调用后得到 Web 服务对象;再用 server.on('request', 回调) 监听请求事件,事件名 request 固定,回调第一个形参是请求对象、第二个是响应对象。
  • 易错点:res.end() 表示结束本次请求和响应,一次请求只能对应一次响应,end 只能调用一次,不能重复写。
  • 易错点:不设置响应头直接返回中文,浏览器会显示乱码。必须 res.setHeader('Content-Type', 'text/html; charset=utf-8'),字段名和值都是固定格式:text/html 表示返回普通文本、可被解析为 HTML 标签,charset=utf-8 告诉请求方按 UTF-8 解析中文。
  • 易错点:端口号有效范围是 0–65535,但 0–1023 及一些特定端口已被占用(80 被 HTTP 协议占用、3306 被 MySQL 数据库服务占用),写 Web 服务时尽量避开,示例用 3000。
  • 易错点:用 node 08/index.js 启动后,终端不会立刻回到待输入状态,因为进程在持续监听端口;要停止需先点一下终端使其激活,再按 Ctrl + C。修改代码后必须先 Ctrl + C 终止再重新 node 启动,否则跑的还是旧代码。
  • 启动后在浏览器访问 http://localhost:3000,localhost 代表本机域名,只能在自己的电脑上使用;资源路径先访问 / 根路径即可。
  • 调试时按 F12 打开开发者工具 → Network(筛选选「全部」,因为不是 XHR 请求)→ 点击该请求 →「响应」看响应体,「标头」里的响应标头能看到刚设置的 content-type 字段和值。

常见错误 ​

  1. 现象:node 执行后报 ENOENT,路径“看起来明明正确”。→ 原因:代码里的相对路径按终端当前工作目录解析,不是按当前 JS 文件所在目录解析。→ 修法:统一用 path.join(__dirname, ...) 或 path.resolve(__dirname, ...) 生成绝对路径。
  2. 现象:fs.readFile 打印出 <Buffer ...>。→ 原因:读取结果是 Buffer 数据流,未转成字符串。→ 修法:使用 data.toString(),或在读取时明确按 utf8 处理。
  3. 现象:fs.writeFile 写入对象/数组时报 TypeError,或原文件内容被整体覆盖。→ 原因:writeFile 更适合字符串/Buffer,且默认覆盖目标文件。→ 修法:对象先 JSON.stringify;需要追加时用 appendFile,覆盖前确认目标文件。
  4. 现象:replace 只替换了第一处换行或第一个匹配项。→ 原因:正则缺少全局修饰符 g。→ 修法:压缩换行用 /[\r\n]/g;删除多处 console.log 时同样加 g。
  5. 现象:压缩 JS 时把大段正常代码误删。→ 原因:.* 默认贪婪匹配,会一直匹配到最后一个符合结尾的位置。→ 修法:改为非贪婪 .*?,或把正则收窄到单条语句,并先用小样本验证。
  6. 现象:浏览器访问 Node 服务时中文乱码。→ 原因:响应头未声明内容类型和字符编码。→ 修法:在 res.end 前设置 res.setHeader('Content-Type', 'text/html; charset=utf-8')。
  7. 现象:改了服务代码,浏览器仍返回旧内容;或启动时报端口占用。→ 原因:旧 Node 进程未停止,或端口已被其他程序占用。→ 修法:终端 Ctrl+C 停止后重启;必要时更换端口或结束占用进程。
  8. 现象:请求处理中报“响应结束后仍写入”或浏览器响应异常。→ 原因:同一个请求里多次调用 res.end()。→ 修法:每个请求只结束一次响应,分支处理时用 return 避免继续执行。

练习 ​

  1. 路径改造:现有 03/index.js 使用 fs.readFile('../test.txt', ...),从项目根目录终端执行会失败。请改成不依赖终端工作目录的写法。验收:在任意目录启动终端执行该文件都能读到 test.txt。
  2. HTML 压缩:读取 public/index.html,去掉 \r 和 \n,写入 dist/index.html,并分别处理成功/失败提示。验收:dist/index.html 为紧凑内容,浏览器可正常打开。
  3. 正则修正:从一段 JS 字符串中删除所有 console.log('...');,但不能误删后续正常代码。请写出替换正则并说明为何需要 g 和非贪婪。验收:多条 console.log 全部删除,后续函数代码保留。
  4. 中文 Web 服务:创建监听 3000 端口的服务,返回包含“你好,世界”的 HTML 内容。验收:浏览器访问 http://localhost:3000 中文正常;F12 的 Network 中能看到正确的响应头。
  5. URL 职责判断:访问 http://localhost:8080/student/data.json 时,说明 8080 与 /student/data.json 分别定位什么;若服务改为监听 3000,URL 应如何变化。验收:能准确区分端口号与资源路径,并写出修改后的 URL。

原视频对照 ​

#视频时长
1Node.js安装与使用13:37
2​fs模块-读写文件13:53
3​path模块-路径处理15:48
4案例-压缩前端html13:13
5案例-压缩前端js14:38
6认识URL中的端口号05:49
7http模块-创建Web服务11:00
8Web服务支持中文字符07:17

小结 ​

  1. Node 学习的核心闭环是:执行 JS、读写文件、处理路径、启动 Web 服务,后续案例都围绕这条链路展开。
  2. 路径问题优先用 __dirname + path.join 解决,不要依赖“当前终端恰好在哪里”。
  3. fs 读写要盯住数据形态:读出来常是 Buffer,写进去优先字符串,回调里先判断 err。
  4. 前端工程化压缩的本质是“读入—替换—写出/整合”,正则的 g 与贪婪控制是高频失分点。
  5. Web 服务最小闭环是“创建服务—监听请求—返回响应—监听端口”,一次请求只能对应一次响应。
  6. 中文返回不是“玄学乱码”,而是响应头声明问题;服务代码修改后必须重启进程验证。
  7. 端口号用于区分同一台机器上的不同服务程序,资源路径用于定位该服务下的具体资源。
  8. 调试时要同时看终端错误、浏览器页面、Network 响应头和响应体,不能只看页面结果。