此次nodejs教程应该都会采用Typescript语法,Typescript是JavaScript的超集,增加了类型判断,用过就回不去js了

以及,全篇能使用的地方都会优先import(ESM),require(CommonJS)少用别用

安装

直接安装Nodejs

已不再推荐,建议是卸了再安装版本管理器,用docker都好,别直装

NVM:node版本管理器

我推荐用NVM是因为我只用过NVM

有些老项目(尤其是工作时),需要更古老的nodejs/npm环境,然而你可能还需要同时拥有一些新东西,直接安装在全局环境变量的nodejs肯定不能同时满足这种需求,所以你需要一个管理器。所以,为了安装这个管理器,你需要先把电脑上的nodejs卸载了,如果是Windows系统,控制面板->添加或删除程序->卸载Nodejs即可,linux不知道,自己解决。至于为什么linux不知道,因为linux上我会直接用docker安装nodejs虚拟环境,主环境里都没有node这个东西,再加上现在我写个人项目用deno而不是node了。

安装

下载nvm

如果是linux,有安装脚本:

curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash
# 或者你使用wget
wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.2/install.sh | bash

# 然后刷新终端环境
source ~/.bashrc

镜像配置

阿里云镜像

nvm npm_mirror https://npmmirror.com/mirrors/npm/
nvm node_mirror https://npmmirror.com/mirrors/node/

使用

具体使用方式:

# 检查版本
nvm --version

# 列出可安装的所有版本
nvm ls available
# 安装特定版本
nvm install 14.17.0
# 将系统环境切换到指定版本
nvm use 14.17.0
# 列出已安装以及正在使用的版本
nvm ls

这里再提一嘴LTS和Current的区别:

  • LTS(Long Term Support)指长期支持版本

  • Current实验性功能的新开发版本

没什么特殊需求新项目无脑最新LTS就可以了

Deno 新一代Typescript运行时

https://www.denojs.cn/

在学过ts后,你应该不会很再想js了,这个时候,你发现你在写nodejs小脚本的时候为了支持ts,你又要装tsc、ts-node,超级麻烦,所以这里有一个nodejs原作者写的新一代deno作为ts运行时,现已加入rust重构前端工具链的经典套餐

安装

linux:

curl -fsSL https://deno.land/install.sh | sh

windows:

iwr https://deno.land/install.ps1 -useb | iex
choco install deno
scoop install deno

mac

brew install deno

cargo源码构建

cargo install deno --locked

执行

# as node
deno run -A ./src/main.ts
# as nodemon
deno run -A --watch ./src/main.ts

Nodejs 特有模块

fs 基础文件操作

引入:

const fs = require('fs');
import * as fs from 'node:fs';
import * as fs from 'node:fs/promises';  // 使用基于回调的api

至于为什么要使用node:fs而不是fs,是因为要兼容deno,deno在笔记写下的时候还和node+npm生态没有特别好的兼容,但是通过node:xx前缀,我们可以让node模块在两种环境下同时运行

import { readFileSync } from "node:fs";
console.log(readFileSync("deno.json", { encoding: "utf8" }));

import * as fs from 'node:fs';
// fs.readFile(__dirname + path[,options],callback)	//文件路径,编码格式,回调函数
fs.readFile('./files/1.txt','utf8', (err,dataStr) => {
    if(err) return console.log('读取文件失败' + err.message)
})
//	如果读取成功,则err的值为null;如果读取失败,err的值为 错误对象,dataStr的值变为 undefined
 
// fs.writeFile(__dirname + path,data[,options],callback)	//data是字符串;options可选,默认utf8
fs.writeFile('./files/2.txt','abcd', (err) => {
    if(err) return console.log(err)
    console.log('文件写入成功!')
})

path 路径模块

import * as path from "node:path"
 
path.join()			//将多个路径片段拼接成一个完整的路径字符串
const pathStr = path.join('/a','/b/c','../','d','e')		//'../'返回上级文件夹
cosole.log(pathStr)		//输出 \a\b\d\e
 
path.basename(path[,ext])	//用来从路径字符串中,将文件名解析出来
//path<String>表示一个路径的字符串,ext<String>表示文件扩展名,返回<String>表示路径中的最后一部分 
path.extname(path)			//用于得到拓展名

http 网络模块

import * as http from "node:http"
const server = http.createServer()
 
server.on('request',(req,res) => {
    //req是请求对象,包含了与客户端相关的数据和属性,例如
    //req.url 是客户端请求的url地址,req.method是客户端的method请求类型
    console.log('Someone visit our web server')
    //为了防止中文显示乱码问题,需要设置响应头
    res.setHeader('Content-Type','text/html; charset=utf-8')
    //把内容响应给客户端,并结束这次请求
    const url = req.url
    let content = '<h1>404 Not found!</h1>'
    if(url === '/' || url === '/index.html') content = '<h1>首页</h1>'
    else if(url === '/about.html') cotent = '<h1>关于</h1>'
    res.end(content)
})
server.listen(80, ()=>{		//运行在80端口可忽略
    console.log('http server running at http://127.0.0.1')
})

常用npm包 for nodejs

Koa 后端框架

小,轻,优雅,国内火(但谁用nodejs呢),海外不如express(可能是因为Vue和React的脚手架内置了express)

安装

pnpm i koa
pnpm i --save-dev @types/koa

使用

import Koa from 'koa';
const app = new Koa();

// 响应
app.use(ctx => {
    ctx.body = 'Hello Koa';
});

app.listen(3000);

快速启动

安装常用中间件(koa-router, koa-body, @koa/cors)

pnpm i koa
pnpm i koa-body
pnpm i koa-router
pnpm i @koa/cors
pnpm i --save-dev @types/koa
pnpm i --save-dev @types/koa-router
pnpm i --save-dev @types/koa__cors

main.ts

import Koa from 'koa';
import Router from 'koa-router';
import { koaBody } from 'koa-body';
import cors from '@koa/cors';

const app = new Koa();
const router = new Router()

app.use(cors());
app.use(koaBody());

router.get('/', ctx => {
    ctx.body = 'Hello Koa'
})

router.post('/test', ctx => {
    ctx.body = `Request Body: ${JSON.stringify(ctx.request.body)}`;
})

app.use(router.routes())
app.use(router.allowedMethods())

app.listen(3000);

静态资源 koa-static

安装

pnpm install koa-static
pnpm i --save-dev @types/koa-static
pnpm i koa-mount
pnpm i --save-dev @types/koa-mount

配置swagger

安装

pnpm i -D swagger-jsdoc koa2-swagger-ui
pnpm i --save-dev @types/swagger-jsdoc

./src/swagger.conf.ts

import swaggerJSDoc from "swagger-jsdoc";

const swaggerDefinition = {
    info: {
        title: "My Swagger",
        version: "1.0.0",
        description: "My Swagger Description",
    },
    host: `localhost:3000`, // 指定项目地址,建议在环境参数中配置,然后用 process.env 读取
    basePath: "/",
};

const options = {
    swaggerDefinition,
    apis: ["./src/routes/*.js", "./src/routes/*.ts"],
};

const swaggerSpec = swaggerJSDoc(options);

export default swaggerSpec;

main.ts

import Koa from 'koa';
import { koaBody } from 'koa-body';
import cors from '@koa/cors';
import { koaSwagger } from "koa2-swagger-ui";
import router from './routes/index.ts';
import swaggerSpec from "./swagger.conf.ts"; // 注意文件路径

const app = new Koa();

app.use(cors());
app.use(koaBody());

router.get("/swaggerDoc", async (ctx) => { ctx.body = swaggerSpec; });
app.use(
    koaSwagger({
        routePrefix: "/docs", // 交互界面 url
        swaggerOptions: { url: "http://localhost:3000/swaggerDoc" }
    })
);

app.use(router.routes())
app.use(router.allowedMethods())

app.listen(3000);

防止你不会swagger注释


/**
 * @swagger
 * definitions:
 *   Project:
 *     type: object
 *     properties:
 *       id:
 *         type: string
 *         description: 项目的唯一标识符
 *         example: "UUID-1"
 *       name:
 *         type: string
 *         description: 项目名称
 *         example: "qqbot"
 *       version:
 *         type: string
 *         description: 项目版本号
 *         example: "0.0.1"
 *       modity_time:
 *         type: string
 *         format: date-time
 *         description: 项目最后修改时间
 *       cover:
 *         type: string
 *         format: uri
 *         description: 项目封面图片链接
 *         example: "https://qzonestyle.gtimg.cn/qzone/qzact/act/external/tiqq/logo.png"
 */

/**
 * @swagger
 * /home/getProjectList:
 *   get:
 *     summary: 获取项目列表
 *     description: 获取项目列表的详细信息
 *     tags:
 *       - Home
 *     parameters: 
 *       - in: query
 *         name: class
 *         type: string
 *         description: 如果含有参数, 则返回对应分类的项目
 *         required: false
 *     responses:
 *       200:
 *         description: 成功返回项目列表
 *         schema:
 *           type: array
 *           items:
 *             $ref: "#/definitions/Project"
 */
router.get('/getProjectList', async (ctx, next) => {
    ctx.response.body = [{
        id: "UUID-1",
        name: "qqbot",
        version: "0.0.1",
        modity_time: new Date(),
        cover: "https://qzonestyle.gtimg.cn/qzone/qzact/act/external/tiqq/logo.png"
    }]
    await next();
})