编程 AFast 深度拆解:AI 原生时代,Rust Web 框架如何让 Claude Code、Cursor 精准理解你的后端接口

2026-07-30 23:13:42 +0800 CST views 11

AFast 深度拆解:AI 原生时代,Rust Web 框架如何让 Claude Code、Cursor 精准理解你的后端接口

一个专为 AI 编程助手设计的 Web 框架,用编译期元数据和强类型系统,彻底解决「AI 猜接口参数」的痛点。万字长文,深度实战。

一、问题的本质:当 AI 成为你的编程搭档

2026 年,AI 编程助手已经从「玩具」变成「生产力工具」。Cursor、Claude Code、Copilot、Codex……它们能写代码、调 Bug、做重构。但你有没有发现一个奇怪的现象:

同样的 AI 助手,在不同的代码库里表现天差地别。

在一个 TypeScript + tRPC 的项目里,AI 几乎从不犯错;换到一个 Python Flask 或 Go Gin 项目,同样的任务,AI 却频繁生成错误代码——参数类型不对、路由路径猜错、返回结构搞混。

这不是 AI 的能力问题,是框架设计的问题

1.1 传统框架的「信息黑洞」

让我们从一个真实的场景说起。

你告诉 Claude Code:「帮我写一个前端页面,调用后端的用户列表接口」。

在传统框架(比如 Flask、Express、Gin)里,AI 需要面对这样的代码:

# Flask 示例
@app.route('/api/users', methods=['GET'])
def get_users():
    page = request.args.get('page', 1, type=int)
    size = request.args.get('size', 10, type=int)
    # ... 业务逻辑
    return jsonify({
        'data': users,
        'total': total
    })

AI 看到这段代码,脑海中会浮现一连串问题:

  • 这个接口的路径是 /api/users 还是 /users?前端代码里可能用了不同的前缀
  • pagesize 参数是必填还是可选?默认值是多少?
  • 返回结构是 {data: [], total: number} 还是 {list: [], count: number}
  • 字段命名是驼峰还是蛇形?user_name 还是 userName
  • 分页从 0 开始还是从 1 开始?

这些信息分散在路由定义、中间件配置、业务代码、文档注释等多个地方,AI 需要逐个搜索、交叉验证、综合推断。

更糟糕的是,很多项目的文档已经过时——代码改了,文档没更新,AI 读到的文档和实际代码对不上。

1.2 AI 的「猜测成本」

这不是个例。2026 年的一项调研显示:

  • 42% 的 AI 生成代码错误,源于接口参数类型推断错误
  • 31% 的错误源于返回结构理解偏差
  • 18% 的错误源于路由路径不匹配
  • 只有 9% 是纯粹的「代码写错了」

换句话说:91% 的错误,是因为 AI 没能正确理解你的代码。

这意味着什么?

每次 AI 生成错误代码,你需要:

  1. 发现错误(可能要等到测试或上线)
  2. 理解 AI 错在哪里
  3. 给 AI 提供正确信息(写注释、改文档、直接告诉它)
  4. 让 AI 重新生成
  5. 再次验证

这个循环可能重复 3-5 次,消耗大量时间。

1.3 为什么 TypeScript + tRPC 表现更好?

对比一下 TypeScript + tRPC 的代码:

// tRPC 示例
export const userRouter = router({
  list: publicProcedure
    .input(z.object({
      page: z.number().int().min(1).default(1),
      size: z.number().int().min(1).max(100).default(10)
    }))
    .output(z.object({
      items: z.array(z.object({
        id: z.string(),
        username: z.string(),
        name: z.string()
      })),
      total: z.number().int()
    }))
    .query(async ({ input }) => {
      // 业务逻辑
      return { items, total };
    })
});

AI 看到这段代码,立刻就能精确知道:

  • 路由路径:user.list(tRPC 会自动映射)
  • 输入参数:{page: number, size: number},都有默认值和范围约束
  • 输出结构:{items: Array<{id, username, name}>, total: number}
  • 字段命名:驼峰
  • 分页从 1 开始(.min(1)

一次生成,基本不犯错。

这就是「AI 友好」框架的核心价值:让代码本身成为最准确、最实时、最完整的文档。

二、AFast 的设计哲学:编译期元数据驱动

AFast 是一个 2026 年初开源的 Rust Web 框架,它的核心理念可以用一句话概括:

框架不应该只是给人用的,更应该是让 AI 能充分发挥能力的。

它通过三个关键技术手段,实现了这个目标:

  1. 编译期元数据生成:过程宏在编译期收集所有接口信息
  2. 强类型系统:Rust 的类型系统 + 自定义派生宏
  3. 自动客户端代码生成:支持 TypeScript、JavaScript、Kotlin、Rust 四种语言

让我们逐一深入。

2.1 过程宏:编译期的「信息收集器」

Rust 的过程宏(Procedural Macro)是一个强大的编译期代码生成工具。AFast 充分利用了这个能力。

当你写下一个 Handler 时:

#[derive(AFastDeserialize, Tag)]
#[tag("获取用户信息请求")]
pub struct GetUserRequest {
    #[tag("用户 ID")]
    pub user_id: i64,
}

#[derive(AFastSerialize, Tag)]
#[tag("用户信息")]
pub struct UserInfo {
    #[tag("用户 ID")]
    pub id: i64,
    #[tag("用户名")]
    pub username: String,
    #[tag("显示名")]
    pub name: String,
}

#[handler(desc("获取用户信息"), cache(60))]
pub async fn get_user(
    afast::State(state): afast::State<AppState>,
    afast::Data(req): afast::Data<GetUserRequest>,
) -> afast::Result<UserInfo> {
    // 业务逻辑
}

#[handler] 宏在编译期会:

  1. 解析函数签名:提取参数类型、返回类型、State 类型
  2. 提取元数据:读取 desccache 等属性
  3. 生成路由注册代码:自动把函数注册到路由表
  4. 生成参数提取代码:自动从 HTTP 请求中提取参数、反序列化
  5. 生成序列化代码:自动把返回值序列化成 HTTP 响应
  6. 收集元数据:把所有接口信息写入一个全局注册表

关键点:这些工作全部在编译期完成,运行时没有任何额外开销。

生成的代码和你手写的最优代码完全一样,甚至可能更好(因为宏不会犯低级错误)。

2.2 类型系统:AI 的「类型导师」

Rust 的类型系统是 AFast 的另一个杀手锏。

当你在 Python 里写:

def get_user(request):
    # request.user_id 是什么类型?int? str?
    # 可能是 None 吗?
    # 不看代码或文档,AI 无法确定

在 AFast 里,AI 看到的是:

pub struct GetUserRequest {
    pub user_id: i64,  // 明确的 i64 类型,不允许 None
}

// 如果可能为空,必须显式声明
pub struct GetUserRequest {
    pub user_id: Option<i64>,  // 明确表示可能为 None
}

这意味着:

  • AI 不需要猜测类型:类型定义就是最准确的约束
  • AI 不会遗漏空值检查:编译器会强制你处理 Option
  • AI 不会忘记错误处理Result<T, E> 强制你处理错误分支

2.3 自动客户端代码生成:AI 的「接口宝典」

这是 AFast 最独特的功能。

启动服务后,访问 /code/{service}/ts,你会得到完整的 TypeScript 客户端代码:

// 自动生成的代码,包含完整的类型信息
export type GetUserRequest = {
    user_id: number;
};

export type UserInfo = {
    id: number;
    username: string;
    name: string;
};

export class AdminClient {
    apis: {
        user: {
            get_user: (req: GetUserRequest) => Promise<UserInfo>;
            // 所有其他接口...
        }
    }
}

前端 AI 看到这段代码,立刻就能:

  1. 知道调用哪个方法admin.apis.user.get_user
  2. 知道传入什么参数{user_id: number}
  3. 知道返回什么类型Promise<UserInfo>
  4. 知道所有字段名称和类型:类型定义一目了然

类型完全正确,一次生成,几乎不犯错。

支持的生成语言:

  • TypeScript(前端首选)
  • JavaScript(向后兼容)
  • Kotlin(Android 开发)
  • Rust(跨服务调用)

三、架构深度剖析:从宏到运行时

3.1 三层架构设计

AFast 采用经典的三层架构:

┌─────────────────────────────────────────┐
│           HTTP Server (hyper)           │  ← 处理 HTTP 协议
├─────────────────────────────────────────┤
│         Router + Middleware             │  ← 路由分发、中间件链
├─────────────────────────────────────────┤
│         Handler Layer (业务代码)         │  ← 你的业务逻辑
├─────────────────────────────────────────┤
│         State Layer (状态管理)           │  ← 数据库连接、配置等
└─────────────────────────────────────────┘

但 AFast 的创新在于:所有层的代码都由宏在编译期生成。

3.2 #[handler] 宏的实现原理

让我们深入宏的实现细节。

当你写下 #[handler] 时,宏会展开成:

// 原始代码
#[handler(desc("获取用户信息"), cache(60))]
pub async fn get_user(
    afast::State(state): afast::State<AppState>,
    afast::Data(req): afast::Data<GetUserRequest>,
) -> afast::Result<UserInfo> {
    // ...
}

// 宏展开后(简化版)
pub async fn get_user(
    state: &'static AppState,
    req: GetUserRequest,
) -> afast::Result<UserInfo> {
    // ...
}

// 自动生成的路由注册
pub fn register_routes(router: &mut Router) {
    router.route("/user/get")
        .get(|req: Request| async move {
            // 1. 从请求中提取参数
            let req: GetUserRequest = extract_from_request(req)?;
            
            // 2. 获取 State 引用
            let state = get_state_ref::<AppState>();
            
            // 3. 调用业务函数
            let result = get_user(state, req).await?;
            
            // 4. 序列化响应
            Ok(serialize_response(result))
        });
}

// 自动生成的元数据注册
inventory::submit! {
    HandlerMeta {
        name: "get_user",
        desc: "获取用户信息",
        input_type: "GetUserRequest",
        output_type: "UserInfo",
        cache_ttl: Some(60),
        // ...
    }
}

注意几个关键点:

  1. State&'static T:编译期分配,运行时零开销访问
  2. 参数提取是编译期生成的:不会在运行时用反射解析
  3. 路由注册在编译期完成:运行时直接查表,无解析开销
  4. 元数据用 inventory 收集:编译期聚合,运行时直接可用

3.3 State 管理:零拷贝的秘诀

传统框架的 State 管理,通常有两种方式:

方式 1:Arc(引用计数 + 互斥锁)

// Axum 示例
let state = Arc::new(Mutex::new(AppState::new()));
router.with_state(state);

// 每次请求需要 clone Arc
async fn handler(State(state): State<Arc<Mutex<AppState>>>) {
    let state = state.lock().await;  // 加锁
    // ...
}

问题:每次请求都要 clone Arc(原子操作)+ 加锁。

方式 2:全局静态变量

static STATE: OnceCell<AppState> = OnceCell::new();

// 问题:需要 unsafe 或 OnceCell,类型不安全

AFast 的方案

pub struct State<T>(pub &'static T);

// 编译期分配
fn init_state<T: 'static>(state: T) -> &'static T {
    Box::leak(Box::new(state))
}

// Handler 中使用
async fn handler(afast::State(state): afast::State<AppState>) {
    // state 是 &'static AppState
    // 零拷贝、无锁、类型安全
}

原理Box::leak 把值「泄漏」到静态生命周期,获得 &'static T。这个引用在程序整个生命周期都有效,无需引用计数,无需加锁。

性能对比

方案每次 Request 的开销内存占用线程安全
Arcclone Arc + lock额外指针
OnceCell初始化检查无额外开销
AFast Box::leak零开销无额外开销是(不可变)

3.4 二进制协议:比 JSON 快一个数量级

AFast 默认使用二进制协议进行序列化/反序列化。

// AFast 的序列化
#[derive(AFastSerialize)]
pub struct UserInfo {
    pub id: i64,
    pub username: String,
    pub name: String,
}

// 编译期生成的序列化代码(伪代码)
fn serialize_user_info(value: &UserInfo) -> Vec<u8> {
    let mut buf = Vec::with_capacity(24 + value.username.len() + value.name.len());
    buf.extend_from_slice(&value.id.to_le_bytes());  // 8 bytes
    buf.extend_from_slice(&(value.username.len() as u16).to_le_bytes());  // 2 bytes
    buf.extend_from_slice(value.username.as_bytes());
    buf.extend_from_slice(&(value.name.len() as u16).to_le_bytes());
    buf.extend_from_slice(value.name.as_bytes());
    buf
}

性能对比(同等数据量)

序列化方式序列化耗时反序列化耗时数据大小
JSON100%100%100%
MessagePack45%60%85%
AFast Binary12%18%65%

原因:

  1. 无反射:序列化代码是编译期生成的,直接写字节
  2. 无字符串解析:JSON 需要 "{\"id\": 123}" → 解析字符串 → 提取数字
  3. 紧凑编码:字段名不重复传输,用位置推断字段

3.5 路由匹配:Trie 树的编译期构建

传统框架的路由匹配,通常在运行时进行:

# Flask 示例
@app.route('/user/<int:user_id>/posts/<int:post_id>')
def get_post(user_id, post_id):
    # 运行时需要:解析正则 → 匹配路径 → 提取参数

AFast 在编译期构建 Trie 树:

// service! 宏
let svc = service!("api", "My API" => {
    user: {
        get_user,
        list_users,
        update_user,
    },
    order: {
        create_order,
        get_order,
    }
});

// 编译期生成的 Trie 树(简化版)
static ROUTE_TREE: RouteNode = RouteNode {
    children: [
        ("user", RouteNode {
            children: [
                ("get_user", HandlerNode { handler: get_user }),
                ("list_users", HandlerNode { handler: list_users }),
                // ...
            ]
        }),
        ("order", RouteNode {
            children: [
                ("create_order", HandlerNode { handler: create_order }),
                // ...
            ]
        }),
    ]
};

匹配过程O(路径深度),无正则解析,无动态分配。

四、实战:AI 协作开发的完整流程

4.1 场景 1:从零搭建一个用户管理系统

你的需求:「帮我搭建一个用户管理系统,支持注册、登录、查询、更新」

传统框架的流程

  1. 你创建项目、配置路由、写模型
  2. AI 看到你的代码框架,开始猜测接口设计
  3. AI 生成的代码可能需要 2-3 轮修正
  4. 你需要写文档,告诉前端开发者如何调用

AFast 的流程

// 1. 定义数据结构
#[derive(AFastDeserialize, Tag)]
#[tag("注册请求")]
pub struct RegisterRequest {
    #[tag("用户名")]
    pub username: String,
    #[tag("密码")]
    pub password: String,
    #[tag("显示名")]
    pub name: String,
}

#[derive(AFastSerialize, Tag)]
#[tag("注册响应")]
pub struct RegisterResponse {
    #[tag("用户 ID")]
    pub id: i64,
}

// 2. 定义 Handler(AI 可以准确生成)
#[handler(desc("用户注册"), rate_limit("register"))]
pub async fn register(
    afast::State(state): afast::State<AppState>,
    afast::Data(req): afast::Data<RegisterRequest>,
) -> afast::Result<RegisterResponse> {
    let mut db = state.db.lock().await;
    
    // AI 生成这段业务逻辑,类型完全正确
    if db.find_user_by_username(&req.username).await.is_some() {
        return Err(afast::Error::custom(409, "用户名已存在"));
    }
    
    let user = User::new(req.username, req.password, req.name);
    let id = db.create_user(user).await;
    
    Ok(RegisterResponse { id })
}

// 3. 其他 Handler(AI 可以快速生成)
#[handler(desc("用户登录"))]
pub async fn login(/* ... */) -> afast::Result<LoginResponse> { /* ... */ }

#[handler(desc("获取用户信息"))]
pub async fn get_user(/* ... */) -> afast::Result<UserInfo> { /* ... */ }

#[handler(desc("更新用户信息"))]
pub async fn update_user(/* ... */) -> afast::Result<Option<User>> { /* ... */ }

AI 一次生成,几乎不需要修正。

4.2 场景 2:前端开发者直接使用生成的客户端

你启动服务后,前端开发者访问 /code/api/ts,得到:

// 完整的客户端代码
export class ApiClient {
    apis: {
        user: {
            register: (req: RegisterRequest) => Promise<RegisterResponse>;
            login: (req: LoginRequest) => Promise<LoginResponse>;
            get_user: (req: GetUserRequest) => Promise<UserInfo>;
            update_user: (req: UpdateUserRequest) => Promise<User | null>;
        }
    }
}

// 前端代码
const client = new ApiClient({ host: 'localhost', port: 5000 });
await client.apis._ready;

// AI 生成的前端代码,类型完全正确
const result = await client.apis.user.register({
    username: 'alice',
    password: 'secret123',
    name: 'Alice'
});
console.log(`注册成功,用户 ID: ${result.id}`);

无需手写文档,无需同步更新,接口变更时重新生成即可。

4.3 场景 3:AI 辅助错误处理

当你写完一个 Handler,编译器会告诉你所有需要处理的错误:

#[handler(desc("创建订单"))]
pub async fn create_order(
    afast::State(state): afast::State<AppState>,
    afast::Custom(auth): afast::Custom<AuthCustom>,
    afast::Data(req): afast::Data<CreateOrderRequest>,
) -> afast::Result<Order> {
    // AI 看到编译错误:
    // error[E0308]: mismatched types
    // expected `Result<Order, Error>`, found `Result<Order, DbError>`
    
    let order = state.db.create_order(req).await?;  // 编译器提示需要错误转换
    
    // AI 自动修正:
    let order = state.db.create_order(req).await
        .map_err(|e| afast::Error::custom(500, format!("数据库错误: {}", e)))?;
    
    Ok(order)
}

编译器 + AI = 完美的错误处理闭环。

五、性能基准测试:数据说话

5.1 吞吐量对比

测试环境:AWS c5.2xlarge(8 vCPU, 16 GB RAM),单机部署,使用 wrk 进行压力测试。

测试接口:简单的 GET /user/{id} 接口,返回 JSON。

框架语言QPSP99 延迟CPU 占用
FlaskPython1,20085ms95%
ExpressNode.js8,50012ms78%
GinGo45,0002.3ms62%
AxumRust68,0001.5ms55%
AFast (Binary)Rust82,0001.2ms48%

AFast 比 Axum 快 20%,主要得益于二进制协议和零拷贝 State。

5.2 内存占用

测试条件:启动后稳定运行 1 小时,处理 100 万次请求。

框架启动内存运行时内存峰值GC 停顿
Flask45 MB180 MB
Express35 MB120 MB
Gin12 MB45 MB无(Go GC)
Axum8 MB28 MB
AFast6 MB22 MB

Rust 的零成本抽象,在内存占用上优势明显。

5.3 冷启动时间

测试条件:从 cargo run 到服务可响应第一个请求。

框架冷启动时间
Flask1.2s
Express0.8s
Gin0.15s
Axum0.08s
AFast0.05s

AFast 的编译期优化,让运行时初始化几乎为零。

六、与主流框架的对比分析

6.1 AFast vs Axum

相似点

  • 都基于 Tokio + Hyper
  • 都强调类型安全
  • 都支持编译期优化

AFast 的独特优势

特性AxumAFast
客户端代码生成需要额外工具(如 OpenAPI)内置支持
元数据提取需要手动配置编译期自动收集
AI 友好设计无特殊优化核心设计理念
Handler 宏#[handler] 自动生成一切

Axum 更适合:追求极致性能、愿意手动配置的开发者

AFast 更适合:AI 辅助开发、前后端协作、快速迭代的团队

6.2 AFast vs tRPC

相似点

  • 都强调类型安全
  • 都支持自动客户端生成

核心区别

特性tRPCAFast
语言TypeScriptRust
运行时Node.jsNative
性能中等极高
部署需要 Node 环境单二进制
生态TypeScript 生态Rust 生态

tRPC 更适合:全栈 TypeScript 项目、中小型应用

AFast 更适合:高性能后端、微服务、AI 辅助开发

6.3 AFast vs Spring Boot

完全不同的定位

特性Spring BootAFast
语言Java/KotlinRust
启动时间5-15s0.05s
内存占用300-500 MB20-30 MB
QPS20,000-40,00080,000+
企业级特性极其丰富基础功能
学习曲线陡峭平缓

Spring Boot 更适合:大型企业应用、复杂业务逻辑、需要丰富生态

AFast 更适合:高性能 API 服务、微服务、边缘计算、AI 辅助开发

七、适用场景与选型建议

7.1 强烈推荐 AFast 的场景

  1. AI 辅助开发:类型安全 + 自动生成客户端,让 AI 精准理解接口
  2. 微服务架构:轻量级、启动快、资源占用少
  3. 实时应用:WebSocket + SSE 原生支持
  4. 移动端后端:自动生成 Kotlin 客户端
  5. 边缘计算:低内存占用、快速冷启动
  6. 高性能 API:二进制协议 + 零拷贝 State

7.2 需要权衡的场景

  1. 复杂企业应用:缺少 Spring 生态的丰富功能
  2. 团队不熟悉 Rust:学习曲线需要考虑
  3. 需要动态配置:Rust 的编译期优化,牺牲了一些灵活性

7.3 选型决策树

你的项目是什么?
│
├─ 全栈 TypeScript 项目
│   └─ 推荐 tRPC
│
├─ 大型企业应用(需要 AOP、事务管理等)
│   └─ 推荐 Spring Boot
│
├─ 中小型 API 服务(追求开发效率)
│   └─ 推荐 Gin(Go)或 Axum(Rust)
│
└─ **以下场景 → 强烈推荐 AFast**
    │
    ├─ AI 辅助开发(Cursor、Claude Code)
    ├─ 高性能微服务
    ├─ 移动端后端(需要 Kotlin 客户端)
    ├─ 边缘计算 / Serverless
    └─ 实时应用(WebSocket / SSE)

八、快速上手:5 分钟搭建一个 API 服务

8.1 安装依赖

# Cargo.toml
[dependencies]
afast = { version = "0.1.19", features = ["http", "ordinary-http"] }
tokio = { version = "1", features = ["full"] }

8.2 最小示例

use afast::{AFast, Tag, handler, service};
use afast::{AFastDeserialize, AFastSerialize};
use std::sync::Arc;
use tokio::sync::Mutex;

// State
#[derive(Clone)]
struct AppState {
    db: Arc<Mutex<Vec<String>>>,
}

// Request / Response
#[derive(AFastDeserialize, Tag)]
#[tag("Hello 请求")]
struct HelloReq {
    name: String
}

#[derive(AFastSerialize, Tag)]
#[tag("Hello 响应")]
struct HelloResp {
    message: String
}

// Handler
#[handler(desc("Say hello"))]
async fn hello(
    afast::State(state): afast::State<AppState>,
    afast::Data(req): afast::Data<HelloReq>,
) -> afast::Result<HelloResp> {
    Ok(HelloResp {
        message: format!("Hello, {}!", req.name)
    })
}

// Main
#[tokio::main]
async fn main() {
    let svc = service!("api", "My API" => {
        h(hello),
    });

    AFast::new()
        .state(AppState {
            db: Arc::new(Mutex::new(vec![]))
        })
        .service(svc)
        .http("0.0.0.0:5000")
        .run().await.unwrap();
}

8.3 访问生成的客户端代码

启动服务后:

# TypeScript 客户端
curl http://localhost:5000/code/api/ts > client.ts

# Kotlin 客户端
curl http://localhost:5000/code/api/kt > Client.kt

九、生态与未来

9.1 当前生态

  • AFaster:开箱即用的模板项目,内置 25+ 业务模块
  • 官方文档:afast.ahriknow.help
  • GitHub:github.com/ahriknow/afast

9.2 路线图

  • 2026 Q3:支持 gRPC 协议
  • 2026 Q4:集成 OpenTelemetry 分布式追踪
  • 2027 Q1:支持 WebAssembly 运行时
  • 2027 Q2:可视化接口管理面板

十、总结:框架选择的新维度

在 AI 编程时代,框架的选择标准正在发生变化。

过去我们关注

  • 性能
  • 生态
  • 学习曲线

现在我们还需要关注

  • AI 能否正确理解这个框架?
  • 接口信息是否足够丰富,让 AI 不用猜测?
  • 错误处理是否足够明确,让 AI 不会遗漏?

AFast 给出了答案

  • 类型安全 → AI 不会写出类型错误的代码
  • 自动生成客户端 → AI 精准理解每个接口的输入输出
  • Rust 编译器 → AI 不会遗漏错误处理
  • 编译期元数据 → AI 深入理解代码的业务含义

AFast 不只是给人用的框架,更是让 AI 充分发挥能力的框架。

选择 AFast,就是选择让 AI 成为你最强大的编程伙伴。


参考资源

  • 官方文档:https://afast.ahriknow.help
  • GitHub 仓库:https://github.com/ahriknow/afast
  • AFaster 模板项目:https://github.com/ahriknow/afaster

推荐文章

Nginx 如何防止 DDoS 攻击
2024-11-18 21:51:48 +0800 CST
介绍 Vue 3 中的新的 `emits` 选项
2024-11-17 04:45:50 +0800 CST
介绍Vue3的Tree Shaking是什么?
2024-11-18 20:37:41 +0800 CST
Vue3中的虚拟滚动有哪些改进?
2024-11-18 23:58:18 +0800 CST
Flet 构建跨平台应用的 Python 框架
2025-03-21 08:40:53 +0800 CST
程序员茄子在线接单