Nodejs
Node.js 是一个基于 Chrome V8 引擎构建的开源、跨平台的 JavaScript 运行时环境。它让开发者能够使用 JavaScript 编写服务器端代码,将 JavaScript 的应用范围从浏览器扩展到了服务器、本地脚本等领域。
🛠️ 典型应用场景
基于以上特点,Node.js 在不同场景下的适用性如下:
-
非常适合
- 实时应用:如在线聊天、直播弹幕、在线协作工具等。
- API 服务:构建 RESTful API 或作为 API 网关/BFF 层。
- 单页应用 (SPA) 服务端渲染 (SSR):如使用 Next.js 等框架。
- 前端工具链:大多数现代前端框架(如 React, Vue)的构建工具都依赖 Node.js。
-
不太适合
- CPU 密集型任务:如大数据分析、AI 模型训练、复杂算法等,这类任务更适合使用 Python 或 Go。
- 对稳定性要求极高的复杂系统:如金融核心系统,传统上更倾向于使用 Java 等语言。
🤔 优势与劣势分析
| 优势 | 说明 |
|---|---|
| 高并发处理能力 | 事件驱动、非阻塞 I/O 模型,非常适合 I/O 密集型应用(如聊天室、API 网关、实时推送)。 |
| 生态丰富 (NPM) | 拥有全球最大的开源包管理器 NPM,几乎任何功能都能找到现成的库。 |
| 全栈语言统一 | 前后端均使用 JavaScript,降低上下文切换成本,便于全栈开发。 |
| 劣势 | 说明 |
|---|---|
| CPU 密集型瓶颈 | 单线程模型,如果执行大量复杂计算(如视频转码),会阻塞事件循环,导致服务响应变慢。 |
| 稳定性风险 | 未捕获的异常可能导致整个进程崩溃。生产环境通常需要使用 PM2 等工具进行进程守护。 |
| 类型安全问题 | JavaScript 是弱类型语言,大型项目容易因类型错误产生 Bug(建议使用 TypeScript 弥补)。 |
koa
Koa 是一个由 Express.js 原班人马打造的、基于 Node.js 的下一代 Web 框架。它的设计目标是成为一个更小、更富有表现力、更健壮的框架。
Koa依赖node v7.6.0或ES2015及更高版本和async方法支持。
✨ 核心特点
- 轻量级设计:Koa 的核心非常小巧,不内置任何中间件。这使得开发者可以按需选择组件,保持应用的精简和灵活。
- 现代化的异步处理 :Koa 利用 ES6 的
async/await语法,让异步代码的编写和阅读都像同步代码一样清晰,有效避免了"回调地狱"问题。 - 优雅的洋葱模型:这是 Koa 最具特色的中间件机制。
🧅 洋葱模型中间件
与 Express 的线性模型不同,Koa 的中间件采用"洋葱模型"(Onion Model)。请求处理流程就像穿过一个洋葱:
- 请求进入:从外层中间件开始,一层层向内执行。
- 核心处理:到达最内层,执行核心业务逻辑。
- 响应返回:从内向外,反向执行之前中间件的后续代码。
这种双向流动的机制,使得在请求处理前后执行统一逻辑(如日志记录、错误处理、响应时间计算)变得非常优雅和高效。
javascript
// 示例:洋葱模型执行顺序
app.use(async (ctx, next) => {
//上下文对象 ctx(context) 包含了与当前http请求相关的所有信息,如:
//http方法、url、请求头、请求体、查询参数等
console.log('1. 请求进入中间件1');
await next(); // 将控制权交给下一个中间件
console.log('4. 响应返回中间件1');
});
app.use(async (ctx, next) => {
console.log('2. 请求进入中间件2');
await next(); //若中间件调用了next(),会暂停当前中间件的执行,将控制权传递给下一个中间件
console.log('3. 响应返回中间件2');
});
app.use(async (ctx) => {
console.log('核心业务逻辑处理');
ctx.body = 'Hello Koa!';
});
// 控制台输出顺序: 1 -> 2 -> 核心业务逻辑处理 -> 3 -> 4
🧩 常用生态
由于 Koa 本身非常精简,实际开发中需要借助丰富的第三方中间件生态。以下是一些常用的中间件:
- 路由 :
koa-router或@koa/router - 请求体解析 :
koa-bodyparser - 静态文件服务 :
koa-static - 日志记录 :
koa-logger - 跨域处理 :
@koa/cors - 文件上传 :
@koa/multer
🚀 快速上手
安装
初始化配置文件 package.json
npm init -y //-y 是默认所有选项
使用 npm 安装 Koa 核心包:
npm install koa

2、创建一个简单应用-- server.js
一个最简单的Koa应用只需要几行代码:
javascript
// koa ------ 基于Node.js平台的下一代web开发框架
// 运用node.js中的koa框架创建一个http服务器
const Koa = require('koa'); //引入koa模块
const app = new Koa(); //实例化一个Koa对象,实例化是指根据一个类创建具体对象的过程
//中间件
app.use(async ctx => {
ctx.body = 'Hello World';
});
//启动成功,访问端口号3000
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});
运行



注意:
全局安装 nodemon,当nodemon在package.json设置需检测的文件,当该文件发生更改时,会自动重新启动应用
npm i nodemon -g
例如:我需要检测server.js 文件,设置**"dev":"nodemon server.js"**
{
"name": "nodeserve",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"dev":"nodemon server.js",
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"koa": "^3.2.0"
}
}
然后运行 npm run dev,当server.js文件修改后,在网页看输出效果时,直接刷新就好,不用重新执行命令 node server.js

app.use(function)
app.use() 方法用于注册中间件。中间件http请求和响应的函数。当一个请求到达服务器时,会从第一个中间件开始执行,直到最后一个中间件。
app.use() 返回 this,因此可以链式表达。
app.use(someMeddleware)
app.use(someOtherMeddleware)
app.listen(3000)
它等同于
app.use(someMeddleware)
.use(someOtherMeddleware)
.listen(3000)
app.listen(···)
Koa应用程序不是HTTP服务器的1对1展现。可以将一个或多个Koa应用程序安装在一起形成具有单个HTTP服务器的更大应用程序。
app.listen(3000)
上方的写法 是下方写法的语法糖
const http = require('http');
http.createServer(app.callback()).listen(3000);
🧩安装核心依赖
我们需要安装 Koa 框架本身,以及一些最常用的中间件来构建一个完整的 Web 服务。
koa: 核心框架。@koa/router: 用于处理 URL 路由。koa-bodyparser: 用于解析 POST 请求的 JSON 数据体。koa-static: 用于提供静态文件(如图片、CSS、JS 文件)。
npm install koa @koa/router koa-bodyparser koa-static
vue3+vite
1、安装框架
pnpm create vite 项目名

2、安装 Router、Axios、Element Plus
✅ Vue Router 4 对应 Vue 3
✅ Element Plus 是 Vue 3 官方适配版本
npm install vue-router@4 axios element-plus
3、配置 Vue Router
1️⃣ 创建路由文件
src/router/index.js
javascript
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about/:id',
name: 'About',
component: () => import('../views/About.vue')
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
src/view/Home.vue
javascript
<script setup>
import { ref } from 'vue'
import {useRouter} from 'vue-router'
import HelloWorld from '../components/HelloWorld.vue'
const router = useRouter()
function goPage(){
router.push('/about?name=Jen');
// Query参数(路径查询)
// router.push({ path: '/about', query: { name: 'Jen' } });
// Params参数(动态路由)
// router.push({ name: 'About', params: { id: 123 } });
// 需在路由配置中定义:{ path: '/about/:id', component: About }
}
</script>
<template>
<div>
<div>首页</div>
<HelloWorld />
<!-- 路径跳转 -->
<div><router-link to="/about">router-link不带参数跳转</router-link></div>
<!-- 命名路由跳转 -->
<div><router-link :to="{ name: 'About', params: { id: 123 } }">命名路由跳转并params带参</router-link></div>
<!-- 带查询参数 -->
<div><router-link :to="{ path: '/about', query: { plan: 'private' } }">带查询query参数</router-link></div>
<div><button @click="goPage">通过js跳转页面About</button></div>
</div>
</template>
src/view/About.vue
javascript
<script setup>
import {useRoute} from 'vue-router'
import { ref } from 'vue'
const route = useRoute() //获取上个路由传递的参数
const count = ref(0)
console.log('query',route.query);
console.log('params',route.params);
</script>
<template>
<div>About</div>
</template>
2️⃣ 在 main.js 中挂载 Router
src/main.js
javascript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App)
.use(router)
.mount('#app')
3️⃣ 添加 <router-view />
src/App.vue
javascript
<template>
<div>
<h1>Vue3 + Vite</h1>
<router-view />
</div>
</template>
4、配置 Element Plus
1️⃣ 全局引入(简单但体积大)
main.js
javascript
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App)
.use(router)
.use(ElementPlus)
.mount('#app')
✅ 直接使用组件即可:
<el-button type="primary">按钮</el-button>
2️⃣ 按需引入(✅ 推荐)
安装自动导入插件
npm install -D unplugin-vue-components unplugin-auto-import
✅ 之后无需手动 import Element Plus 组件
配置 vite.config.js
javascript
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
// https://vite.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers:[ElementPlusResolver()]
}),
Components({
resolvers:[ElementPlusResolver()]
})
],
// server: {
// open:true,
// // port:8088,
// proxy: {
// '/api': {
// target: 'http://localhost:3000',
// changeOrigin: true,
// // rewrite: path => path.replace(/^\/api/, '')
// }
// }
// }
})
一、包管理工具npm、yarn、pnpm
📊 1.1 、核心差异对比表
| 特性 | npm | Yarn (Classic/Berry) | pnpm |
|---|---|---|---|
| 底层机制 | 扁平化结构 (Flat) | 扁平化 (v1) / PnP (v2+) | 硬链接 + 符号链接 |
| 安装速度 | 较慢 (串行/优化中) | 快 (并行安装) | 极快 (复用全局缓存) |
| 磁盘占用 | 高 (每个项目重复存储) | 中 (v1) / 低 (v2 PnP) | 极低 (全局复用,节省 50%+) |
| 幽灵依赖 | 存在 (可访问未声明的包) | 存在 (v1) / 解决 (v2 PnP) | 严格解决 (无法访问未声明包) |
| Monorepo | 支持 (Workspaces) | 优秀 (Workspaces) | 极佳 (原生支持,性能最强) |
| 默认状态 | Node.js 内置 | 需单独安装 | 需单独安装 |
🧐 1.2、深度解析:它们到底有什么不同?
1.2.1. npm (Node Package Manager)
- 地位:Node.js 的"亲儿子",随 Node.js 一起安装,无需额外配置。
- 原理 :采用扁平化 的
node_modules结构。它会把依赖提升到顶层,以解决早期版本的"嵌套地狱"问题。 - 优点:生态最成熟,兼容性最好,几乎所有教程和工具都默认支持 npm。
- 缺点 :磁盘占用大(不同项目会重复存储相同的包);存在"幽灵依赖"问题(即你可以在代码中引用
package.json中未声明的包,因为 npm 把它提升到了顶层,这会导致部署时可能报错)。
1.2.2 、Yarn
- 地位:由 Facebook 推出,旨在解决早期 npm 的性能和不一致性问题。
- 版本分歧 :
- Yarn Classic (v1) :引入了
yarn.lock锁定版本,支持并行安装,速度比老版 npm 快很多。 - Yarn Berry (v2/v3/v4) :架构彻底重构,推出了 Plug'n'Play (PnP) 模式。它甚至不再生成
node_modules文件夹,而是通过映射文件告诉 Node.js 包在哪里。
- Yarn Classic (v1) :引入了
- 优点:v1 版本非常稳定;PnP 模式极致节省空间且杜绝幽灵依赖。
- 缺点:PnP 模式虽然先进,但兼容性是最大痛点,部分老旧工具或 IDE 插件可能无法识别依赖,需要额外配置。
1.2.3、 pnpm (Performant npm)
- 地位:后起之秀,目前社区增长最快,被誉为"兼顾速度与兼容性的最佳方案"。
- 原理 :内容寻址存储 。它在全局维护一个存储库(Store),所有项目通过硬链接 (Hard Link)和符号链接 (Symbolic Link)引用这些文件。
- 通俗理解 :如果你有 10 个项目都用了
React 18,npm 会在硬盘里存 10 份,而 pnpm 只存 1 份,其他项目都是"快捷方式"。
- 通俗理解 :如果你有 10 个项目都用了
- 优点 :
- 省空间:磁盘占用极低,安装速度极快(尤其是第二次安装时,几乎瞬间完成)。
- 严格隔离 :它生成的
node_modules结构是非扁平的,你无法require未在package.json中声明的包,这倒逼代码规范,避免了"在我电脑上能跑,服务器上报错"的问题。 - Monorepo 神器:在处理包含多个子包的大型项目(Monorepo)时,pnpm 的性能优势极其明显。
安装案例问题原因分析

pnpm 在尝试从 https://registry.npmjs.org/ 下载依赖包时,多次请求失败,最终导致安装中断。这是网络层面的连接问题,尤其常见于国内网络环境访问 npm 官方源时。
- 网络延迟或阻断 :你访问的是 npm 官方源
registry.npmjs.org,该域名在国内访问不稳定,常出现 DNS 解析慢、连接超时或被干扰。 - 代理或防火墙:如果你在公司内网或使用 VPN,可能被代理或防火墙拦截了 HTTPS 请求。
- pnpm 默认配置敏感 :
pnpm对网络超时和重试机制比npm更严格,并发请求多,更容易触发超时。 - 证书或 TLS 问题(虽然图中未显示,但常伴随出现):部分企业环境会中间人解密 HTTPS,导致 Node.js 不信任证书。
切换为国内镜像源(最有效)
这是最直接、最有效的解决方式。推荐使用 淘宝 NPM 镜像 或 华为云镜像 。设置后,所有 pnpm install 都会自动走国内镜像,速度飞快,基本告别超时。
javascript
# 临时使用淘宝镜像安装
pnpm install mysql2 --registry https://registry.npmmirror.com
# 或者永久设置镜像源
pnpm config set registry https://registry.npmmirror.com
💡 1.2.4、选型建议:我该用哪个?
结合 2026 年的技术环境,我的建议如下:
-
首选 pnpm:
- 如果你正在开启一个新项目,或者你的团队深受磁盘空间不足、安装依赖慢的困扰。
- 如果你在使用 Monorepo 架构(如 Turborepo, Nx),pnpm 是目前的最佳拍档。
- 它既保留了
node_modules的兼容性(不像 Yarn PnP 那样需要折腾),又解决了 npm 的性能和空间痛点。
-
继续使用 npm:
- 如果你是初学者,不想在安装工具上花费额外精力。
- 如果是维护老旧项目,且团队没有迁移计划,保持现状是最稳妥的。
-
使用 Yarn:
- 如果你维护的是已经在使用 Yarn Workspaces 的大型项目。
- 如果你追求 Yarn Berry 的零安装(Zero-Installs)特性,并且有能力解决 PnP 带来的兼容性挑战。
🔄 1.2.5、如何迁移?
如果你决定从 npm 或 Yarn 切换到 pnpm,过程非常简单:
- 安装 pnpm :
npm install -g pnpm - 清理旧文件 :删除项目中的
node_modules和锁文件 (package-lock.json或yarn.lock)。 - 安装依赖 :运行
pnpm install。 - 修改脚本 :在
package.json中,将npm run ...或yarn ...替换为pnpm ...(可选,为了统一)。
pnpm 会自动生成 pnpm-lock.yaml,之后你就可以享受极速的安装体验了。
1.3、cnpm、pnpm区别
cnpm 和 pnpm 虽然名字只有一字之差,但它们的定位、原理和解决的问题完全不同 。简单来说:pnpm 是为了"快和省",而 cnpm 是为了解决国内"网络慢"的问题。
以下是两者的详细对比分析:
1.3.1、核心区别对比
表格
| 特性 | pnpm | cnpm |
|---|---|---|
| 核心定位 | 高性能包管理器 | 国内镜像客户端 |
| 主要解决问题 | 磁盘空间浪费、安装速度慢、幽灵依赖 | 官方源下载超时、网络不稳定 |
| 底层原理 | 使用硬链接 和内容寻址存储 | 本质是 npm 命令 + 淘宝镜像源参数 |
| 磁盘占用 | 极低 (所有项目共享同一个全局仓库) | 高 (与 npm 相同,每个项目独立复制文件) |
| 依赖结构 | 严格隔离 (通过符号链接),杜绝幽灵依赖 | 扁平结构 (与 npm 一致),可能存在幽灵依赖 |
| 开发者 | pnpm 社区 (Zoltan Kochan 等) | 阿里巴巴 (淘宝前端团队) |
1.3.2、深度解析
pnpm:磁盘空间的"压缩大师"
pnpm (Performant npm) 是一个完全重写的包管理器。
- 原理 :它在全局维护一个内容寻址的存储仓库。当你安装
lodash时,pnpm会把文件存到全局仓库,然后在项目node_modules中通过硬链接指向它,而不是复制文件。 - 优势 :
- 超级省空间 :如果你电脑上有 100 个项目都用
lodash,npm会存 100 份,而pnpm只存 1 份。 - 安装极快:因为大部分文件是硬链接,不需要重复下载和解压。
- 严格的依赖结构 :它不会像
npm那样把所有包都提升到根目录,这使得你的代码必须显式声明依赖,避免了"明明没安装却能用"的幽灵依赖问题。
- 超级省空间 :如果你电脑上有 100 个项目都用
cnpm:国内网络的"加速器"
cnpm 并不是一个新的包管理工具,它本质上就是 npm。
- 原理 :它是一个简单的 Shell 脚本或命令行工具,运行
cnpm install实际上等于运行npm install --registry=https://registry.npmmirror.com。 - 优势 :
- 解决超时 :将下载源指向国内的阿里云服务器,解决了连接
registry.npmjs.org经常超时的问题。 - 零学习成本 :用法和
npm一模一样。
- 解决超时 :将下载源指向国内的阿里云服务器,解决了连接
- 劣势 :它的磁盘占用和安装逻辑与
npm完全一致,没有pnpm的性能优化。
1.4、最佳实践:强强联合
你其实不需要在两者之间二选一,因为它们解决的是不同维度的问题。
最推荐的方案是:使用 pnpm + 切换国内源。
这样既能享受 pnpm 的磁盘节省和速度优势,又能利用国内镜像解决网络超时问题。
1.5、pnpm 设置方法:
1.5.1、安装 pnpm (如果还没装):
npm install -g pnpm
1.5.2、设置国内源 (让 pnpm 飞起来):
pnpm config set registry https://registry.npmmirror.com
设置完成后,你既拥有了 pnpm 的高性能,又拥有了 cnpm 的网络速度,完全不再需要安装 cnpm 了。
二、CommonJS和ES Module模块化
2.1、CommonJS:服务端的同步哲学
CommonJS 是为服务器端(非浏览器环境)设计的模块化规范,Node.js 是其最典型的实现。
- 核心思想 :采用同步加载的方式。因为在服务器端,模块文件都存储在本地磁盘,读取速度很快,同步阻塞不会成为性能瓶颈。
- 加载时机 :运行时加载 。依赖关系在代码执行到
require()语句时才被处理。 - 语法特点 :
- 使用
require()函数来导入模块。 - 使用
module.exports或exports对象来导出模块。
- 使用
- 适用场景 :主要用于 Node.js 后端开发。在浏览器中直接使用会阻塞页面渲染,通常需要借助 Webpack、Browserify 等工具进行打包转换。
javascript
// 导出:math.js (模块定义)
function add(a, b) {
return a + b;
}
module.exports = { add };
// 导入:app.js (模块使用)
const math = require('./math.js'); // 同步加载
console.log(math.add(2, 3)); // 输出 5
2.2、走向统一:ES Module
随着 ECMAScript 2015 (ES6) 标准的发布,JavaScript 拥有了官方的模块化标准------ES Module (ESM) 。它结合了 CommonJS 的简洁语法和 AMD 的异步能力(通过动态 import()),并支持静态分析以实现 Tree Shaking 等优化,已成为现代前端和 Node.js 开发的统一标准。
javascript
//导出: math.js (ES Module)
export function add(a, b) { return a + b; }
// 导入:app.js (ES Module)
import { add } from './math.js'; // 静态声明,编译时分析
console.log(add(2, 3));
2.3、ES Module和CommonJS有什么区别?
简单来说,CommonJS 是为服务器端(Node.js)设计的传统规范,而 ES Module 是为浏览器和服务端统一设计的现代标准。
以下是它们的核心区别:
2.3.1、语法差异
这是最直观的区别。
- CommonJS : 使用 require() 函数导入模块,使用 module.exports 或 exports 对象导出模块。
- ES Module : 使用 import 语句导入模块,使用 export 或 export default 语句导出模块。
2.3.2、加载机制与执行时机
这是两者最核心的区别,决定了它们的运行方式和性能。
-
CommonJS (运行时加载):
- 动态加载 :模块在代码运行时 被加载。当执行到
require()语句时,才会去读取、执行模块文件,并返回其module.exports对象。 - 同步加载:加载过程是阻塞的,必须等待模块加载并执行完毕,后续代码才会继续执行。这在服务器端(文件在本地)不是问题,但在浏览器端(需要网络请求)会严重阻塞页面渲染。
- 动态加载 :模块在代码运行时 被加载。当执行到
-
ES Module (编译时加载):
- 静态分析 :模块的依赖关系在代码编译阶段 就已经被确定。
import和export语句必须位于模块顶层,不能写在条件语句或函数内部。这使得构建工具(如 Webpack, Vite)可以在打包前就分析出完整的依赖图。 - 异步加载 :在浏览器中,ESM 默认是异步加载的,不会阻塞 HTML 解析和页面渲染。Node.js 中也支持通过动态
import()实现异步加载。
- 静态分析 :模块的依赖关系在代码编译阶段 就已经被确定。
2.3.3、环境支持
- CommonJS : 是 Node.js 的默认模块系统(
.js文件)。浏览器不原生支持,需要通过 Webpack 等工具打包转换。在 package.json 中设置 "type": "commonjs" 来启用。 - ES Module : 是浏览器的原生标准 (使用
<script type="module">)。在 Node.js 中,需要通过.mjs扩展名或在 package.json 中设置**"type": "module"** 来启用。
2.4、核心区别速查表
| 特性 | CommonJS (CJS) | ES Module (ESM) |
|---|---|---|
| 语法 | require / module.exports |
import / export |
| 加载时机 | 运行时加载 (动态) | 编译时加载 (静态) |
| 加载方式 | 同步加载 | 异步加载 (浏览器/动态import) |
| 导出模式 | 值的拷贝 (快照) | 值的引用 (实时绑定) |
| Tree Shaking | 不支持 | 支持 |
顶层 this |
指向 module.exports |
undefined (严格模式) |
| 浏览器支持 | 需打包工具转换 | 原生支持 |
2.5、如何选择?
- 新项目 :优先使用 ES Module。它是未来的标准,拥有更好的性能优化(Tree Shaking)、更清晰的语法和跨平台能力。
- Node.js 旧项目/库 :可能仍需使用 CommonJS 以保持兼容性。
- 混合使用 :在现代开发中,经常需要在两者之间互操作。ESM 可以导入 CJS 模块(通常作为默认导入),而 CJS 模块可以通过动态
import()来异步加载 ESM 模块。
三、Koa创建HTTP服务器,路由管理
在 Koa 框架中,路由管理是构建 Web 应用的核心环节。由于 Koa 本身是一个极简的框架,它不内置路由功能 ,而是依赖第三方库来实现。目前业界最标准、最常用的方案是使用 @koa/router (它是早期 koa-router 的维护版本)。
🛠️ 3.1. 路由库安装
首先,你需要安装核心依赖。除了 koa 本身,还需要安装路由库 koa/router和用于解析 POST 请求体的中间件koa-bodyparser。
koa-router 兼容koa1 和 koa2 的历史版本
@koa/router 专为koa2设计的新版本,推荐使用
npm i @koa/router
🚀 3.2. 基础路由用法
在最简单的场景下,你可以在主文件中直接定义路由。
核心概念:
- router.get/post/put/delete: 对应不同的 HTTP 请求方法。
- ctx.params : 获取 URL 路径中的参数(如
/users/:id)。 - ctx.query : 获取 URL 查询字符串参数(如
/search?q=koa)。 - ctx.request.body : 获取 POST 请求的数据(需配合
koa-bodyparser)。
javascript
//引入koa模块
const Koa = require('koa');
// 引入路由库
const Router = require('@koa/router');
const app = new Koa(); //创建应用程序实例
const router = new Router({prefix:'/api'}); //实例化一个Router对象,并设置一个路由访问前缀
// 中间件对路由的引用
app.use(router.routes()).use(router.allowedMethods());
// 向客户端提供资源
// http://localhost:3000/api/getInfo
router.get('/getInfo',ctx=> {
ctx.body = {
code:200,
message:'success',
list:[
{id:1,value:'v1'},
{id:2,value:'v2'},
{id:3,value:'v3'},
]
}
})
//启动成功,访问端口号3000
app.listen(3000, () => {
console.log('Server is running on http://localhost:3000');
});

四、中间件,koa-cors处理跨域请求
后端接口 : http://localhost:3000/api/getInfo
🔖4.1.前端解决跨域
Vite 代理(解决跨域)
vite.config.js
javascript
export default defineConfig({
server: {
open:true,
// port:8088,
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
// rewrite: path => path.replace(/^\/api/, '')
}
}
}
})
javascript
onMounted(async() => {
try {
const response = await axios.get('/api/getInfo');
console.log('response',response);
} catch (error) {
console.error('获取数据失败:', error);
}
})
🎗️4.2.后端解决跨域
前端不配置vite.config.js时,后端处理跨域。
4.2.1 前端简单封装个axios.js请求
javascript
// axios 二次封装
// 1、导入核心库
import axios from "axios";
// 2、配置路径
axios.defaults.baseURL = 'http://localhost:3000'
// 3、请求拦截器
axios.interceptors.request.use(config =>config);
// 4、响应拦截器
axios.interceptors.response.use(
res =>res,
error=>Promise.reject(error)
);
// 5、导出
export default axios;
在 Koa2 中配置跨域(CORS)最简单、最标准的方式是使用官方推荐的中间件 @koa/cors(或者 koa2-cors)。使用 @koa/cors 中间件(强烈推荐)
以下是详细的配置步骤:
4.2.1 安装依赖
npm install @koa/cors
或安装
npm install koa2-cors
4.2.2 在项目中使用
在你的 Koa 入口文件(通常是 app.js 或 index.js)中引入并配置。
1、基础配置(允许所有跨域):
javascript
const Koa = require('koa');
const cors = require('@koa/cors');
const app = new Koa();
// 注册 cors 中间件(必须放在路由之前)
app.use(cors());
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
2、高级配置(带 Cookie 且限制域名): 如果你前端请求需要携带 Cookie(如 axios.defaults.withCredentials = true),那么跨域的 Origin 不能设置为 *,必须指定具体的域名,并且设置 credentials: true。
关键配置项总结
表格
| 配置项 | 说明 | 注意事项 |
|---|---|---|
origin |
指定允许跨域访问的源。 | 当 credentials: true 时,不能 设置为 *,必须是具体的域名。 |
credentials |
是否允许浏览器携带凭证(如 Cookie)。 | 设置为 true 后,前端也必须设置 withCredentials: true。 |
allowMethods |
允许的 HTTP 请求方法。 | 通常包括 GET, POST, PUT, DELETE, OPTIONS。 |
allowHeaders |
允许的请求头字段。 | 通常包括 Content-Type, Authorization。 |
maxAge |
预检请求(OPTIONS)的缓存时间(秒)。 | 可以减少不必要的预检请求次数。 |
javascript
const Koa = require('koa');
const cors = require('@koa/cors');
const app = new Koa();
app.use(cors({
// 动态设置 Origin
origin: function (ctx) {
// 白名单
const whiteList = ['http://localhost:8080', 'https://www.yourdomain.com'];
const requestOrigin = ctx.request.header.origin;
// 如果请求源在白名单内,则返回该请求源;否则不允许跨域
if (whiteList.includes(requestOrigin)) {
return requestOrigin;
}
return false;
// 如果想允许所有域名并且携带cookie,可以直接返回请求源(不安全,仅用于开发环境)
// return requestOrigin || '*';
},
// 允许的请求头
allowHeaders: ['Content-Type', 'Authorization', 'Accept'],
// 允许的请求方法
allowMethods: ['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS'],
// 允许携带凭证(Cookie)
credentials: true,
// 预检请求的有效期,单位是秒,20天
maxAge: 86400 * 20,
}));
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
javascript
// koa请求跨域配置(注意:一定要在路由使用前设置)
app.use(
cors({
origin:function(ctx){ //指定允许跨域访问的源。
return ctx.request.headers.origin || ""; //动态获取地址 //当 credentials: true 时,不能设置为 *,必须是具体的域名。
},
credentials:true,
exposeHeaders:["WWW-Authenticate","Server-Authorization"],
allowMethods:["GET","POST","PUT","DELETE","OPTIONS"],
allowHeaders:["Content-Type","Authorization","Accept"]
})
)
五、API开发
5.1、GET请求
javascript
router.get('/getInfo',ctx=> {
ctx.body = {
code:200,
message:'success',
list:[
{id:1,value:'v1'},
{id:2,value:'v2'},
{id:3,value:'v3'},
]
}
})
5.2、POST请求
在 Koa 中,请求体解析是一个常见的需求,但 Koa 核心库本身并不提供解析功能。你需要使用中间件来处理。
关于请求体解析,主要有两个流行的库:koa-bodyparser 和 koa-body。它们功能相似,但有一些关键区别。
koa-bodyparser: 这是最经典的请求体解析中间件,轻量且专注于解析 JSON、表单和文本数据。但它不支持文件上传。koa-body: 这是一个功能更全的中间件,它不仅支持 JSON 和表单解析,还支持multipart/form-data格式,可以直接处理文件上传 。(文件上传中间件:@koa/multer)
你可以根据需求选择安装其中一个。
5.2.1 安装 koa-bodyparser (适用于纯数据解析)
安装依赖
npm install koa-bodyparser
koa-bodyparser 基础用法
javascript
const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
// 1. 必须在路由之前使用
app.use(bodyParser({
// 允许解析的类型,默认为 ['json', 'form']
enableTypes: ['json', 'form', 'text'],
// JSON 请求体大小限制,默认 '1mb'
jsonLimit: '2mb',
// 表单请求体大小限制,默认 '56kb'
formLimit: '100kb',
}));
//请求跨域设置
// 2. 定义路由
app.use(async (ctx) => {
// 解析后的数据会挂载到 ctx.request.body 上
console.log(ctx.request.body);
ctx.body = { message: '数据接收成功', data: ctx.request.body };
});
app.listen(3000);
5.2.2 安装 koa-body (适用于需要文件上传的场景)
安装依赖
npm install koa-body
koa-body 进阶用法 (支持文件上传)
javascript
const Koa = require('koa');
const { koaBody } = require('koa-body'); // 注意解构
const app = new Koa();
// 1. 使用 koa-body 中间件
app.use(koaBody({
// 允许解析的类型
multipart: true, // 支持 multipart/form-data (文件上传)
urlencoded: true, // 支持 application/x-www-form-urlencoded
json: true, // 支持 application/json
text: true, // 支持 text/plain
// 限制请求体大小
jsonLimit: '2mb',
formLimit: '100kb',
// 文件上传相关配置
// formidable: { uploadDir: './uploads' } // 可以配置 formidable 的选项,如上传目录
}));
// 2. 定义路由
app.use(async (ctx) => {
// 无论是 JSON 数据还是表单数据,都可以通过 ctx.request.body 获取
console.log(ctx.request.body);
// 如果是文件上传,文件信息会在 ctx.request.body.files 中
// 注意:koa-body 的不同版本,文件信息的位置可能略有不同,请参考其官方文档
if (ctx.request.body.files) {
console.log('上传的文件:', ctx.request.body.files);
}
ctx.body = { message: '请求处理成功', data: ctx.request.body };
});
app.listen(3000);
5.2.3 两者核心要点与对比
- 中间件顺序 : 无论使用哪个库,都必须 在定义路由 (
router.routes()) 之前使用,否则无法解析请求体。 - 数据获取 : 解析后的数据都会挂载在
ctx.request.body上,你可以像访问普通对象一样访问它。 - 功能选择 :
- 如果你的项目不需要文件上传 ,
koa-bodyparser足够用且更轻量。 - 如果你的项目需要处理文件上传 ,请直接使用
koa-body,它集成了formidable库的功能,一站式解决。
- 如果你的项目不需要文件上传 ,
表格
| 特性 | koa-bodyparser |
koa-body |
|---|---|---|
| JSON 解析 | ✅ 支持 | ✅ 支持 |
| 表单解析 | ✅ 支持 | ✅ 支持 |
| 文件上传 | ❌ 不支持 | ✅ 支持 |
| 主要依赖 | co-body |
formidable |
| 适用场景 | 纯 API 数据交互 | 需要处理文件上传的复杂表单 |
六、用 Node.js 包 mysql2 来读写 MySQL 数据库
在 Node.js 中处理 MySQL 数据库,通常涉及安装驱动 、建立连接 、执行 SQL 语句 以及处理数据这几个核心步骤。
虽然原生的 mysql 包很常用,但我强烈建议使用 mysql2,因为它支持 Promise(配合 async/await 使用更方便)、性能更好且支持连接池。
6.1、安装驱动
npm install mysql2
6.2、 建立数据库连接
在生产环境中,我们通常使用连接池而不是单例连接。连接池可以复用连接,提高并发性能,避免频繁创建和销毁 TCP 连接的开销。
创建一个数据库配置文件(例如 db.js):
javascript
const mysql = require('mysql2/promise'); // 引入 promise 版本
// 创建连接池
const pool = mysql.createPool({
host: 'localhost', // 数据库地址
user: 'root', // 用户名
password: '123456', // 密码
database: 'test_db', // 数据库名
port: 3306, // 端口
waitForConnections: true,
connectionLimit: 10, // 最大连接数
queueLimit: 0
});
module.exports = pool;
6.3、 执行 CRUD 操作 (增删改查)
在 Node.js 中操作 MySQL,最核心的是使用 参数化查询 (使用 ? 占位符),这能有效防止 SQL 注入攻击。
6.3.1、🔍 查询数据
使用 pool.query 执行 SELECT 语句。
javascript
const pool = require('./db'); // 引入上面的连接池
async function getUsers() {
try {
const [rows] = await pool.query('SELECT * FROM users WHERE status = ?', ['active']);
console.log('查询结果:', rows);
return rows;
} catch (err) {
console.error('查询失败:', err);
}
}
6.3.2、✏️ 更新数据
使用 UPDATE 语句。
javascript
async function updateUser(id, newName) {
const sql = 'UPDATE users SET name = ? WHERE id = ?';
const values = [newName, id];
try {
const [result] = await pool.query(sql, values);
console.log('更新影响的行数:', result.affectedRows);
} catch (err) {
console.error('更新失败:', err);
}
}
6.3.3、📝 插入数据
使用 INSERT 语句,并通过对象或数组传递数据。
javascript
async function addUser(userData) {
const sql = 'INSERT INTO users (name, email, age) VALUES (?, ?, ?)';
const values = [userData.name, userData.email, userData.age];
try {
const [result] = await pool.query(sql, values);
console.log('插入成功,ID:', result.insertId);
return result;
} catch (err) {
console.error('插入失败:', err);
}
}
6.3.4、🗑️ 删除记录
使用 DELETE 语句。
javascript
async function deleteUser(id) {
const sql = 'DELETE FROM users WHERE id = ?';
try {
const [result] = await pool.query(sql, [id]);
console.log('删除影响的行数:', result.affectedRows);
} catch (err) {
console.error('删除失败:', err);
}
}
6.4、结合 Koa 使用
结合 Koa 项目,你可以这样封装一个 API 接口:
javascript
const Koa = require('koa');
const Router = require('@koa/router');
const pool = require('./db'); // 引入数据库连接池
const app = new Koa();
const router = new Router();
// 获取用户列表接口
router.get('/users', async (ctx) => {
try {
const [rows] = await pool.query('SELECT id, name, email FROM users');
ctx.body = {
code: 200,
list: rows
};
} catch (error) {
ctx.status = 500;
ctx.body = { code: 500, message: '数据库查询错误' };
}
});
// 添加用户接口
router.post('/users', async (ctx) => {
const { name, email } = ctx.request.body;
try {
const [result] = await pool.query('INSERT INTO users (name, email) VALUES (?, ?)', [name, email]);
ctx.body = {
code: 200,
message: '添加成功',
id: result.insertId
};
} catch (error) {
ctx.status = 500;
ctx.body = { code: 500, message: '添加失败' };
}
});
app.use(router.routes()).use(router.allowedMethods());
app.listen(3000);
6.5、 关键注意事项
- 防止 SQL 注入 :永远不要使用字符串拼接来构建 SQL 语句(例如
'SELECT * FROM users WHERE id = ' + userId)。必须 使用?占位符和参数数组。 - 连接池管理 :使用 createPool 而不是
createConnection,这样 Node.js 会自动管理连接的复用和释放。 - 错误处理 :数据库操作是异步的,务必使用 try...catch 捕获错误,避免程序崩溃。
- 环境配置 :数据库的账号密码等敏感信息,建议使用
dotenv包读取.env文件,不要硬编码在代码里。