Skip to content

vextjs/vext

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

72 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

VextJS

License: MIT Node.js TypeScript

ไธ€ไธช็ŽฐไปฃๅŒ–็š„ Node.js Web ๆก†ๆžถ๏ผŒๅผ€็ฎฑๅณ็”จ๏ผŒไธ“ไธบๆž„ๅปบ้ซ˜ๆ€ง่ƒฝ RESTful API ่€Œ่ฎพ่ฎกใ€‚

VextJS ๆไพ› Adapter ๆžถๆž„๏ผˆๅบ•ๅฑ‚ๅฏๆ›ฟๆข๏ผ‰ใ€ๆ’ไปถ็ณป็ปŸใ€็บฆๅฎšๅผ่ทฏ็”ฑใ€ๆœๅŠก่‡ชๅŠจๆณจๅ…ฅใ€ๅ‚ๆ•ฐๆ ก้ชŒใ€OpenAPI ๆ–‡ๆกฃ่‡ชๅŠจ็”Ÿๆˆ็ญ‰ไผไธš็บง็‰นๆ€ง๏ผŒ่ฎฉไฝ ไธ“ๆณจไบŽไธšๅŠก้€ป่พ‘ใ€‚


โœจ ็‰นๆ€ง

  • ๐Ÿ”Œ Adapter ๆžถๆž„ โ€” ๅบ•ๅฑ‚ HTTP ๆก†ๆžถๅฏๆ›ฟๆข๏ผˆ้ป˜่ฎค Native Adapter๏ผŒ้›ถๅค–้ƒจไพ่ต–๏ผ‰๏ผŒไธšๅŠกไปฃ็ ๆ— ้œ€ๆ”นๅŠจ
  • ๐Ÿ“ ็บฆๅฎšๅผ่ทฏ็”ฑ โ€” src/routes/ ไธ‹็š„ๆ–‡ไปถ่‡ชๅŠจๆ‰ซๆๅŠ ่ฝฝ๏ผŒๆ–‡ไปถ่ทฏๅพ„ๅณ่ทฏ็”ฑๅ‰็ผ€
  • ๐Ÿงฉ ๆ’ไปถ็ณป็ปŸ โ€” ๆ‹“ๆ‰‘ๆŽ’ๅบใ€ไพ่ต–ๅฃฐๆ˜Žใ€็”Ÿๅ‘ฝๅ‘จๆœŸ้’ฉๅญ๏ผŒ่ฝปๆพๆ‰ฉๅฑ•ๆก†ๆžถ่ƒฝๅŠ›
  • ๐Ÿ’‰ ๆœๅŠก่‡ชๅŠจๆณจๅ…ฅ โ€” src/services/ ไธ‹็š„ class ่‡ชๅŠจๅฎžไพ‹ๅŒ–ๅนถๆŒ‚่ฝฝๅˆฐ app.services
  • ๐Ÿ›ก๏ธ ๅ‚ๆ•ฐๆ ก้ชŒ โ€” ้›†ๆˆ schema-dsl๏ผŒๅฃฐๆ˜Žๅผๆ ก้ชŒ + i18n ้”™่ฏฏๆถˆๆฏ
  • ๐Ÿ“– OpenAPI ๆ–‡ๆกฃ โ€” ่ทฏ็”ฑๅ…ƒไฟกๆฏ่‡ชๅŠจๆ”ถ้›†๏ผŒ็”Ÿๆˆ OpenAPI 3.1 JSON
  • ๐Ÿ”ฅ ๅผ€ๅ‘ๆจกๅผ็ƒญ้‡่ฝฝ โ€” ไธ‰ๅฑ‚้‡่ฝฝ็ญ–็•ฅ๏ผˆSoft Reload + Cold Restart๏ผ‰๏ผŒๆฏซ็ง’็บงๅ้ฆˆ
  • ๐Ÿ—๏ธ ๅ†…็ฝฎไธญ้—ดไปถ โ€” requestIdใ€CORSใ€bodyParserใ€rateLimitใ€accessLogใ€responseWrapper ๅผ€็ฎฑๅณ็”จ
  • โšก ่ทฏ็”ฑ็ผ“ๅญ˜ โ€” ๅฃฐๆ˜Žๅผ cache: 60๏ผŒLRU ๅ†…ๅญ˜ๅญ˜ๅ‚จ๏ผŒๆ ‡็ญพๅคฑๆ•ˆ๏ผŒVary headers๏ผŒๆกไปถ็ผ“ๅญ˜
  • ๐ŸŒ i18n ๆ”ฏๆŒ โ€” src/locales/ ่ฏญ่จ€ๅŒ…่‡ชๅŠจๅŠ ่ฝฝ๏ผŒๆ ก้ชŒ้”™่ฏฏๆถˆๆฏๅคš่ฏญ่จ€
  • ๐Ÿงช ๆต‹่ฏ•ๅทฅๅ…ท โ€” ๅ†…็ฝฎ createTestApp๏ผŒๆ— ้œ€ๅฏๅŠจ HTTP ๆœๅŠกๅ™จๅณๅฏๆต‹่ฏ•่ทฏ็”ฑ
  • โšก TypeScript ๅŽŸ็”Ÿ โ€” ๅฎŒๆ•ด็ฑปๅž‹ๅฎšไน‰๏ผŒๆž่‡ด็š„ IDE ่กฅๅ…จไฝ“้ชŒ
  • ๐Ÿ“ฆ ้›ถ้…็ฝฎๅฏๅŠจ โ€” ๅˆ็†็š„้ป˜่ฎค้…็ฝฎ๏ผŒๆœ€ๅฐ‘ 5 ไธชๅญ—ๆฎตๅณๅฏ่ฟ่กŒ

๐Ÿ“ฆ ๅฎ‰่ฃ…

npm install vextjs

้ป˜่ฎคไฝฟ็”จ Native Adapter๏ผˆๅŸบไบŽ Node.js http.createServer + find-my-way radix trie๏ผ‰๏ผŒ้›ถๅค–้ƒจ HTTP ๆก†ๆžถไพ่ต–๏ผŒๆ€ง่ƒฝๆœ€ไผ˜ใ€‚

ๅฆ‚้œ€ไฝฟ็”จๅ…ถไป– adapter๏ผŒ่ฏท้ขๅค–ๅฎ‰่ฃ…ๅฏนๅบ”ๆก†ๆžถๅŒ…๏ผš

# Fastify adapter
npm install fastify

# Hono adapter
npm install hono @hono/node-server

# Express adapter
npm install express

# Koa adapter
npm install koa

็„ถๅŽๅœจ้…็ฝฎไธญๆŒ‡ๅฎš adapter๏ผš

// src/config/default.js
export default {
  adapter: "fastify", // 'native' (้ป˜่ฎค) | 'fastify' | 'hono' | 'express' | 'koa'
};

โšก ๆ€ง่ƒฝ

VextJS ๆไพ› 5 ็ง adapter๏ผŒ่ฆ†็›–ไธๅŒไฝฟ็”จๅœบๆ™ฏใ€‚ไปฅไธ‹ไธบๅŸบๅ‡†ๆต‹่ฏ•ๆ•ฐๆฎ๏ผˆ5 ่ฝฎๅ–ไธญไฝๆ•ฐ๏ผ‰๏ผš

Native vs Fastify๏ผˆๆ ธๅฟƒๅฏนๆฏ”๏ผ‰

ๅœบๆ™ฏ Raw Native Vext Native Raw Fastify Vext Fastify Native ้ข†ๅ…ˆ
JSON ๅ“ๅบ” 44,932 36,819 45,619 29,203 +26.1%
่ทฏ็”ฑๅ‚ๆ•ฐ 43,859 36,755 43,676 24,386 +50.7%
ไธญ้—ดไปถ้“พ 28,337 31,698 41,286 22,719 +39.5%

Vext-Native ๅœจๆ‰€ๆœ‰ๅœบๆ™ฏ้ข†ๅ…ˆ Vext-Fastify 26~51%๏ผˆไธญ้—ดไปถ้“พๅœบๆ™ฏ Vext-Native ็”š่‡ณ่ถ…่ถŠ่ฃธ่ท‘ Native +11.9%๏ผ‰ใ€‚

ๅ…จ Adapter ๆ€ง่ƒฝๆฆ‚่งˆ๏ผˆJSON ๅœบๆ™ฏ๏ผ‰

Adapter Vext RPS Overhead ้ขๅค–ไพ่ต– ๆŽจ่ๅœบๆ™ฏ
Native โญ 36,819 18.1% โœ… ้›ถไพ่ต– ้ป˜่ฎคๆŽจ่๏ผŒๆ€ง่ƒฝๆœ€ไผ˜
Express 30,974 -3.7% express ๅทฒๆœ‰ Express ็”Ÿๆ€้œ€ๅค็”จ
Fastify 29,203 36.0% fastify ้œ€่ฆ Fastify ็”Ÿๆ€ๆ’ไปถ
Koa 22,488 29.4% koa ๅทฒๆœ‰ Koa ไธญ้—ดไปถ้œ€ๅค็”จ
Hono 15,684 24.2% hono @hono/node-server Web Standard API ๅ…ผๅฎน

ๆต‹่ฏ•็Žฏๅขƒ: Node.js v24.14.0 + autocannon๏ผˆ50 connections, 10 pipelining, 10s ร— 5 ่ฝฎๅ–ไธญไฝๆ•ฐ, Windows x64, i7-9700, 32GB RAM๏ผŒ2026-03-23๏ผ‰

Native adapter ไฝฟ็”จ Node.js ๅ†…็ฝฎ http.createServer + find-my-way radix trie ่ทฏ็”ฑ๏ผŒๆ˜ฏ VextJS ๅ”ฏไธ€ไธไพ่ต–็ฌฌไธ‰ๆ–น HTTP ๆก†ๆžถ็š„ adapterใ€‚Vext-Native ๆฏ” Vext-Fastify ๅฟซ 26.1%๏ผˆJSON๏ผ‰/ 39.5%๏ผˆไธญ้—ดไปถ้“พ๏ผ‰๏ผŒๆฏ” Vext-Hono ๅฟซ 135%๏ผŒๆฏ” Vext-Express ๅฟซ 18.9%๏ผˆExpress v5 + Node.js v24 ๆ€ง่ƒฝๅคงๅน…ๆๅ‡๏ผ‰ใ€‚ๆ‰€ๆœ‰ๆ•ฐๆฎ็ป 5 ่ฝฎไธญไฝๆ•ฐ้ชŒ่ฏ๏ผŒ็ปๅคงๅคšๆ•ฐ CV๏ผˆๅ˜ๅผ‚็ณปๆ•ฐ๏ผ‰< 3.5%ใ€‚

Adapter ้€‰ๆ‹ฉๆŒ‡ๅ—

ไฝ ็š„ๅœบๆ™ฏ ๆŽจ่ Adapter ็†็”ฑ
ๆ–ฐ้กน็›ฎ๏ผŒๆ— ๅކๅฒๅŒ…่ขฑ native๏ผˆ้ป˜่ฎค๏ผ‰ ้›ถๅค–้ƒจไพ่ต– + ๆ€ง่ƒฝๆœ€ไผ˜
ๅทฒๆœ‰ Fastify ๆ’ไปถ็”Ÿๆ€ fastify ๅฏๅค็”จ Fastify ๆ’ไปถ๏ผˆๅฆ‚ fastify-multipart๏ผ‰
ๅทฒๆœ‰ Express ไธญ้—ดไปถ express ๅ…ผๅฎนๅบžๅคง็š„ Express ไธญ้—ดไปถ็”Ÿๆ€
ๅทฒๆœ‰ Koa ไธญ้—ดไปถ koa ๅ…ผๅฎน Koa ไธญ้—ดไปถ
้œ€่ฆ Web Standard API hono Hono ๆ”ฏๆŒ Request/Response Web API ๆ ‡ๅ‡†

๐Ÿš€ ๅฟซ้€Ÿๅผ€ๅง‹

1. ๅˆ›ๅปบ้กน็›ฎ็ป“ๆž„

my-app/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ config/
โ”‚   โ”‚   โ””โ”€โ”€ default.js       # ๅบ”็”จ้…็ฝฎ
โ”‚   โ””โ”€โ”€ routes/
โ”‚       โ””โ”€โ”€ index.js          # ่ทฏ็”ฑๅฎšไน‰
โ””โ”€โ”€ package.json

2. ้…็ฝฎ package.json

{
  "name": "my-app",
  "type": "module",
  "scripts": {
    "start": "vext start",
    "dev": "vext dev"
  },
  "dependencies": {
    "vextjs": "^0.2.3"
  }
}

3. ็ผ–ๅ†™้…็ฝฎ

// src/config/default.js
export default {
  port: 3000,
  host: "0.0.0.0",
  logger: {
    level: "info",
  },
  openapi: {
    enabled: true,
  },
};

๐Ÿ’ก ๅช้œ€ๅฃฐๆ˜Žไฝ ๅ…ณๅฟƒ็š„ๅญ—ๆฎต๏ผŒๅ…ถไป–ๅญ—ๆฎต๏ผˆrequestIdใ€corsใ€bodyParserใ€rateLimitใ€accessLog ็ญ‰๏ผ‰็”ฑๆก†ๆžถ่‡ชๅŠจ่กฅๅ…จ้ป˜่ฎคๅ€ผใ€‚

4. ็ผ–ๅ†™่ทฏ็”ฑ

// src/routes/index.js
import { defineRoutes } from "vextjs";

export default defineRoutes((app) => {
  app.get("/", {}, async (req, res) => {
    res.json({ message: "Hello, VextJS!" });
  });

  app.get("/health", {}, async (req, res) => {
    res.json({ status: "ok", uptime: process.uptime() });
  });
});

5. ๅฏๅŠจ

# ๅผ€ๅ‘ๆจกๅผ๏ผˆ็ƒญ้‡่ฝฝ๏ผ‰
npm run dev

# ็”Ÿไบงๆจกๅผ
npm start
# ้ชŒ่ฏ
curl http://localhost:3000/
# โ†’ {"code":0,"data":{"message":"Hello, VextJS!"}}

๐Ÿ–ฅ๏ธ CLI ๅ‘ฝไปค

VextJS ๆไพ›ๅ†…็ฝฎ CLI๏ผŒ้€š่ฟ‡ npx vext ๆˆ– package.json scripts ่ฐƒ็”จใ€‚

vext start โ€” ็”Ÿไบงๆจกๅผ

vext start                    # ไฝฟ็”จ้ป˜่ฎค้…็ฝฎๅฏๅŠจ
vext start --port 8080        # ๆŒ‡ๅฎš็ซฏๅฃ
vext start --host 127.0.0.1   # ๆŒ‡ๅฎš็›‘ๅฌๅœฐๅ€

ๅฏๅŠจๆต็จ‹๏ผšๆฃ€ๆต‹้กน็›ฎ็ป“ๆž„ โ†’ ๅŠ ่ฝฝ้…็ฝฎ โ†’ ๆณจๅ†Œๆ’ไปถ/ไธญ้—ดไปถ/ๆœๅŠก/่ทฏ็”ฑ โ†’ ๅฏๅŠจ HTTP ๆœๅŠกๅ™จใ€‚

ๅฆ‚ๆžœๅญ˜ๅœจ dist/ ็ผ–่ฏ‘ไบง็‰ฉ๏ผŒ่‡ชๅŠจไฝฟ็”จ็ผ–่ฏ‘ๅŽ็š„ JS ่ฟ่กŒ๏ผ›TypeScript ้กน็›ฎๆ—  dist/ ๆ—ถ่‡ชๅŠจ้€š่ฟ‡ tsx ๅŠ ่ฝฝใ€‚

vext dev โ€” ๅผ€ๅ‘ๆจกๅผ

vext dev                      # ๅฏๅŠจๅผ€ๅ‘ๆœๅŠกๅ™จ
vext dev --poll               # Docker / NFS ็Žฏๅขƒไฝฟ็”จ่ฝฎ่ฏขๆจกๅผ
vext dev --poll-interval 2000 # ่‡ชๅฎšไน‰่ฝฎ่ฏข้—ด้š”๏ผˆๆฏซ็ง’๏ผ‰
vext dev --debounce 50        # ่‡ชๅฎšไน‰้˜ฒๆŠ–้—ด้š”๏ผˆๆฏซ็ง’๏ผŒ้ป˜่ฎค 0 ไธๅผ€ๅฏ๏ผ‰
vext dev --no-hot             # ็ฆ็”จ Soft Reload๏ผŒๆ‰€ๆœ‰ๅ˜ๆ›ด่ตฐ Cold Restart
vext dev --clear              # ๆฏๆฌก้‡่ฝฝๅŽๆธ…็ฉบๆŽงๅˆถๅฐ

ไธ‰ๅฑ‚้‡่ฝฝ็ญ–็•ฅ๏ผš

Tier ่งฆๅ‘ๆกไปถ ๅŠจไฝœ ้€Ÿๅบฆ
T1 ไปฃ็ ไฟฎๆ”น๏ผˆmodify๏ผ‰ Soft Reload โ€” esbuild.transform() ็ƒญๆ›ฟๆข โšก ๆฏซ็ง’็บง
T2 ๆ–‡ไปถๆ–ฐๅขž / ๅˆ ้™ค Soft Reload โ€” esbuild ctx.rebuild() ้‡ๅปบ โšก ๆฏซ็ง’็บง
T3 ้…็ฝฎ / ๆ’ไปถ / .env ๅ˜ๆ›ด Cold Restart โ€” kill + fork ้‡ๅฏๅญ่ฟ›็จ‹ ๐Ÿ”„ ็ง’็บง

้”ฎ็›˜ๅฟซๆท้”ฎ๏ผš

ๆŒ‰้”ฎ ๅŠŸ่ƒฝ
r ๆ‰‹ๅŠจ Cold Restart
h ๆ‰‹ๅŠจ Soft Reload๏ผˆๅ…จ้‡๏ผ‰
c ๆธ…็ฉบๆŽงๅˆถๅฐ
? ๆ˜พ็คบๅธฎๅŠฉ
Ctrl+C ้€€ๅ‡บๅผ€ๅ‘ๆœๅŠกๅ™จ

vext build โ€” ๆž„ๅปบ

vext build                    # TypeScript ็ผ–่ฏ‘ไธบ JavaScript

๐Ÿ“ ้กน็›ฎ็ป“ๆž„

my-app/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ config/
โ”‚   โ”‚   โ”œโ”€โ”€ default.js        # ้ป˜่ฎค้…็ฝฎ
โ”‚   โ”‚   โ”œโ”€โ”€ production.js     # ็”Ÿไบง็Žฏๅขƒ่ฆ†็›–๏ผˆๅฏ้€‰๏ผ‰
โ”‚   โ”‚   โ””โ”€โ”€ local.js          # ๆœฌๅœฐ่ฆ†็›–๏ผŒไธๆไบคๅˆฐ Git๏ผˆๅฏ้€‰๏ผ‰
โ”‚   โ”œโ”€โ”€ routes/               # ่ทฏ็”ฑ็›ฎๅฝ•๏ผˆ่‡ชๅŠจๆ‰ซๆ๏ผ‰
โ”‚   โ”‚   โ”œโ”€โ”€ index.js          # โ†’ /
โ”‚   โ”‚   โ”œโ”€โ”€ users.js          # โ†’ /users
โ”‚   โ”‚   โ””โ”€โ”€ api/
โ”‚   โ”‚       โ””โ”€โ”€ posts.js      # โ†’ /api/posts
โ”‚   โ”œโ”€โ”€ services/             # ๆœๅŠกๅฑ‚๏ผˆ่‡ชๅŠจๆณจๅ…ฅๅˆฐ app.services๏ผ‰
โ”‚   โ”‚   โ”œโ”€โ”€ user.js           # โ†’ app.services.user
โ”‚   โ”‚   โ””โ”€โ”€ payment/
โ”‚   โ”‚       โ””โ”€โ”€ stripe.js     # โ†’ app.services.payment.stripe
โ”‚   โ”œโ”€โ”€ middlewares/           # ่‡ชๅฎšไน‰่ทฏ็”ฑ็บงไธญ้—ดไปถ
โ”‚   โ”œโ”€โ”€ plugins/              # ๆ’ไปถ๏ผˆๆ‹“ๆ‰‘ๆŽ’ๅบๅŠ ่ฝฝ๏ผ‰
โ”‚   โ”œโ”€โ”€ locales/              # i18n ่ฏญ่จ€ๅŒ…๏ผˆๅฏ้€‰๏ผ‰
โ”‚   โ”‚   โ”œโ”€โ”€ zh-CN.json
โ”‚   โ”‚   โ””โ”€โ”€ en.json
โ”‚   โ””โ”€โ”€ ...
โ”œโ”€โ”€ package.json
โ””โ”€โ”€ tsconfig.json             # TypeScript ้กน็›ฎ๏ผˆๅฏ้€‰๏ผ‰

๐Ÿ›ฃ๏ธ ่ทฏ็”ฑ

่ทฏ็”ฑไฝฟ็”จ defineRoutes ๅฎšไน‰๏ผŒๆ”ฏๆŒไธ‰ๆฎตๅผ (path, options, handler) ๅ’Œไธคๆฎตๅผ (path, handler) ่ฏญๆณ•ใ€‚

ๅŸบ็ก€่ทฏ็”ฑ

// src/routes/users.js
import { defineRoutes } from "vextjs";

export default defineRoutes((app) => {
  // ไธ‰ๆฎตๅผ๏ผšpath, options, handler
  app.get(
    "/list",
    {
      docs: { summary: "่Žทๅ–็”จๆˆทๅˆ—่กจ" },
      validate: {
        query: { page: "number:1-", limit: "number:1-100" },
      },
    },
    async (req, res) => {
      const { page, limit } = req.valid("query");
      const users = await app.services.user.findAll({ page, limit });
      res.json(users);
    },
  );

  // ไธคๆฎตๅผ๏ผšpath, handler๏ผˆๆ—  options๏ผ‰
  app.get("/:id", async (req, res) => {
    const user = await app.services.user.findById(req.params.id);
    res.json(user);
  });

  app.post(
    "/",
    {
      validate: {
        body: { name: "string:1-50", email: "email" },
      },
    },
    async (req, res) => {
      const user = await app.services.user.create(req.valid("body"));
      res.json(user, 201);
    },
  );

  app.delete("/:id", {}, async (req, res) => {
    await app.services.user.delete(req.params.id);
    res.json({ deleted: true });
  });
});

่ทฏ็”ฑๆ˜ ๅฐ„่ง„ๅˆ™

ๆ–‡ไปถ่ทฏๅพ„ ่ทฏ็”ฑๅ‰็ผ€
src/routes/index.js /
src/routes/users.js /users
src/routes/api/posts.js /api/posts
src/routes/api/v2/orders.js /api/v2/orders

่ทฏ็”ฑ้€‰้กน

app.get(
  "/protected",
  {
    // ่ทฏ็”ฑ็บงไธญ้—ดไปถๅผ•็”จ๏ผˆ้œ€ๅœจ config.middlewares ็™ฝๅๅ•ไธญๅฃฐๆ˜Ž๏ผ‰
    middlewares: ["auth", { name: "rbac", options: { roles: ["admin"] } }],

    // ๅ‚ๆ•ฐๆ ก้ชŒ๏ผˆschema-dsl ่ฏญๆณ•๏ผ‰
    validate: {
      query: { page: "number", limit: "number" },
      param: { id: "string" },
      body: { name: "string:1-100", email: "email" },
    },

    // OpenAPI ๆ–‡ๆกฃๅ…ƒไฟกๆฏ
    docs: {
      summary: "่Žทๅ–ๅ—ไฟๆŠค่ต„ๆบ",
      description: "้œ€่ฆ่ฎค่ฏๅ’Œ็ฎก็†ๅ‘˜่ง’่‰ฒ",
      tags: ["Admin"],
    },
  },
  handler,
);

๐Ÿ”ง ๆœๅŠกๅฑ‚

ๆœๅŠกๆ–‡ไปถๆ”พๅœจ src/services/ ไธ‹๏ผŒๅฏผๅ‡บไธ€ไธช class๏ผŒๆก†ๆžถ่‡ชๅŠจๅฎžไพ‹ๅŒ–ๅนถๆณจๅ…ฅๅˆฐ app.servicesใ€‚

// src/services/user.js
export default class UserService {
  constructor(app) {
    this.app = app;
    this.logger = app.logger;
  }

  async findAll({ page = 1, limit = 20 }) {
    this.logger.info(`Fetching users page=${page} limit=${limit}`);
    // ๆ•ฐๆฎๅบ“ๆŸฅ่ฏข...
    return { users: [], total: 0 };
  }

  async findById(id) {
    // ...
  }

  async create(data) {
    // ...
  }
}
// ๅœจ่ทฏ็”ฑไธญไฝฟ็”จ
app.get("/users", {}, async (req, res) => {
  const result = await app.services.user.findAll({ page: 1 });
  res.json(result);
});

ๅตŒๅฅ—ๆœๅŠก

services/
โ”œโ”€โ”€ user.js              โ†’ app.services.user
โ”œโ”€โ”€ user-profile.js      โ†’ app.services.userProfile
โ””โ”€โ”€ payment/
    โ”œโ”€โ”€ stripe.js         โ†’ app.services.payment.stripe
    โ””โ”€โ”€ alipay.js         โ†’ app.services.payment.alipay
  • ๆ–‡ไปถๅ kebab-case ่‡ชๅŠจ่ฝฌๆขไธบ camelCase
  • ไปฅ _ ๅผ€ๅคด็š„ๆ–‡ไปถ/็›ฎๅฝ•ไผš่ขซ่ทณ่ฟ‡
  • ๆก†ๆžถ่‡ชๅŠจๆฃ€ๆต‹ๆœๅŠกไน‹้—ด็š„ๅพช็Žฏไพ่ต–

๐Ÿงฉ ๆ’ไปถ

ๆ’ไปถ็”จไบŽๆ‰ฉๅฑ•ๆก†ๆžถ่ƒฝๅŠ›๏ผŒๆ”ฏๆŒไพ่ต–ๅฃฐๆ˜Žๅ’Œๆ‹“ๆ‰‘ๆŽ’ๅบๅŠ ่ฝฝใ€‚

// src/plugins/redis.js
import { definePlugin } from "vextjs";
import Redis from "ioredis";

export default definePlugin({
  name: "redis",

  // ๅฃฐๆ˜Žไพ่ต–๏ผˆๅฏ้€‰๏ผ‰๏ผŒๆก†ๆžถ่‡ชๅŠจๆŒ‰ๆ‹“ๆ‰‘้กบๅบๅŠ ่ฝฝ
  // dependencies: ['database'],

  async setup(app) {
    const redis = new Redis(app.config.redis);

    // ๆ‰ฉๅฑ• app ๅฏน่ฑก
    app.extend("cache", redis);

    // ๆณจๅ†Œๅ…จๅฑ€ไธญ้—ดไปถ
    app.use(async (req, res, next) => {
      req.cache = app.cache;
      await next();
    });

    // ๆณจๅ†Œๅ…ณ้—ญ้’ฉๅญ๏ผˆไผ˜้›…ๅ…ณ้—ญๆ—ถๆ‰ง่กŒ๏ผ‰
    app.onClose(async () => {
      await redis.quit();
      app.logger.info("Redis disconnected");
    });
  },
});
// ๅœจ่ทฏ็”ฑ/ๆœๅŠกไธญไฝฟ็”จๆ’ไปถๆ‰ฉๅฑ•็š„่ƒฝๅŠ›
const cached = await app.cache.get("user:123");

๐Ÿ›ก๏ธ ไธญ้—ดไปถ

ๅ†…็ฝฎไธญ้—ดไปถ

VextJS ๅ†…็ฝฎไปฅไธ‹ไธญ้—ดไปถ๏ผŒๅ…จ้ƒจๅผ€็ฎฑๅณ็”จ๏ผŒๆ— ้œ€ๆ‰‹ๅŠจๆณจๅ†Œ๏ผš

ไธญ้—ดไปถ ๅŠŸ่ƒฝ ้…็ฝฎๅญ—ๆฎต
requestId ไธบๆฏไธช่ฏทๆฑ‚็”Ÿๆˆๅ”ฏไธ€ ID config.requestId
cors ่ทจๅŸŸ่ต„ๆบๅ…ฑไบซ config.cors
bodyParser ่ฏทๆฑ‚ไฝ“่งฃๆž๏ผˆJSON / URLEncoded๏ผ‰ config.bodyParser
rateLimit ้€Ÿ็އ้™ๅˆถ config.rateLimit
responseWrapper ็ปŸไธ€ๅ“ๅบ”ๆ ผๅผ { code, data, message } config.response
accessLog ่ฏทๆฑ‚่ฎฟ้—ฎๆ—ฅๅฟ— config.accessLog
errorHandler ๅ…จๅฑ€้”™่ฏฏๅค„็† + 404 ๅ…œๅบ• โ€”

่‡ชๅฎšไน‰ไธญ้—ดไปถ

// src/middlewares/auth.js
import { defineMiddleware } from "vextjs";

export default defineMiddleware(async (req, res, next) => {
  const token = req.headers["authorization"];
  if (!token) {
    req.app.throw(401, "Unauthorized");
  }
  // ่งฃๆž token๏ผŒ่ฎพ็ฝฎ็”จๆˆทไฟกๆฏ...
  await next();
});

ๅทฅๅŽ‚ไธญ้—ดไปถ๏ผˆๅธฆ้…็ฝฎๅ‚ๆ•ฐ๏ผ‰

// src/middlewares/rbac.js
import { defineMiddlewareFactory } from "vextjs";

export default defineMiddlewareFactory((options) => {
  return async (req, res, next) => {
    if (!options.roles.includes(req.user?.role)) {
      req.app.throw(403, "Forbidden");
    }
    await next();
  };
});
// ๅœจ่ทฏ็”ฑไธญไฝฟ็”จ๏ผˆ้œ€ๅœจ config.middlewares ็™ฝๅๅ•ๅฃฐๆ˜Ž๏ผ‰
app.get(
  "/admin",
  {
    middlewares: [{ name: "rbac", options: { roles: ["admin"] } }],
  },
  handler,
);

โš™๏ธ ้…็ฝฎ

้…็ฝฎๆ–‡ไปถๆ”ฏๆŒไธ‰ๅฑ‚ๅˆๅนถ๏ผšdefault โ†’ {NODE_ENV} โ†’ localใ€‚

// src/config/default.js โ€” ้ป˜่ฎค้…็ฝฎ
export default {
  port: 3000,
  host: "0.0.0.0",
  logger: {
    level: "info", // 'debug' | 'info' | 'warn' | 'error' | 'silent'
  },
  cors: {
    origins: ["*"], // ๅ…่ฎธ็š„ๆฅๆบๅˆ—่กจ๏ผˆๆ•ฐ็ป„ๆ ผๅผ๏ผ‰
    methods: ["GET", "POST", "PUT", "PATCH", "DELETE"],
  },
  bodyParser: {
    maxBodySize: "1mb",
  },
  rateLimit: {
    max: 100, // ๆฏไธช็ช—ๅฃๆœŸๆœ€ๅคง่ฏทๆฑ‚ๆ•ฐ
    window: 60, // ็ช—ๅฃๆœŸๆ—ถ้•ฟ๏ผˆ็ง’๏ผŒๆ•ฐๅญ—๏ผ‰
  },
  requestId: {
    enabled: true,
    header: "X-Request-Id",
  },
  response: {
    hideInternalErrors: true, // ็”Ÿไบง็Žฏๅขƒ้š่—ๅ†…้ƒจ้”™่ฏฏ่ฏฆๆƒ…
  },
  openapi: {
    enabled: true,
    title: "My API",
    version: "1.0.0",
  },
  shutdown: {
    timeout: 10, // ไผ˜้›…ๅ…ณ้—ญ่ถ…ๆ—ถ๏ผˆ็ง’๏ผ‰
  },
};
// src/config/production.js โ€” ็”Ÿไบง็Žฏๅขƒ่ฆ†็›–
export default {
  logger: { level: "warn" },
  response: { hideInternalErrors: true },
};
// src/config/local.js โ€” ๆœฌๅœฐๅผ€ๅ‘่ฆ†็›–๏ผˆๅŠ ๅ…ฅ .gitignore๏ผ‰
export default {
  port: 4000,
  logger: { level: "debug" },
};

CLI ๅ‚ๆ•ฐ --port / --host ไผ˜ๅ…ˆ็บงๆœ€้ซ˜๏ผŒ่ฆ†็›–้…็ฝฎๆ–‡ไปถไธญ็š„ๅ€ผใ€‚


โœ… ๅ‚ๆ•ฐๆ ก้ชŒ

่ทฏ็”ฑ้€‰้กนไธญ็š„ validate ๅญ—ๆฎตไฝฟ็”จ schema-dsl ่ฏญๆณ•๏ผŒๅฃฐๆ˜Žๅผๆ ก้ชŒ่ฏทๆฑ‚ๅ‚ๆ•ฐใ€‚

app.post(
  "/users",
  {
    validate: {
      body: {
        name: "string:1-50", // ๅญ—็ฌฆไธฒ๏ผŒ้•ฟๅบฆ 1-50
        email: "email", // ้‚ฎ็ฎฑๆ ผๅผ
        age: "number:0-150?", // ๅฏ้€‰ๆ•ฐๅญ—๏ผŒ่Œƒๅ›ด 0-150
        role: "enum:admin,user,guest", // ๆžšไธพๅ€ผ
        tags: "[string]", // ๅญ—็ฌฆไธฒๆ•ฐ็ป„
      },
      query: {
        format: "enum:json,xml?", // ๅฏ้€‰ๆžšไธพ
      },
    },
  },
  async (req, res) => {
    const body = req.valid("body"); // ็ฑปๅž‹ๅฎ‰ๅ…จ็š„ๆ ก้ชŒๅŽๆ•ฐๆฎ
    const query = req.valid("query");
    // ...
  },
);

ๆ ก้ชŒๅคฑ่ดฅๆ—ถ่‡ชๅŠจ่ฟ”ๅ›ž 400 ้”™่ฏฏ๏ผŒๅŒ…ๅซ่ฏฆ็ป†็š„ๅญ—ๆฎต้”™่ฏฏไฟกๆฏใ€‚ๆ”ฏๆŒ i18n ๅคš่ฏญ่จ€้”™่ฏฏๆถˆๆฏใ€‚


โšก ่ทฏ็”ฑ็ผ“ๅญ˜

่ทฏ็”ฑ้€‰้กนไธญ็š„ cache ๅญ—ๆฎตๆไพ›ๅฃฐๆ˜Žๅผๅ“ๅบ”็ผ“ๅญ˜๏ผŒๆ”ฏๆŒๆ•ฐๅญ—็ฎ€ๅ†™ๆˆ–ๅฎŒๆ•ด้…็ฝฎๅฏน่ฑกใ€‚

// ๆ•ฐๅญ—็ฎ€ๅ†™๏ผš็ผ“ๅญ˜ 60 ็ง’
app.get("/products", { cache: 60 }, async (req, res) => {
  res.json(await db.getProducts());
});

// ๅฎŒๆ•ด้…็ฝฎ๏ผšTTL + Vary headers + ๆ ‡็ญพๅคฑๆ•ˆ
app.get(
  "/products",
  {
    cache: {
      ttl: 120,
      vary: ["accept-language"], // ไธๅŒ่ฏญ่จ€ๅ•็‹ฌ็ผ“ๅญ˜
      tags: ["products"], // ๆ ‡็ญพ๏ผˆ็”จไบŽๆ‰น้‡ๅคฑๆ•ˆ๏ผ‰
      condition: (req) => !req.query.refresh, // ๆกไปถ็ผ“ๅญ˜
    },
  },
  async (req, res) => {
    res.json(await db.getProducts());
  },
);

็ผ“ๅญ˜ๅ‘ฝไธญๆ—ถ่‡ชๅŠจ่ฎพ็ฝฎ X-Cache: HIT ๅ’Œ Cache-Control: public, max-age=N ๅ“ๅบ”ๅคดใ€‚

่ฟ่กŒๆ—ถ API

// ๆŒ‰ๆ ‡็ญพๆ‰น้‡ๅคฑๆ•ˆ
await app.cache.invalidate("products");

// ๆธ…็ฉบๆ‰€ๆœ‰็ผ“ๅญ˜
await app.cache.clear();

// ๆŸฅ็œ‹็ผ“ๅญ˜็ปŸ่ฎก
const stats = app.cache.stats();
// โ†’ { entries: 42, hits: 128, misses: 31, hitRate: 0.805 }

ๅ…จๅฑ€้…็ฝฎ

// src/config/default.js
export default {
  cache: {
    enabled: true, // ๆ˜ฏๅฆๅฏ็”จ๏ผˆ้ป˜่ฎค true๏ผ‰
    defaultTtl: 60, // ้ป˜่ฎค TTL ็ง’ๆ•ฐ
    maxEntries: 1000, // ๆœ€ๅคง็ผ“ๅญ˜ๆก็›ฎๆ•ฐ
  },
};

๐Ÿ“– OpenAPI ๆ–‡ๆกฃ

ๅฏ็”จ openapi.enabled: true ๅŽ๏ผŒๆก†ๆžถ่‡ชๅŠจไปŽ่ทฏ็”ฑๅ…ƒไฟกๆฏ็”Ÿๆˆ OpenAPI 3.1 ๆ–‡ๆกฃ๏ผŒๅนถๆไพ›ไบคไบ’ๅผๆ–‡ๆกฃ้กต้ขใ€‚

ๆ–‡ๆกฃ็ซฏ็‚น

็ซฏ็‚น ่ฏดๆ˜Ž
GET /openapi.json OpenAPI JSON spec๏ผˆไพ›ๅค–้ƒจๅทฅๅ…ทๆถˆ่ดน๏ผ‰
GET /docs Scalar API Reference ไบคไบ’ๅผๆ–‡ๆกฃ้กต้ข๏ผˆๆ–‡ๆกฃ้˜…่ฏป + Try it out๏ผ‰
# ่Žทๅ– OpenAPI JSON
curl http://localhost:3000/openapi.json

# ๆต่งˆๅ™จๆ‰“ๅผ€ไบคไบ’ๅผๆ–‡ๆกฃ
open http://localhost:3000/docs

Scalar ้…็ฝฎ

// src/config/default.js
export default {
  openapi: {
    enabled: true,
    title: "My API",
    version: "1.0.0",
    // Scalar API Reference ้…็ฝฎ
    scalar: {
      theme: "default", // ไธป้ข˜: default / alternate / moon / purple / solarized / ...
      darkMode: false, // ๆทฑ่‰ฒๆจกๅผ
      layout: "modern", // ๅธƒๅฑ€: modern / classic
      showSidebar: true, // ๆ˜พ็คบไพง่พนๆ 
    },
  },
};

่ทฏ็”ฑๆ–‡ๆกฃๅ…ƒไฟกๆฏ

่ทฏ็”ฑ็š„ docs ้€‰้กน็”จไบŽ่กฅๅ……ๆ–‡ๆกฃๅ…ƒไฟกๆฏ๏ผš

app.get(
  "/users/:id",
  {
    docs: {
      summary: "่Žทๅ–็”จๆˆท่ฏฆๆƒ…",
      description: "ๆ นๆฎ็”จๆˆท ID ่Žทๅ–ๅฎŒๆ•ด็š„็”จๆˆทไฟกๆฏ",
      tags: ["Users"],
    },
    validate: {
      params: { id: "string" },
    },
  },
  handler,
);

๐Ÿงช ๆต‹่ฏ•

VextJS ๆไพ›ๅ†…็ฝฎๆต‹่ฏ•ๅทฅๅ…ท๏ผŒๆ— ้œ€ๅฏๅŠจ HTTP ๆœๅŠกๅ™จๅณๅฏๆต‹่ฏ•่ทฏ็”ฑใ€‚

import { describe, it, expect } from "vitest";
import { createTestApp } from "vextjs/testing";

describe("User API", () => {
  it("should return user list", async () => {
    const app = await createTestApp({
      rootDir: "/path/to/project",
    });

    const res = await app.request.get("/users/list?page=1&limit=10");

    expect(res.status).toBe(200);
    expect(res.body.code).toBe(0);
    expect(res.body.data).toBeDefined();
  });
});

๐ŸŒ i18n ๅ›ฝ้™…ๅŒ–

ๅœจ src/locales/ ไธ‹ๆ”พ็ฝฎ่ฏญ่จ€ๆ–‡ไปถ๏ผŒๆก†ๆžถ่‡ชๅŠจๅŠ ่ฝฝๅนถๆณจๅ†Œๅˆฐ schema-dsl ๆ ก้ชŒๅ™จใ€‚

// src/locales/zh-CN.json
{
  "validation.required": "{field} ไธ่ƒฝไธบ็ฉบ",
  "validation.string.min": "{field} ้•ฟๅบฆไธ่ƒฝๅฐ‘ไบŽ {min} ไธชๅญ—็ฌฆ",
  "validation.email": "{field} ๆ ผๅผไธๆญฃ็กฎ"
}
// src/locales/en.json
{
  "validation.required": "{field} is required",
  "validation.string.min": "{field} must be at least {min} characters",
  "validation.email": "{field} is not a valid email"
}

๐Ÿ›๏ธ ๆžถๆž„ๆฆ‚่งˆ

็”จๆˆท่ฏทๆฑ‚
  โ”‚
  โ–ผ
โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚               VextJS Framework               โ”‚
โ”‚                                              โ”‚
โ”‚  โ”Œโ”€โ”€โ”€ ๅ†…็ฝฎไธญ้—ดไปถ้“พ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”  โ”‚
โ”‚  โ”‚ requestId โ†’ cors โ†’ bodyParser โ†’        โ”‚  โ”‚
โ”‚  โ”‚ rateLimit โ†’ responseWrapper โ†’ accessLogโ”‚  โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ”‚
โ”‚                    โ”‚                         โ”‚
โ”‚                    โ–ผ                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€ ่ทฏ็”ฑ็บงไธญ้—ดไปถ โ”€โ”€โ”                       โ”‚
โ”‚  โ”‚ auth โ†’ rbac โ†’ ... โ”‚                       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ”‚                    โ”‚                         โ”‚
โ”‚                    โ–ผ                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€ ๅ‚ๆ•ฐๆ ก้ชŒ โ”€โ”€โ”€โ”€โ”€โ”€โ”                       โ”‚
โ”‚  โ”‚ validate(schema)  โ”‚                       โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜                       โ”‚
โ”‚                    โ”‚                         โ”‚
โ”‚                    โ–ผ                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€ ่ทฏ็”ฑ Handler โ”€โ”€โ”  โ”Œโ”€โ”€ Services โ”€โ”€โ”    โ”‚
โ”‚  โ”‚  app.get/post/... โ”‚โ”€โ†’โ”‚ app.services โ”‚    โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜    โ”‚
โ”‚                    โ”‚                         โ”‚
โ”‚                    โ–ผ                         โ”‚
โ”‚  โ”Œโ”€โ”€โ”€ Adapter Layer โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”   โ”‚
โ”‚  โ”‚  Native (้ป˜่ฎค) / Fastify / Hono /     โ”‚   โ”‚
โ”‚  โ”‚  Express / Koa  โ€” ๅฏๆ›ฟๆข              โ”‚   โ”‚
โ”‚  โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
  โ”‚
  โ–ผ
HTTP ๅ“ๅบ” โ†’ { code: 0, data: {...} }

ๅฏๅŠจๆต็จ‹

  1. ้…็ฝฎๅŠ ่ฝฝ โ€” default โ†’ {env} โ†’ local ไธ‰ๅฑ‚ๅˆๅนถ + ๅ†ป็ป“
  2. ๅˆ›ๅปบ App โ€” ๅˆๅง‹ๅŒ– loggerใ€validatorใ€adapterใ€throw
  3. i18n ๅŠ ่ฝฝ โ€” ่‡ชๅŠจๆ‰ซๆ src/locales/
  4. ๆ’ไปถๅŠ ่ฝฝ โ€” ๆ‹“ๆ‰‘ๆŽ’ๅบ + ไพๆฌก setup()
  5. ไธญ้—ดไปถๅŠ ่ฝฝ โ€” ๆŒ‰ config.middlewares ็™ฝๅๅ•ๅŠ ่ฝฝ
  6. ๆœๅŠกๅŠ ่ฝฝ โ€” ๆ‰ซๆ src/services/๏ผŒๅฎžไพ‹ๅŒ–ๆณจๅ…ฅ app.services
  7. ่ทฏ็”ฑๅŠ ่ฝฝ โ€” ๆ‰ซๆ src/routes/๏ผŒๆณจๅ†Œๅˆฐ adapter
  8. ๅ†…็ฝฎไธญ้—ดไปถๆณจๅ†Œ โ€” requestId โ†’ cors โ†’ bodyParser โ†’ rateLimit โ†’ responseWrapper โ†’ accessLog โ†’ errorHandler
  9. HTTP ็›‘ๅฌ โ€” adapter.listen(port, host)
  10. ๅฐฑ็ปช้’ฉๅญ โ€” ๆ‰ง่กŒ onReady ๅ›ž่ฐƒ

๐Ÿ“‹ ็Žฏๅขƒๅ˜้‡

ๅ˜้‡ ่ฏดๆ˜Ž ้ป˜่ฎคๅ€ผ
NODE_ENV ่ฟ่กŒ็Žฏๅขƒ production๏ผˆstart๏ผ‰/ development๏ผˆdev๏ผ‰
VEXT_PORT ่ฆ†็›–็›‘ๅฌ็ซฏๅฃ โ€”
VEXT_HOST ่ฆ†็›–็›‘ๅฌๅœฐๅ€ โ€”
VEXT_DEV_POLL ๅผบๅˆถ่ฝฎ่ฏขๆจกๅผ๏ผˆ1 / 0๏ผ‰ ่‡ชๅŠจๆฃ€ๆต‹
VEXT_DEV_NO_HOT ็ฆ็”จ Soft Reload โ€”
VEXT_DEV_DEBOUNCE ้˜ฒๆŠ–้—ด้š”๏ผˆๆฏซ็ง’๏ผ‰ 0๏ผˆไธๅผ€ๅฏ๏ผ‰

๐Ÿ—บ๏ธ ่ทฏ็บฟๅ›พ

  • Adapter ๆžถๆž„๏ผˆNative ้ป˜่ฎค + Hono / Fastify / Express / Koa ๅฏ้€‰๏ผ‰
  • Native Adapter๏ผˆ้›ถๅค–้ƒจไพ่ต–๏ผŒhttp.createServer + find-my-way radix trie๏ผ‰
  • ็บฆๅฎšๅผ่ทฏ็”ฑ + ไธ‰ๆฎตๅผ่ฏญๆณ•
  • ๆ’ไปถ็ณป็ปŸ๏ผˆๆ‹“ๆ‰‘ๆŽ’ๅบ + ็”Ÿๅ‘ฝๅ‘จๆœŸ๏ผ‰
  • ๆœๅŠก่‡ชๅŠจๆณจๅ…ฅ
  • ๅ†…็ฝฎไธญ้—ดไปถ๏ผˆrequestId / CORS / bodyParser / rateLimit / accessLog๏ผ‰
  • ๅ‚ๆ•ฐๆ ก้ชŒ๏ผˆschema-dsl ้›†ๆˆ๏ผ‰
  • OpenAPI 3.1 ๆ–‡ๆกฃ็”Ÿๆˆ
  • CLI๏ผˆstart / dev / build / stop / reload / status๏ผ‰
  • ๅผ€ๅ‘ๆจกๅผ็ƒญ้‡่ฝฝ๏ผˆSoft Reload + Cold Restart๏ผ‰
  • ๆต‹่ฏ•ๅทฅๅ…ท๏ผˆcreateTestApp๏ผ‰
  • Cluster ๅคš่ฟ›็จ‹๏ผˆMaster/Worker + Rolling Restart๏ผ‰
  • ๆ€ง่ƒฝๅŸบๅ‡†ๆต‹่ฏ•๏ผˆautocannon ่‡ชๅŠจๅŒ– + ๅคš่ฝฎๅ–ไธญไฝๆ•ฐ๏ผ‰
  • AsyncLocalStorage ๅฏ้…็ฝฎ่ทณ่ฟ‡
  • Native Adapter ๆ€ง่ƒฝไผ˜ๅŒ–๏ผˆOverhead ้™่‡ณ 18%๏ผŒ้ข†ๅ…ˆ Fastify 2651%๏ผ‰
  • vext create ้กน็›ฎ่„šๆ‰‹ๆžถ
  • ๆ–‡ๆกฃ็ซ™๏ผˆrspress๏ผ‰
  • ่ทฏ็”ฑ็บงๅ“ๅบ”็ผ“ๅญ˜๏ผˆLRU ๅ†…ๅญ˜ๅญ˜ๅ‚จ๏ผŒๆ ‡็ญพๅคฑๆ•ˆ๏ผŒVary headers๏ผ‰
  • SSE ๆ”ฏๆŒ
  • WebSocket ๆ”ฏๆŒ

๐Ÿค ่ดก็Œฎ

ๆฌข่ฟŽๆไบค Issue ๅ’Œ Pull Requestใ€‚

# ๅ…‹้š†้กน็›ฎ
git clone https://github.com/vextjs/vext.git
cd vext

# ๅฎ‰่ฃ…ไพ่ต–
npm install

# ๅผ€ๅ‘๏ผˆTypeScript ็›‘ๅฌ็ผ–่ฏ‘๏ผ‰
npm run dev

# ่ฟ่กŒๆต‹่ฏ•
npm test

# ็ฑปๅž‹ๆฃ€ๆŸฅ
npm run typecheck

# ๆž„ๅปบ
npm run build

๐Ÿ“„ ่ฎธๅฏ่ฏ

MIT ยฉ 2025 vextjs

About

VextJS is a modern full-stack framework designed to enhance development efficiency. It offers out-of-the-box features and default configurations, allowing developers to quickly start projects, making it ideal for building high-performance RESTful APIs.

Topics

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors