《Go 后端开发者学 React:组件通信与插槽》

React 组件通信与插槽完全指南:从 Props、Children 到组件设计实战

React + TypeScript 学习笔记 · 组件通信 · 组件复用 · 由浅入深

前言

学习 React 时,经常会遇到几个问题:

  • 父组件如何给子组件传递数据?
  • 子组件如何通知父组件修改数据?
  • 兄弟组件之间如何共享数据?
  • React 有没有类似 Vue 的 slot 插槽?
  • props、children、Context 分别什么时候使用?

其实,这些问题都属于 React 的两个核心知识点:

组件通信:解决组件之间如何传递数据和事件。

组件插槽:解决组件内部的内容如何由外部决定。

本文使用 React 函数组件和 TypeScript,从最简单的例子逐步深入。

一、先理解 React 组件是什么

在 React 中,组件可以简单理解为:一个返回 JSX 的函数。

例如:

javascript 复制代码
function UserCard() {
  return (
    <div>
      <h3>张三</h3>
      <p>年龄:25</p>
    </div>
  );
}

然后在另一个组件中使用:

javascript 复制代码
function App() {
  return (
    <div>
      <UserCard />
      <UserCard />
    </div>
  );
}

这样就相当于复用了两次 UserCard 组件。

但有个问题:两个组件都显示张三。

如果希望不同组件显示不同用户,就需要组件通信。

二、父传子:Props(最基础)

Props 是 React 中最常用的组件通信方式。

一句话理解:

父组件通过属性给子组件传递数据,子组件通过 props 接收。

2.1 最简单的例子

子组件:

typescript 复制代码
// UserCard.tsx

interface UserCardProps {
  name: string;
  age: number;
}

function UserCard(props: UserCardProps) {
  return (
    <div>
      <h3>{props.name}</h3>
      <p>年龄:{props.age}</p>
    </div>
  );
}

export default UserCard;

父组件:

javascript 复制代码
// App.tsx

import UserCard from "./UserCard";

function App() {
  return (
    <div>
      <UserCard name="张三" age={25} />
      <UserCard name="李四" age={30} />
    </div>
  );
}

效果:

核心关系:

需要注意:

  • name 和 age 是父组件传入的属性。
  • UserCardProps 定义属性的 TypeScript 类型。
  • 子组件只能读取 props,不应该直接修改 props。

2.2 Props 解构写法

上面的写法:

javascript 复制代码
function UserCard(props: UserCardProps) {
  return <h3>{props.name}</h3>;
}

实际开发中更常使用解构:

javascript 复制代码
function UserCard({ name, age }: UserCardProps) {
  return (
    <div>
      <h3>{name}</h3>
      <p>年龄:{age}</p>
    </div>
  );
}

这里的:

复制代码
{ name, age }: UserCardProps

是 JavaScript 的对象解构,加上 TypeScript 类型声明。

2.3 Props 可以传递什么?

不仅能传递字符串、数字,还可以传递对象、数组、函数。

typescript 复制代码
interface User {
  id: number;
  name: string;
}

interface UserCardProps {
  user: User;
  tags: string[];
  active: boolean;
}

function UserCard({ user, tags, active }: UserCardProps) {
  return (
    <div>
      <h3>{user.name}</h3>

      <p>{active ? "在线" : "离线"}</p>

      {tags.map(tag => (
        <span key={tag}>{tag}</span>
      ))}
    </div>
  );
}

父组件:

ini 复制代码
<UserCard
  user={{ id: 1, name: "张三" }}
  tags={["管理员", "开发者"]}
  active={true}
/>

小结:Props 负责把数据从父组件传给子组件,是 React 最基础的通信方式。

三、子传父:回调函数(重点)

现在假设有一个需求:

父组件维护任务列表,子组件展示任务。

用户点击子组件中的"删除"按钮,希望父组件删除对应任务。

问题是:子组件怎么通知父组件?

React 通常使用回调函数实现。

核心思想:

父组件把函数传给子组件,子组件调用这个函数,把信息传回父组件。

3.1 基础例子

子组件:

typescript 复制代码
interface ChildProps {
  onMessage: (message: string) => void;
}

function Child({ onMessage }: ChildProps) {
  return (
    <button onClick={() => onMessage("你好,父组件!")}>
      发送消息
    </button>
  );
}

父组件:

javascript 复制代码
import { useState } from "react";

function Parent() {
  const [message, setMessage] = useState("");

  const handleMessage = (msg: string) => {
    setMessage(msg);
  };

  return (
    <div>
      <h3>收到消息:{message}</h3>

      <Child onMessage={handleMessage} />
    </div>
  );
}

交互演示

父组件 Parent

收到消息:暂无消息

子组件 Child

点击向父组件发送消息

整个流程:

scss 复制代码
父组件 Parent
   │
   │ 传递 handleMessage 函数
   ▼
子组件 Child
   │
   │ 用户点击按钮
   │
   │ onMessage("你好")
   ▼
父组件 handleMessage()
   │
   │ setMessage()
   ▼
父组件重新渲染

这里需要理解:

typescript 复制代码
onMessage: (message: string) => void;

表示 onMessage 是一个函数类型,接收 string 参数,返回 void。

3.2 实战:子组件删除父组件中的任务

这是一个很典型的业务场景。

typescript 复制代码
import { useState } from "react";

interface Task {
  id: number;
  name: string;
}

interface TaskItemProps {
  task: Task;
  onDelete: (id: number) => void;
}

// 子组件
function TaskItem({ task, onDelete }: TaskItemProps) {
  return (
    <div>
      <span>{task.name}</span>

      <button onClick={() => onDelete(task.id)}>
        删除
      </button>
    </div>
  );
}

// 父组件
function TaskList() {
  const [tasks, setTasks] = useState<Task[]>([
    { id: 1, name: "视频回查" },
    { id: 2, name: "文章回扫" }
  ]);

  const handleDelete = (id: number) => {
    setTasks(prev => prev.filter(task => task.id !== id));
  };

  return (
    <div>
      {tasks.map(task => (
        <TaskItem
          key={task.id}
          task={task}
          onDelete={handleDelete}
        />
      ))}
    </div>
  );
}

这里同时存在两种通信:

  • task={task}:父组件向子组件传数据。
  • onDelete={handleDelete}:父组件向子组件传回调,子组件通过调用它通知父组件。

子传父本质上并不是子组件直接修改父组件的数据,而是子组件调用父组件提供的函数。

四、兄弟组件通信:状态提升

假设有两个组件:

markdown 复制代码
          Parent
         /      \
     Search     UserList
  • Search:输入搜索关键词。
  • UserList:根据关键词展示用户。

它们是兄弟组件,应该怎么通信?

最常见的方案是:把共享状态放在共同的父组件中。

这叫做 Lifting State Up(状态提升)。

4.1 完整示例

typescript 复制代码
import { useState } from "react";

interface SearchProps {
  keyword: string;
  onKeywordChange: (value: string) => void;
}

function Search({ keyword, onKeywordChange }: SearchProps) {
  return (
    <input
      value={keyword}
      onChange={e => onKeywordChange(e.target.value)}
      placeholder="搜索用户"
    />
  );
}

interface UserListProps {
  keyword: string;
}

function UserList({ keyword }: UserListProps) {
  const users = ["张三", "李四", "王五"];

  return (
    <ul>
      {users
        .filter(name => name.includes(keyword))
        .map(name => (
          <li key={name}>{name}</li>
        ))}
    </ul>
  );
}

function Parent() {
  const [keyword, setKeyword] = useState("");

  return (
    <div>
      <Search
        keyword={keyword}
        onKeywordChange={setKeyword}
      />

      <UserList keyword={keyword} />
    </div>
  );
}

交互演示

核心思路:

markdown 复制代码
Search 输入关键词
        │
        ▼
Parent 更新 keyword
        │
        ▼
UserList 接收新 keyword
        │
        ▼
列表重新渲染

记住:兄弟组件通常不直接互相通信,而是通过共同父组件共享状态。

五、React 插槽:children(重点)

前面讲的都是组件之间传递数据。

现在换一个需求。

假设你要开发一个公共弹窗组件 Modal,但是每个弹窗展示的内容都不一样。

例如:

  • 删除弹窗:展示"确定删除吗?"
  • 用户弹窗:展示用户信息。
  • 任务弹窗:展示任务执行详情。

难道每个弹窗都要重新写一遍吗?

不需要。

这就引出了**插槽(Slot)**的概念。

5.1 什么是插槽?

一句话理解:

插槽就是组件预留一个位置,允许外部组件把内容放进来。

React 没有完全对应 Vue slot 的内置语法,最常见的实现方式是 props.children。

先看一个例子:

typescript 复制代码
import type { ReactNode } from "react";

interface CardProps {
  children: ReactNode;
}

function Card({ children }: CardProps) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

使用:

javascript 复制代码
function App() {
  return (
    <Card>
      <h2>用户信息</h2>
      <p>姓名:张三</p>
      <p>年龄:25</p>
    </Card>
  );
}

渲染效果:

用户信息

这里:

xml 复制代码
<Card>
  <h2>用户信息</h2>
  <p>姓名:张三</p>
</Card>

<Card> 标签中间的内容,会作为 children 传递给 Card 组件。

相当于:

xml 复制代码
<Card
  children={
    <>
      <h2>用户信息</h2>
      <p>姓名:张三</p>
    </>
  }
/>

其中 <>...</> 是 React Fragment,用来组合多个元素,不额外创建 DOM 节点。

插槽原理

Card 组件

children 插入的位置

<UserInfo />

你可以把 children 理解成:

父组件把一块 JSX 内容交给子组件,子组件决定这块内容放在哪里。

5.2 children 可以传入什么?

children 不局限于 HTML 标签,也可以是字符串、数字、组件或多个 JSX 元素。

例如:

xml 复制代码
<Card>你好</Card>
xml 复制代码
<Card>
  <UserInfo />
</Card>
xml 复制代码
<Card>
  <h2>标题</h2>
  <TaskList />
  <button>提交</button>
</Card>

在 TypeScript 中,通常使用:

typescript 复制代码
import type { ReactNode } from "react";

interface CardProps {
  children: ReactNode;
}

ReactNode 是能够作为 React 渲染内容的类型。

5.3 React 插槽和 Vue 插槽对比

如果以后看到 Vue 的代码,可以这样理解:

Vue

xml 复制代码
<!-- Card.vue -->
<template>
  <div>
    <slot />
  </div>
</template>

使用:

xml 复制代码
<Card>
  <h2>用户信息</h2>
</Card>

React

javascript 复制代码
function Card({ children }) {
  return (
    <div>
      {children}
    </div>
  );
}

使用:

xml 复制代码
<Card>
  <h2>用户信息</h2>
</Card>

两者思想相似:

Vue 使用 <slot />,React 通常使用 {children}。

六、具名插槽:多个位置如何传入不同内容?

children 适合一个主要内容区域。

但有时候,一个组件需要多个插槽。

例如一个页面布局:

如果只有 children,怎么区分 Header、Sidebar 和 Footer?

React 常见的解决方式是:使用普通 props 接收不同位置的 ReactNode。

6.1 自定义多个插槽

css 复制代码
import type { ReactNode } from "react";

interface LayoutProps {
  header: ReactNode;
  sidebar: ReactNode;
  children: ReactNode;
  footer?: ReactNode;
}

function Layout({
  header,
  sidebar,
  children,
  footer
}: LayoutProps) {
  return (
    <div>
      <header>{header}</header>

      <div className="layout-body">
        <aside>{sidebar}</aside>
        <main>{children}</main>
      </div>

      <footer>{footer}</footer>
    </div>
  );
}

使用:

javascript 复制代码
function App() {
  return (
    <Layout
      header={<h1>任务管理平台</h1>}
      sidebar={<TaskMenu />}
      footer={<p>Copyright 2026</p>}
    >
      <TaskList />
    </Layout>
  );
}

这里的:

css 复制代码
header={<h1>任务管理平台</h1>}

就是把一段 JSX 作为属性传递给组件。

同理:

ini 复制代码
sidebar={<TaskMenu />}

表示把 TaskMenu 组件的 React 元素传给 sidebar。

6.2 什么时候使用 children,什么时候使用具名插槽?

场景 推荐方式
卡片主要内容 children
弹窗主体 children
顶部标题 title 或 header
底部操作按钮 footer
侧边栏 sidebar
多个可定制区域 多个 ReactNode props

核心:React 的具名插槽通常就是把 JSX 当作 props 传递。

七、进阶插槽:Render Props

你前面学习过 renderItem,其实它就是 Render Props 思想的典型应用。

这个概念的难点就一句话:普通插槽传的是 "现成的内容",Render Props 传的是 "一个函数"------ 数据还在子组件手里,但怎么画由你(父组件)决定。

普通插槽:

xml 复制代码
<Card>
  <h2>固定内容</h2>
</Card>

这里传入的是现成的 JSX 内容。

但如果子组件希望把内部的数据传给外部,让外部决定怎么渲染呢?

这时可以使用 Render Props。

7.1 最简单的 Render Props

typescript 复制代码
import type { ReactNode } from "react";

interface User {
  id: number;
  name: string;
}

interface UserProviderProps {
  render: (user: User) => ReactNode;
}

function UserProvider({ render }: UserProviderProps) {
  const user: User = {
    id: 1,
    name: "张三"
  };

  return <div>{render(user)}</div>;
}

父组件:

javascript 复制代码
function App() {
  return (
    <UserProvider
      render={(user) => (
        <h2>用户名:{user.name}</h2>
      )}
    />
  );
}

关键是:

javascript 复制代码
render={(user) => (
  <h2>{user.name}</h2>
)}

这次传入的不是 JSX,而是一个返回 JSX 的函数。

子组件内部执行:

scss 复制代码
render(user)

把自己的数据传给父组件提供的渲染函数。

7.2 Render Props 和普通 children 的区别

对比 children Render Props
传入内容 JSX 元素 函数
能否接收子组件提供的数据 不能直接通过该元素接收 可以
典型写法 <Card>内容</Card> render={(item) => ...}
适用场景 布局、容器 列表渲染、数据驱动的内容定制

7.3 renderItem 实战

例如自己封装一个通用列表:

typescript 复制代码
import type { ReactNode } from "react";

interface ListProps<T extends { id: number }> {
  data: T[];
  renderItem: (item: T, index: number) => ReactNode;
}

function MyList<T extends { id: number }>({
  data,
  renderItem
}: ListProps<T>) {
  return (
    <div>
      {data.map((item, index) => (
        <div key={item.id}>
          {renderItem(item, index)}
        </div>
      ))}
    </div>
  );
}

调用:

ini 复制代码
const tasks = [
  { id: 1, name: "视频回查" },
  { id: 2, name: "文章回扫" }
];

<MyList
  data={tasks}
  renderItem={(item, index) => (
    <div>
      {index + 1}. {item.name}
    </div>
  )}
/>

可以理解为:

scss 复制代码
data.map((item, index) => renderItem(item, index))

这就是 Render Props 最核心的思想:组件提供数据,外部提供渲染方式。

八、跨层级组件通信:Context

前面介绍的 Props 很适合父子组件之间通信。

但是假设组件结构是这样的:

css 复制代码
App
 └── Page
      └── Layout
           └── Header
                └── UserAvatar

现在 App 中保存了用户信息,而 UserAvatar 需要读取它。

使用 Props 就需要:

css 复制代码
App → Page → Layout → Header → UserAvatar

每个中间组件都要传一遍 user。

这种情况叫做 Props Drilling(属性逐层透传)。

React 提供了 Context 来解决这类问题。

8.1 Context 是什么?

一句话理解:

Context 允许组件共享数据,让深层子组件不必经过每一层 Props 传递,就能读取上层提供的值。

例如:

css 复制代码
             App
              │
              │ 提供 UserContext
              ▼
            Page
              │
            Layout
              │
            Header
              │
              ▼
          UserAvatar
          直接读取 Context

8.2 最简单的 Context 示例

javascript 复制代码
import { createContext, useContext } from "react";

// 1. 创建 Context
const UserContext = createContext("游客");

// 2. 深层子组件
function UserAvatar() {
  const username = useContext(UserContext);

  return <div>当前用户:{username}</div>;
}

// 3. 中间组件
function Header() {
  return <UserAvatar />;
}

// 4. 父组件
function App() {
  return (
    <UserContext.Provider value="张三">
      <Header />
    </UserContext.Provider>
  );
}

最终显示:

复制代码
当前用户:张三

注意:

ini 复制代码
const username = useContext(UserContext);

即使没有通过 Props 一层层传递,UserAvatar 仍然能读取到上层 Provider 提供的值。

这里使用兼容 React 18 的 Provider 写法,React 19 也支持直接使用 <UserContext value="张三">。

8.3 Context 能结合 useState 使用吗?

当然可以,而且非常常见。

例如实现全局主题切换:

javascript 复制代码
import {
  createContext,
  useContext,
  useState,
  type ReactNode
} from "react";

interface ThemeContextType {
  theme: "light" | "dark";
  toggleTheme: () => void;
}

const ThemeContext = createContext<ThemeContextType | null>(null);

function ThemeProvider({ children }: { children: ReactNode }) {
  const [theme, setTheme] = useState<"light" | "dark">("light");

  const toggleTheme = () => {
    setTheme(prev => prev === "light" ? "dark" : "light");
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
}

function ThemeButton() {
  const context = useContext(ThemeContext);

  if (!context) {
    throw new Error("ThemeButton 必须在 ThemeProvider 内使用");
  }

  return (
    <button onClick={context.toggleTheme}>
      当前主题:{context.theme}
    </button>
  );
}

function App() {
  return (
    <ThemeProvider>
      <ThemeButton />
    </ThemeProvider>
  );
}

交互演示

主题设置

Context 的主要优势:

  • 减少多层 Props 透传。
  • 适合主题、当前用户、语言等跨层共享数据。
  • 配合 useState 或 useReducer 可以共享状态及更新方法。

但要注意:Context 不代表所有状态都应该放在全局。 如果数据只被父子两个组件使用,直接用 Props 往往更简单。

九、进阶:受控组件与双向交互

现在来看一个非常常见的组件设计模式。

假设你封装一个搜索框,希望:

  1. 父组件控制搜索框中的文本。
  2. 子组件展示该文本。
  3. 用户输入时通知父组件。
  4. 父组件更新状态,再传回子组件。

这叫做受控组件模式。

typescript 复制代码
import { useState } from "react";

interface SearchInputProps {
  value: string;
  onChange: (value: string) => void;
}

// 子组件
function SearchInput({ value, onChange }: SearchInputProps) {
  return (
    <input
      value={value}
      onChange={e => onChange(e.target.value)}
    />
  );
}

// 父组件
function App() {
  const [keyword, setKeyword] = useState("");

  return (
    <div>
      <SearchInput
        value={keyword}
        onChange={setKeyword}
      />

      <p>搜索关键词:{keyword}</p>
    </div>
  );
}

执行流程:

scss 复制代码
           父组件 App
                │
         value={keyword}
                │
                ▼
           SearchInput
                │
           用户输入内容
                │
       onChange(newValue)
                │
                ▼
          setKeyword()
                │
         父组件重新渲染
                │
                ▼
       新 value 传回子组件

看起来像"双向绑定",但 React 的数据流本质仍然是单向的:

  • 数据通过 Props 向下传递。
  • 事件通过回调向上传递。

这种 value + onChange 模式在 React 组件库中非常普遍。

十、组件通信常见错误

10.1 子组件直接修改 Props

错误:

javascript 复制代码
function Child({ user }: { user: User }) {
  user.name = "李四"; // ❌ 不应该修改 Props 对象
  return <div>{user.name}</div>;
}

正确方式是让父组件更新状态,再将新数据传给子组件。

typescript 复制代码
interface ChildProps {
  user: User;
  onChangeName: (name: string) => void;
}

function Child({ user, onChangeName }: ChildProps) {
  return (
    <button onClick={() => onChangeName("李四")}>
      修改 {user.name}
    </button>
  );
}

10.2 错把函数调用结果传给 onClick

错误:

xml 复制代码
<button onClick={handleDelete(1)}>
  删除
</button>

这样会在渲染时执行函数,而不是点击时执行(假设 handleDelete 不是返回事件处理函数的高阶函数)。

正确:

scss 复制代码
<button onClick={() => handleDelete(1)}>
  删除
</button>

10.3 混淆 children 和 renderItem

javascript 复制代码
// 普通插槽:直接传 JSX
<Card>
  <UserInfo />
</Card>

// Render Props:传函数
<MyList
  data={users}
  renderItem={(user) => <UserCard user={user} />}
/>

它们的核心区别就是:children 通常传现成的内容,renderItem 传生成内容的函数。

10.4 在多个兄弟组件中重复维护同一份状态

如果两个组件需要始终展示同一个状态,通常不要让它们各自维护独立副本。

更推荐把状态提升到共同父组件,确保有一个明确的数据来源。

十一、组件通信方式如何选择?

这是实际开发时最需要掌握的一张表。

场景 推荐方案
父组件向子组件传数据 Props
子组件通知父组件 回调函数
兄弟组件共享状态 状态提升
跨多层组件共享数据 Context
组件传入自定义内容 children
一个组件有多个内容区域 具名 ReactNode Props
子组件提供数据,外部决定如何渲染 Render Props
复杂跨页面全局状态 Context 或专门的状态管理库

如果项目更加复杂,还可能接触:

  • Zustand:轻量全局状态管理。
  • Redux Toolkit:适合需要集中管理复杂状态的项目。
  • TanStack Query:侧重服务端数据获取、缓存与同步。
  • 自定义 Hook:复用状态逻辑,而不一定共享同一份状态。

它们并不是 Props 和 Context 的替代品,而是针对不同问题的工具。

十二、综合实战:设计一个可复用的任务卡片

最后将 Props、回调函数、children 和 TypeScript 组合起来。

目标:封装一个任务卡片,支持自定义内容和删除操作。

typescript 复制代码
import type { ReactNode } from "react";

interface Task {
  id: number;
  name: string;
  status: string;
}

interface TaskCardProps {
  task: Task;
  children?: ReactNode;
  footer?: ReactNode;
  onDelete: (id: number) => void;
}

function TaskCard({
  task,
  children,
  footer,
  onDelete
}: TaskCardProps) {
  return (
    <div className="task-card">
      <h3>{task.name}</h3>

      <p>状态:{task.status}</p>

      <div className="task-content">
        {children}
      </div>

      <div className="task-footer">
        {footer}

        <button onClick={() => onDelete(task.id)}>
          删除
        </button>
      </div>
    </div>
  );
}

父组件使用:

javascript 复制代码
import { useState } from "react";

function App() {
  const [tasks, setTasks] = useState<Task[]>([
    { id: 1, name: "视频回查", status: "running" },
    { id: 2, name: "文章回扫", status: "pending" }
  ]);

  const handleDelete = (id: number) => {
    setTasks(prev => prev.filter(task => task.id !== id));
  };

  return (
    <div>
      {tasks.map(task => (
        <TaskCard
          key={task.id}
          task={task}
          onDelete={handleDelete}
          footer={<button>查看详情</button>}
        >
          <p>任务 ID:{task.id}</p>
        </TaskCard>
      ))}
    </div>
  );
}

在这个例子中:

  • task={task}:父传子。
  • onDelete={handleDelete}:子组件通过回调通知父组件。
  • children:内容插槽。
  • footer:具名插槽。
  • useState:父组件管理数据。
  • map:动态生成任务卡片。

一个可复用的 React 组件,基本就是这样设计出来的。

十三、总结:掌握这五个概念就够了

React 最核心的设计思想有两个:

第一,数据单向流动。 父组件通过 Props 向子组件传递数据,子组件通过回调函数通知父组件。

第二,组合优于继承。 通过 children、具名插槽和 Render Props,把组件组合成更复杂的页面,而不是依靠复杂的继承关系。

建议学习顺序:Props → 回调函数 → 状态提升 → children → Render Props → Context。

参考官方文档:React Passing Props、React Sharing State、React useContext。

相关推荐
Penge6662 小时前
《Go 后端开发者学 React:useState》
javascript
Penge6662 小时前
《Go 后端开发者学 React:数据渲染》
javascript
ikoala5 小时前
AI 写页面像说明书,修 Bug 像贴膏药?把这两句话写进 AGENTS.md
前端·javascript·ai编程
Penge6665 小时前
《Go 后端开发者学 TypeScript:ES Module了解》
javascript
Penge6665 小时前
《Go 后端开发者学 TypeScript:从零搭建与运行》
javascript
Penge6666 小时前
《Go 后端开发者学 TypeScript:keyof关键字》
javascript
Penge6667 小时前
《Go 后端开发者学 TypeScript:extends关键字》
javascript
张张123y7 小时前
DSH 插件调试避坑:client.js 设置面板、热重载与高频报错排查表
开发语言·javascript·人工智能·开源·ecmascript·后端开发·ai agent
Penge66612 小时前
《Go 后端开发者学 TypeScript:typescript 类型断言》
javascript