1. 基本介绍

node.js是js的运行环境,能够让js在后端/服务端等运行。

所以语法还是js,只是需要了解一些新的模块。

2.第一个node程序

方式一:终端输入node,在交互式界面中直接输入js代码即可

方式二:新建js文件并写入内容,终端node 文件名执行

3.基本项目结构

node使用npm进行包管理(pnpm/yarn也可以,差别不大,pnpm更快一些),安装好各种包之后会出现两个文件

package.json:记录项目相关信息及依赖的顶层包,版本号可以不精确

package-lock.json:记录项目版本及详细的依赖树,锁定精确的版本号

node_modules文件夹是下好的依赖,其余是代码文件

4.fs模块

用于文件读写。

有同步和基于promise的异步的方法,引入模块后即可调用相关的方法执行对应操作。

可以进行:文件读写、目录管理、文件状态检测、流式读取文件、其他简单操作。具体demo代码在图片中。

// fs模块使用方式

// Promise API
// 引入fs/promises模块m
const fs = require('fs/promises');
// 传统回调/同步api
const fsCallback = require('fs');
const fsSync = require('fs')

// =======================
// 1. 读写文件
// =======================
async function handleFile() {
    // 覆盖写文件
    await fs.writeFile('demo.txt', 'hello, node.js!', 'utf8');
    // 追加写文件
    await fs.appendFile('demo.txt', '\nNew line append.', 'utf8');
    // 读文件内容
    const content = await fs.readFile('demo.txt', 'utf8');
    console.log(content);
}

// =======================
// 2. 目录管理
// =======================
async function handleDirectory() {
    // 新建目录
    await fs.mkdir('folder/path', { recursive: true });

    // 读取路径下所有文件,返回fs.Dirent数组
    const entries = await fs.readdir('./', { withFileTypes: true });
    for (const entry of entries) {
        console.log(`${entry.name} -> ${entry.isDirectory() ? 'DIR' : 'FILE'}`);
    }

    // 删除目录,recursive递归删除,force忽略不存在错误
    await fs.rm('folder', { recursive: true, force: true });
}

// ======================
// 3. 文件状态检测
// ======================
const { constants } = require('fs')
async function inspectFile(path) {
    // access进行可访问性权限检查,检查成功继续执行,检查失败(文件不存在/无权限)抛出错误
    // |是按位或,代表把两个权限打包,而不是任一满足即可
    await fs.access(path, constants.F_OK | constants.R_OK);
    // 返回一个fs.Stats实例,包含状态信息
    const stats = await fs.stat(path);
    console.log('size: ', stats.size);
    console.log('Is file: ', stats.isFile());
    console.log('Created time: ', stats.birthtime);
}

// =========================
// 4. 流式读取文件,利用pipeline,可以自动处理错误与资源清理
// =========================
const { pipeline } = require('stream/promises');
await pipeline(
    fs.createReadStream('large_souce.log'),
    fs.createWriteStream('copy_file.log')
);

// ======================
// 5. 其他操作
// ======================
// 文件重命名
fs.rename('old_name.txt', 'new_name.txt');
// 删除文件
fs.unlink('new_name.txt');

handleFile();
handleDirectory();
inspectFile('server.js')

5.http模块

可以处理http客户端/服务端请求。

引入模块后即可自己启动本地服务/向远程url发送请求。

两个核心对象,req(request)是客户端发送的请求对象,res(response)是服务端返回的响应对象。

本地服务构建流程:创建http服务,启动并监听http服务,可以自定义res的内容。

向远程url发送请求可以自定义req,但只能监听res返回内容。

// 创建一个基础的http服务

// 使用 CommonJS 规范的 require() 函数导入 Node.js 内置模块
const http = require('http');

// ===========================
// 1.自己启动本地服务
// ===========================

// 用http模块中的方法创建一个http服务,接收一个回调函数作为收到请求时的响应
// 仅是创建,并没有启动
// req: request可读流,客户端发来的请求
//    req.method  http方法
//    req.url  请求路径及查询参数
//    req.headers  请求头对象
// res:response可写流,服务器返回的请求
//    res.writeHead 显式写入状态吗与响应头
//    res.setHeader 设置单个响应头
//    res.write 向客户端发送一块数据
//    res.end 完成响应并关闭连接,必须使用
const server = http.createServer((req, res) => {
    const { method, url } = req;
    console.log(`method: ${method}, url: ${url}`);

    // 因为是本地启动的服务,所以可以自己写服务返回内容
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain; charset=utf-8');
    res.end('你好,这是我的第一个 Node.js 服务器!\n');
});

// 启动并监听3000端口,服务启动则执行回调
// 可自行打开 localhost:3000 查看返回结果
const PORT = 3000;
server.listen(PORT, () => {
    console.log(`服务已启动:http://localhost:${PORT}`);
});


// ==========================
// 2.向远程url发送请求
// ==========================
const req = http.get("http://jsonplaceholder.typicode.com/todos/1", (res) => {
    let rawData = '';
    // 监听服务器返回的内容
    res.on('data', (chunk) => {
        rawData += chunk;
    })
    res.on('end', () => {
        const parsedData = JSON.parse(rawData);
        console.log('服务器返回的数据为:', parsedData);
    })
})

// 监听请求发送情况
req.on('error', (err) => {
    console.error(`Request failed, ${err.message}`)
})

6.express框架

6.1 基本用法:引入express库,定义请求体解析中间件,定义接口,启动服务并监听

6.2 中间件:

有req,res,next的被叫做是中间件,其实就是一个函数,可以自定义放行(next)逻辑

使用方式一:全局注册并使用中间件app.use

使用方式二:自定义中间件并挂载到合适的路由使用

6.3模块化路由:

类似vue,可以在子代文件中定义细路由并导出,亲代文件中引入并注册挂载到对应大路由

// express框架

//  ======================
//  express框架基本用法
//  ======================
// 1. 引入express库
const express = require('express');
// 2. 创建express应用实例
const app = express();
const PORT = 3000;

// 3. 注册请求体解析中间件,req的请求体会被自动转换为js对象绑定到req.json上
app.use(express.json());

// 4. 定义get接口
app.get('/', (req, res) => {
    // 向客户端发送信息,并结束本次请求响应
    res.send('Hello, Express');
});

// 定义get接口,id为占位符(带冒号表示放进来的是变量的值)
app.get('/user/:id', (req, res) => {
    const userId = req.params.id;
    // 设置响应头,同时内容转为json格式的字符串发送给客户端
    res.json({ id: userId, name: 'Alice' });
});

// 定义post请求
app.post('/users', (req, res) => {
    const user = req.body;
    // 定义返回的状态码,同时链式定义返回的json字符串
    res.status(201).json({ message: 'user created', data: user });
})

// 5. 启动服务在PORT端口上并监听
app.listen(PORT, () => {
    console.log(`服务正在运行,端口号为${PORT}`);
})


// ========================
// 中间件
// ========================
// a. 全局注册中间件并使用
// 有req,res,next的被叫做是中间件,其实就是一个函数,可以自定义放行(next)逻辑
app.use((req, res, next) => {
    console.log('...');
    next();
})

// b1. 定义鉴权中间件
function authGuard(req, res, next) {
    const token = req.headers['authorization'];
    if (token) {
        next();
    } else {
        // 中间件进行拦截并响应,设置状态码,返回json字符串
        res.status(401).json({ error: 'Unauthorized' })
    }
}

// b2. 使用authGuard中间件,用.send/.json方法就会自动结束本次请求响应,不用手动next
app.get('/dashboard', authGuard, (req, res) => {
    res.send('welcome');
})

// c. 多中间件链式组合
// validateParams也是一个中间件,这里没有实现,只是占位表示一下
app.post('/profile', validateParams, authGuard, (req, res) => {
    res.send('Profile Updated');
})


// =======================
// 模块化路由
// =======================
// a1. ./routes/userRoutes 
// 子文件中使用路由封装组件
const router = express.Router();

// a2. 子文件中定义get接口
router.get('/', (req, res) => res.send('User List'));
router.get('/:id', (req, res) => res.send(`User ${req.params.id}`));

// a3. 子文件中导出路由接口
module.exports = router;

// b1. 亲代文件中引入子路由
const userRouter = require('./routes/userRoutes');
// b2. 亲代文件中注册子路由,把对应的url重定向过去
app.use('/api/users', userRouter);

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐