《Go 后端开发者学 React:Hook》

React Hooks 从入门到实战:一篇搞懂 React 常用 Hook

适合人群:有 HTML、CSS、JavaScript 基础,刚开始学习 React 的后端开发者。

学习目标:理解 Hook 的作用,掌握常用 Hook,能够看懂并编写 React 业务组件。

一、什么是 React Hook?

1.1 先理解 React 组件

在 React 中,页面通常由一个个组件组成。

比如下面是一个最简单的 React 组件:

javascript 复制代码
function App() {
  return <h1>Hello React</h1>;
}

export default App;

这里需要理解三个知识点:

  • App:一个普通的 JavaScript 函数
  • return:返回需要渲染的页面内容
  • JSX:允许在 JavaScript 中编写类似 HTML 的语法

我们可以简单理解为:

React 组件 = 一个返回页面内容的函数。

但是,这种普通函数组件存在一个问题:怎么管理会变化的数据?

1.2 普通变量为什么不够用?

假设我们需要实现一个计数器。

javascript 复制代码
function Counter() {
  let count = 0;

  function add() {
    count++;
    console.log(count);
  }

  return (
    <div>
      <p>当前数字:{count}</p>
      <button onClick={add}>+1</button>
    </div>
  );
}

点击按钮后:

  • count 确实增加了。
  • console.log 可以看到变化。
  • 但是页面上的数字不会因此自动更新。

原因是:

React 不会因为普通局部变量发生变化,就重新渲染组件。

而且组件重新渲染时,函数会重新执行,普通局部变量也会重新初始化。

所以 React 提供了一种管理状态的机制:Hook。

1.3 Hook 到底是什么?

Hook 可以理解为:

React 提供的一组特殊函数,让函数组件拥有状态管理、副作用处理、引用保存等能力。

例如:

scss 复制代码
const [count, setCount] = useState(0);

这里的 useState 就是一个 Hook。

Hook 的名字通常以 use 开头。

常见 Hook:

Hook 主要作用 通俗理解
useState 管理组件状态 保存会变化的数据
useEffect 处理副作用 数据变化后执行一些操作
useRef 保存可变引用 保存值或操作 DOM
useMemo 缓存计算结果 避免重复做昂贵计算
useCallback 缓存函数引用 避免每次都创建新函数
useContext 跨组件共享数据 减少逐层传递 Props
useReducer 管理复杂状态 集中处理状态变化

刚入门时,优先掌握 useState 和 useEffect。


二、useState:管理组件状态(最重要)

2.1 基本语法

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

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>当前数字:{count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
    </div>
  );
}

拆解:

scss 复制代码
const [count, setCount] = useState(0);

相当于:

  • count:当前状态值
  • setCount:修改状态的函数
  • 0:初始值

useState 返回一个数组,包含状态值和更新函数。

这里用到了 JavaScript 的数组解构语法。

例如:

ini 复制代码
const arr = [10, 20];

const [a, b] = arr;

console.log(a); // 10
console.log(b); // 20

所以:

scss 复制代码
const [count, setCount] = useState(0);

可以理解为:

React,帮我保存一个叫 count 的状态,初始值为 0,并提供一个修改它的方法 setCount。

当执行:

scss 复制代码
setCount(10);

React 会安排组件重新渲染,让页面显示最新状态。

2.2 useState 的常见用法

用法一:数字状态

scss 复制代码
const [count, setCount] = useState(0);

setCount(10);
setCount(prev => prev + 1);

其中:

ini 复制代码
prev => prev + 1

表示根据上一次状态计算新状态。

当新状态依赖旧状态时,推荐这种写法。

用法二:字符串状态

ini 复制代码
const [name, setName] = useState('');

return (
  <input
    value={name}
    onChange={(e) => setName(e.target.value)}
  />
);

用户输入时,name 跟着发生变化。

这里:

  • value:输入框当前显示的值
  • onChange:输入内容变化时触发
  • e.target.value:获取用户输入的内容

用法三:布尔状态

经常用于控制弹窗、加载状态、显示与隐藏。

javascript 复制代码
const [visible, setVisible] = useState(false);

return (
  <div>
    <button onClick={() => setVisible(!visible)}>
      切换显示
    </button>

    {visible && <p>这是一段内容</p>}
  </div>
);

这里的:

css 复制代码
visible && <p>这是一段内容</p>

表示:

如果 visible 为 true,就渲染内容。

用法四:数组状态

例如一个任务列表:

ini 复制代码
const [tasks, setTasks] = useState<string[]>([]);

function addTask() {
  setTasks(prev => [...prev, '新任务']);
}

这里:

css 复制代码
[...prev, '新任务']

表示创建一个新数组,把旧数组元素复制过来,再添加一个新元素。

为什么不直接使用 push?

arduino 复制代码
tasks.push('新任务'); // 不推荐

因为 React 状态应该按不可变数据的方式更新。直接修改原数组不会主动通知 React 更新页面。

用法五:对象状态

php 复制代码
const [user, setUser] = useState({
  name: '张三',
  age: 20
});

function updateAge() {
  setUser(prev => ({
    ...prev,
    age: 21
  }));
}

这里的 ...prev 表示保留其他字段,只修改 age。

2.3 TypeScript 中的 useState

实际工作中,经常会看到:

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

const [user, setUser] = useState<User | null>(null);

拆解:

sql 复制代码
useState<User | null>(null)
  • User:指定状态的数据类型
  • | null:允许当前没有用户
  • null:初始化为空

这就是 TypeScript 的泛型与联合类型。

也可以这样写:

ini 复制代码
const [list, setList] = useState<User[]>([]);

表示 list 是一个 User 对象数组。

2.4 useState 最容易踩的坑

scss 复制代码
setCount(count + 1);
console.log(count);

执行之后,console.log 不一定打印更新后的状态。

原因:

调用 setCount 不会直接修改当前这次函数执行中 count 变量的值。

React 会在后续渲染中提供新的状态值。

另外,连续执行:

scss 复制代码
setCount(count + 1);
setCount(count + 1);

不能保证增加两次,因为两次读取的可能是相同的 count。

正确写法:

ini 复制代码
setCount(prev => prev + 1);
setCount(prev => prev + 1);

三、useEffect:处理副作用

3.1 什么叫副作用?

先来看一个场景。

页面打开时,需要请求后端接口获取任务列表。

在 React 中,渲染页面是组件的主要职责。

而下面这些行为不是单纯计算页面内容:

  • 请求后端接口
  • 注册浏览器事件
  • 创建定时器
  • 订阅外部消息
  • 操作浏览器外部系统

这类操作通常称为副作用(Side Effect) 。

React 提供 useEffect,用于让组件与外部系统同步。

3.2 基本语法

javascript 复制代码
import { useEffect } from 'react';

useEffect(() => {
  console.log('Effect 执行了');
}, []);

拆解:

scss 复制代码
useEffect(
  () => {
    // 需要执行的副作用
  },
  []
);

它接收两个主要参数:

  1. 第一个参数:副作用执行函数
  2. 第二个参数:依赖数组

依赖数组决定 Effect 在哪些情况下需要重新执行。

3.3 useEffect 的三种常见写法

第一种:不传依赖数组

javascript 复制代码
useEffect(() => {
  console.log('组件渲染完成');
});

首次渲染后执行,并且每次重新渲染后都会执行。

第二种:空依赖数组

scss 复制代码
useEffect(() => {
  console.log('组件挂载');
}, []);

一般情况下,组件挂载后执行一次。

注意:开发环境启用 React Strict Mode 时,React 可能额外执行一次 Effect 的设置与清理,用于检查代码是否安全。因此开发时看到两次日志,并不一定是程序异常。

第三种:指定依赖变量

scss 复制代码
const [count, setCount] = useState(0);

useEffect(() => {
  console.log('count 发生变化:', count);
}, [count]);

首次渲染后执行,并在 count 变化后重新执行。

注意:并不是只有变化时才执行,首次挂载后也会执行。

3.4 useEffect 请求接口

假设接口如下:

bash 复制代码
GET /api/tasks

返回:

css 复制代码
[  { "id": 1, "name": "任务A" },  { "id": 2, "name": "任务B" }]

React 中可以这样写:

ini 复制代码
import { useState, useEffect } from 'react';

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

function TaskList() {
  const [tasks, setTasks] = useState<Task[]>([]);

  useEffect(() => {
    async function fetchTasks() {
      const response = await fetch('/api/tasks');
      const data: Task[] = await response.json();

      setTasks(data);
    }

    fetchTasks();
  }, []);

  return (
    <div>
      {tasks.map(task => (
        <p key={task.id}>{task.name}</p>
      ))}
    </div>
  );
}

执行流程:

  1. 组件第一次渲染,tasks 为空数组。
  2. React 完成页面渲染。
  3. useEffect 执行,调用接口。
  4. 接口返回数据。
  5. setTasks(data) 更新状态。
  6. React 重新渲染,页面显示任务列表。

这就是最常见的 React 数据请求流程之一。

生产环境还应考虑请求错误、加载状态、请求取消等问题。

3.5 useEffect 的清理函数

假设监听浏览器窗口大小:

javascript 复制代码
useEffect(() => {
  function handleResize() {
    console.log(window.innerWidth);
  }

  window.addEventListener('resize', handleResize);

  return () => {
    window.removeEventListener('resize', handleResize);
  };
}, []);

这里:

javascript 复制代码
return () => {
  window.removeEventListener('resize', handleResize);
};

是清理函数。

清理函数会在组件卸载时执行,也会在 Effect 依赖变化、重新执行之前调用。

目的就是避免重复注册监听或资源泄漏。

3.6 useEffect 的常见误区

很多新手看到数据变化,就想用 useEffect。

例如:

scss 复制代码
const [price, setPrice] = useState(100);
const [count, setCount] = useState(2);
const [total, setTotal] = useState(200);

useEffect(() => {
  setTotal(price * count);
}, [price, count]);

这虽然能够工作,但是不推荐。

因为 total 完全可以由 price 和 count 计算出来。

更简单的写法:

ini 复制代码
const [price, setPrice] = useState(100);
const [count, setCount] = useState(2);

const total = price * count;

能在渲染过程中直接计算的数据,通常不需要额外用 useEffect 和 useState 保存。


四、useRef:保存引用和操作 DOM

4.1 useRef 是什么?

先记住一个最重要的区别:

  • useState:修改状态通常会触发重新渲染
  • useRef:修改 .current 不会触发重新渲染

基本语法:

ini 复制代码
const countRef = useRef(0);

它会返回:

css 复制代码
{
  current: 0
}

可以通过:

ini 复制代码
countRef.current = 10;

修改值。

4.2 useRef 操作 DOM

例如点击按钮后,自动聚焦输入框。

javascript 复制代码
import { useRef } from 'react';

function Search() {
  const inputRef = useRef<HTMLInputElement>(null);

  function focusInput() {
    inputRef.current?.focus();
  }

  return (
    <div>
      <input ref={inputRef} />
      <button onClick={focusInput}>
        聚焦输入框
      </button>
    </div>
  );
}

拆解:

ini 复制代码
const inputRef = useRef<HTMLInputElement>(null);

表示创建一个引用,用于保存 HTML 输入框的 DOM 元素。

ini 复制代码
<input ref={inputRef} />

告诉 React:将这个输入框的 DOM 元素赋给 inputRef.current。

ini 复制代码
inputRef.current?.focus();

调用输入框的聚焦方法。

其中 ?. 是 JavaScript 的可选链操作符。

如果 current 不为 null,就执行 focus()。

4.3 useRef 保存定时器

javascript 复制代码
const timerRef = useRef<ReturnType<typeof setInterval> | null>(null);

function start() {
  if (timerRef.current !== null) return;

  timerRef.current = setInterval(() => {
    console.log('执行定时任务');
  }, 1000);
}

function stop() {
  if (timerRef.current !== null) {
    clearInterval(timerRef.current);
    timerRef.current = null;
  }
}

这里的 timerRef 用来保存定时器 ID。

因为保存定时器 ID 本身不需要刷新页面,所以适合使用 useRef。


五、useMemo:缓存计算结果

5.1 为什么需要 useMemo?

React 组件每次重新渲染时,函数中的普通计算也会重新执行。

例如:

javascript 复制代码
function App() {
  const [count, setCount] = useState(0);

  const result = expensiveCalculation();

  return <div>{result}</div>;
}

即使 expensiveCalculation() 的输入没有变化,点击按钮更新 count 后,组件重新渲染,也会重新计算。

如果计算非常耗时,就可能造成性能浪费。

5.2 useMemo 的基本语法

ini 复制代码
const result = useMemo(() => {
  return expensiveCalculation();
}, []);

它会缓存计算结果。

只有依赖发生变化时,才重新计算。

例如:

ini 复制代码
const [list, setList] = useState([1, 2, 3]);
const [count, setCount] = useState(0);

const total = useMemo(() => {
  console.log('重新计算总和');

  return list.reduce((sum, num) => sum + num, 0);
}, [list]);

这里:

  • list 变化时,重新计算 total
  • count 变化时,通常复用缓存的 total

注意:useMemo 是性能优化工具,不是保证只执行一次的机制。

5.3 什么时候使用 useMemo?

适用于:

  • 大量数据排序
  • 复杂列表过滤
  • 大量数据聚合
  • 昂贵的计算逻辑

例如:

ini 复制代码
const filteredTasks = useMemo(() => {
  return tasks.filter(task =>
    task.name.includes(keyword)
  );
}, [tasks, keyword]);

如果数据很少、计算很简单,直接计算通常就足够了。

不要为了使用 Hook 而使用 Hook。


六、useCallback:缓存函数

6.1 useCallback 是什么?

在 React 函数组件中:

javascript 复制代码
function App() {
  const handleClick = () => {
    console.log('click');
  };

  return <button onClick={handleClick}>点击</button>;
}

每次组件重新渲染,都会创建一个新的 handleClick 函数对象。

通常这没有问题。

但是,如果将函数传给经过 memo 优化的子组件,新的函数引用可能导致子组件不必要地重新渲染。

6.2 基本语法

ini 复制代码
const handleClick = useCallback(() => {
  console.log('click');
}, []);

useCallback 会缓存函数引用。

只有依赖变化时,React 才会返回新的函数引用。

例如:

ini 复制代码
const [count, setCount] = useState(0);

const handleAdd = useCallback(() => {
  setCount(prev => prev + 1);
}, []);

这里依赖数组为空是合理的,因为函数只使用了稳定的 setCount 更新函数,并通过函数式更新读取最新状态。

6.3 useMemo 和 useCallback 的区别

对比 useMemo useCallback
缓存什么 计算结果 函数引用
返回什么 计算后的值 函数本身
主要用途 减少昂贵计算 保持函数引用稳定

一句话记忆:

useMemo 缓存结果,useCallback 缓存函数。

不要默认给所有函数套 useCallback,只有确实需要稳定函数引用时再使用。


七、useContext:跨组件传递数据

7.1 为什么需要 useContext?

假设组件结构如下:

css 复制代码
App
 └── Layout
      └── Header
           └── UserInfo

现在 App 里面有一个用户名:

ini 复制代码
const username = '张三';

如果使用 Props 传递,需要:

css 复制代码
App → Layout → Header → UserInfo

中间每一层都需要接收并继续传递参数。

这种现象叫做 Props Drilling(属性逐层传递)。

React 提供 Context 来共享数据,useContext 则用于读取这些数据。

7.2 示例:共享用户信息

先创建 Context:

ini 复制代码
import { createContext, useContext } from 'react';

const UserContext = createContext('游客');

父组件提供数据:

javascript 复制代码
function App() {
  return (
    <UserContext.Provider value="张三">
      <Header />
    </UserContext.Provider>
  );
}

子组件读取:

javascript 复制代码
function Header() {
  const username = useContext(UserContext);

  return <h1>欢迎,{username}</h1>;
}

即使中间存在很多层组件,也可以通过 Context 读取最近的上层 Provider 提供的数据。

7.3 useContext 适合什么?

常见场景:

  • 登录用户信息
  • 主题配置
  • 国际化语言配置
  • 全局偏好设置

但是 Context 并不等于完整的状态管理框架。

共享值变化时,读取该 Context 的组件可能重新渲染,因此仍然需要关注使用范围和性能。


八、useReducer:复杂状态管理

8.1 为什么需要 useReducer?

如果一个页面存在多个状态:

scss 复制代码
const [loading, setLoading] = useState(false);
const [error, setError] = useState('');
const [data, setData] = useState([]);

随着业务逻辑越来越复杂,状态之间会存在关联。

此时可以考虑 useReducer。

它的核心思想是:

把状态修改规则集中放到一个函数中。

8.2 useReducer 示例

javascript 复制代码
import { useReducer } from 'react';

function reducer(state: number, action: string) {
  switch (action) {
    case 'add':
      return state + 1;

    case 'minus':
      return state - 1;

    case 'reset':
      return 0;

    default:
      return state;
  }
}

function Counter() {
  const [count, dispatch] = useReducer(reducer, 0);

  return (
    <div>
      <p>{count}</p>

      <button onClick={() => dispatch('add')}>
        +1
      </button>

      <button onClick={() => dispatch('minus')}>
        -1
      </button>

      <button onClick={() => dispatch('reset')}>
        重置
      </button>
    </div>
  );
}

这里需要理解三个概念:

  • state:当前状态
  • action:希望执行的操作
  • dispatch:发送操作,触发状态计算

例如:

scss 复制代码
dispatch('add');

可以理解为:

React,我要执行 add 操作,请根据 reducer 中的规则生成新的状态。

8.3 useState 和 useReducer 怎么选?

简单状态:

scss 复制代码
const [visible, setVisible] = useState(false);

优先使用 useState。

多个相关状态、复杂状态转换:

scss 复制代码
const [state, dispatch] = useReducer(reducer, initialState);

可以考虑 useReducer。


九、自定义 Hook:封装重复逻辑

9.1 什么叫自定义 Hook?

React 内置了 useState、useEffect 等 Hook。

但是实际开发中,经常会遇到重复的状态逻辑。

例如多个页面都需要:

  • 请求任务列表
  • 管理 loading
  • 管理 error
  • 保存接口数据

我们可以把这些逻辑封装为一个自定义 Hook。

9.2 示例:useTasks

scss 复制代码
import { useEffect, useState } from 'react';

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

function useTasks() {
  const [tasks, setTasks] = useState<Task[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');

  useEffect(() => {
    const controller = new AbortController();

    async function loadTasks() {
      try {
        const response = await fetch('/api/tasks', {
          signal: controller.signal
        });

        if (!response.ok) {
          throw new Error('请求失败');
        }

        const data: Task[] = await response.json();
        setTasks(data);
      } catch (err) {
        if (!controller.signal.aborted) {
          setError('加载任务失败');
        }
      } finally {
        if (!controller.signal.aborted) {
          setLoading(false);
        }
      }
    }

    loadTasks();

    return () => controller.abort();
  }, []);

  return { tasks, loading, error };
}

页面组件中直接使用:

javascript 复制代码
function TaskPage() {
  const { tasks, loading, error } = useTasks();

  if (loading) {
    return <p>加载中...</p>;
  }

  if (error) {
    return <p>{error}</p>;
  }

  return (
    <div>
      {tasks.map(task => (
        <p key={task.id}>{task.name}</p>
      ))}
    </div>
  );
}

这样,页面组件只需要关注页面如何展示,接口请求逻辑则放在 Hook 里。

注意:自定义 Hook 并不会自动让多个组件共享同一份状态。每次调用通常都有自己独立的状态。


十、Hook 的使用规则(必须掌握)

React Hook 并不是想在哪里调用就在哪里调用。

规则一:不要在条件判断中调用 Hook

错误写法:

scss 复制代码
if (visible) {
  const [count, setCount] = useState(0);
}

正确写法:

scss 复制代码
const [count, setCount] = useState(0);

if (visible) {
  // 使用 count
}

规则二:不要在循环中调用 Hook

错误写法:

ini 复制代码
for (let i = 0; i < 10; i++) {
  useState(0);
}

规则三:不要在普通事件函数中调用 Hook

错误写法:

scss 复制代码
function handleClick() {
  const [count, setCount] = useState(0);
}

React Hook 应该在 React 函数组件或自定义 Hook 的顶层调用。

为什么?

因为 React 需要依靠稳定的 Hook 调用顺序,将每次渲染中的状态正确对应起来。

可以记住:

Hook 放组件顶层,不能随意放进 if、for、事件函数中。


十一、实战:开发一个任务管理页面

现在把前面学到的内容组合起来。

需求:

  1. 页面显示任务列表。
  2. 输入任务名称。
  3. 点击按钮添加任务。
  4. 支持删除任务。
  5. 自动计算当前任务数量。
typescript 复制代码
import { useState, useMemo } from 'react';

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

function TaskManager() {
  // 1. 任务列表状态
  const [tasks, setTasks] = useState<Task[]>([]);

  // 2. 输入框状态
  const [keyword, setKeyword] = useState('');

  // 3. 添加任务
  function addTask() {
    const name = keyword.trim();

    if (!name) return;

    const newTask: Task = {
      id: Date.now(),
      name
    };

    setTasks(prev => [...prev, newTask]);

    setKeyword('');
  }

  // 4. 删除任务
  function deleteTask(id: number) {
    setTasks(prev =>
      prev.filter(task => task.id !== id)
    );
  }

  // 5. 计算任务数量(这里其实不必用 useMemo)
  const taskCount = tasks.length;

  // 6. 渲染页面
  return (
    <div>
      <h2>任务管理系统</h2>

      <input
        value={keyword}
        onChange={e => setKeyword(e.target.value)}
        placeholder="请输入任务名称"
      />

      <button onClick={addTask}>
        添加任务
      </button>

      <p>当前任务数量:{taskCount}</p>

      {tasks.map(task => (
        <div key={task.id}>
          <span>{task.name}</span>

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

export default TaskManager;

11.1 执行流程

假设输入框输入:

复制代码
数据回扫任务

执行:

scss 复制代码
setKeyword('数据回扫任务');

触发组件重新渲染,输入框显示对应内容。

点击添加:

ini 复制代码
setTasks(prev => [...prev, newTask]);

React 保存新的任务数组,并重新渲染任务列表。

这里:

ini 复制代码
tasks.map(task => (
  <div key={task.id}>
    {task.name}
  </div>
))

表示遍历任务数组,把每个任务渲染为一个元素。

key 用来帮助 React 识别列表中的元素。

点击删除:

ini 复制代码
setTasks(prev =>
  prev.filter(task => task.id !== id)
);

创建一个不包含指定任务的新数组,React 再次更新页面。

整个流程就是:

复制代码
用户操作
   ↓
触发事件函数
   ↓
调用 setState
   ↓
React 重新渲染组件
   ↓
根据最新 State 生成页面

这也是 React 开发中最核心的思维方式。


十二、工作中应该优先掌握哪些 Hook?

优先级 Hook 掌握程度
⭐⭐⭐⭐⭐ useState 必须熟练
⭐⭐⭐⭐⭐ useEffect 必须熟练
⭐⭐⭐⭐ useRef 掌握常见场景
⭐⭐⭐⭐ useContext 能看懂、能使用
⭐⭐⭐ useMemo 理解缓存机制
⭐⭐⭐ useCallback 理解函数引用
⭐⭐⭐ useReducer 理解复杂状态管理
⭐⭐⭐⭐ 自定义 Hook 能理解和封装基础逻辑

React 还提供 useLayoutEffect、useId、useTransition、useDeferredValue 等其他 Hook,建议在掌握基础 Hook 后再学习。

最后总结

React Hooks 看起来很多,但可以按职责分类:

第一类:状态管理

  • useState:管理普通状态
  • useReducer:管理复杂状态

第二类:副作用与引用

  • useEffect:与外部系统同步
  • useRef:保存引用、操作 DOM

第三类:性能优化

  • useMemo:缓存计算结果
  • useCallback:缓存函数引用

第四类:数据共享

  • useContext:读取跨组件共享的数据

第五类:逻辑复用

  • 自定义 Hook:复用带有状态和其他 Hook 的逻辑

真正需要建立的是这个核心认知:

React 不是让我们直接修改 HTML 页面,而是让我们描述:当前数据状态下,页面应该长什么样。

用户操作改变状态,React 根据新状态更新页面。

理解这一点之后,Hook 的学习会容易很多。

相关推荐
Penge66611 分钟前
《Go 后端开发者学 React:前端 HTTP 请求详解》
javascript
Penge6669 小时前
《Go 后端开发者学 React:组件通信与插槽》
javascript
Penge66611 小时前
《Go 后端开发者学 React:useState》
javascript
Penge66611 小时前
《Go 后端开发者学 React:数据渲染》
javascript
ikoala14 小时前
AI 写页面像说明书,修 Bug 像贴膏药?把这两句话写进 AGENTS.md
前端·javascript·ai编程
Penge66614 小时前
《Go 后端开发者学 TypeScript:ES Module了解》
javascript
Penge66614 小时前
《Go 后端开发者学 TypeScript:从零搭建与运行》
javascript
Penge66615 小时前
《Go 后端开发者学 TypeScript:keyof关键字》
javascript
Penge66616 小时前
《Go 后端开发者学 TypeScript:extends关键字》
javascript