news 2026/10/21 17:48:10

React Hooks 组合实战:useState、useEffect、useContext 协同与自定义 Hook 封装

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
React Hooks 组合实战:useState、useEffect、useContext 协同与自定义 Hook 封装

1. 先把 React Hooks 3 这件事聊透

很多同学看到“React Hooks3”这个标题,第一反应是“是不是又出了三个新 Hook”。实际搞过一段时间 React 的人应该都清楚,社区里每隔一阵就会冒出来各种新名词,有的来自 React 官方,有的来自生态工具,有的纯粹是某个团队内部封装后的习惯叫法。我最初看到这个标题也以为是某种新版本发布,后来仔细梳理才发现,把 Hooks 相关的东西按“第三个阶段”来理解更靠谱,或者按“三个核心 Hooks 的组合运用”来切入也非常顺。

这个内容能解决的问题很直接:当 useState、useEffect、useContext 单独用都顺了,怎么把它们组合起来写出更健壮的组件逻辑;怎么在函数组件里组织数据流和副作用;怎么把分散在多个组件里的公共状态逻辑抽出来复用。适合的人群也明确:已经会用基础 Hooks、但面对复杂业务场景还是容易绕晕的中级前端开发者。如果你刚接触 React 没多久,直接上来啃这一篇会有点吃力,建议先把基础 Hook 的文档过一遍再回来继续读。

Hooks 这套东西从一开始出现,目的就是让函数组件拥有类组件的能力,同时避开 class 里 this 指向、生命周期逻辑分散这些问题。但很多人用了一段时间后就会发现一个问题:基础 Hook 只是起点,真正决定代码质量的是组合的思路。你可以在一个组件里写十来个 useState 和 useEffect,跑起来没有任何问题,但别人接手的时候大概率会想骂人。这不是 Hook 本身的问题,是组织方式的问题。那么接下来就从设计思路开始,把 Hooks 组合使用的整套逻辑重新拆一遍。

2. 为什么需要“Hooks 3”这种组合视角

2.1 从“会用单个 Hook”到“体系化用 Hook”的跨越

先看一个非常典型的例子。某个列表页面,需求是进入页面后从后端拉取数据、展示加载态、支持下拉刷新、点击条目跳详情、离开页面时清理定时器。如果你只是会 useState 和 useEffect,大概率会写出这样一段代码:

function ListPage() { const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [refreshing, setRefreshing] = useState(false); useEffect(() => { fetchData(); }, []); async function fetchData() { setLoading(true); try { const res = await fetch('/api/list'); const json = await res.json(); setData(json.data); } catch (e) { setError(e.message); } finally { setLoading(false); } } async function refreshData() { setRefreshing(true); try { const res = await fetch('/api/list?refresh=1'); const json = await res.json(); setData(json.data); } finally { setRefreshing(false); } } return <div>{/* 渲染逻辑 */}</div>; }

这段代码能跑,但问题很明显:loading、refreshing、error 这几个状态各自独立,页面同时存在 loading 和 refreshing 为 true 的可能,逻辑上就是错的;fetchData 和 refreshData 两段逻辑高度重复;定时器清理这种副作用被塞在某个 useEffect 里,等组件复杂起来,副作用与渲染逻辑全部纠缠在一起。

这时候就需要换个视角:不是“这个组件里有哪些状态”,而是“这个页面有几种核心业务能力”。这个视角的转变,就是我理解的“Hooks 3”的核心意义——把 Hooks 当作一种组织业务逻辑的思维工具,而不是单纯的状态开关。按这个思路,上面的列表页可以被拆成几个清晰的逻辑单元:列表数据的拉取、加载态管理、定时器副作用管理。

2.2 三个关键 Hook 的协作模式

为什么要从“三个 Hook 的组合”切入建立体系?因为绝大多数业务场景都能被 useState、useEffect、useContext 这三个核心覆盖:

  • useState 是数据的载体,负责保持“当前应该显示什么”的状态;
  • useEffect 是数据的桥梁,负责响应状态变化并同步外部副作用;
  • useContext 是数据的通路,负责跨层级共享信息,避免 props 层层透传。

这三者的协作模式可以类比成一家小型公司的流程:useState 相当于仓库,负责储存公司当前资产的记录;useEffect 相当于执行部门,每次资产记录变化后,需要按需进行采购、清理、调整;useContext 相当于跨部门沟通渠道,让不同层级的信息可以直接传达,而不必每次都由管理层(祖先组件)层层转发。

这套协作模式落到具体代码中,常见的方式是这样:useState 定义核心状态,useEffect 监听状态变化并触发副作用,useContext 提供跨组件共享的全局状态。例如一个用户登录态的场景,AuthContext 里保存用户信息,LoginPage 用 useState 管理表单输入,登录成功之后通过 useEffect 将用户信息写入 Context,并持久化到 localStorage。

2.3 用自定义 Hook 拆分逻辑单元

当业务逐渐复杂,把所有 useState 和 useEffect 都堆在一个组件里,组件体积会膨胀到难以维护。这时候最自然的下一步,是把这些逻辑抽成一个自定义 Hook。自定义 Hook 本质上就是普通函数,只是约定以 use 开头,并且内部可以调用其他 Hook。

还是刚才的列表页,我会把列表数据的获取逻辑抽成一个 useFetchList 的 Hook:

function useFetchList(url, options = {}) { const [state, setState] = useState({ data: [], loading: false, error: null, refreshing: false }); useEffect(() => { let didCancel = false; setState(prev => ({ ...prev, loading: true })); fetch(url, options) .then(res => res.json()) .then(data => { if (!didCancel) { setState(prev => ({ ...prev, data: data.data, loading: false })); } }) .catch(e => { if (!didCancel) { setState(prev => ({ ...prev, error: e.message, loading: false })); } }); return () => { didCancel = true; }; }, [url]); const refresh = useCallback(() => { // 重新拉取 }, [url]); return { ...state, refresh }; }

这样组件里就不需要再关心 fetch 这些细节,只负责消费数据。这种拆分的价值在项目变大之后非常明显:一个页面组件从几百行缩到几十行,新需求加进来时,只需要改自定义 Hook 内部逻辑,页面组件的结构保持稳定。

2.4 什么时候不要用“三 Hook 组合”过度设计

这套组合确实好用,但不意味着每个函数组件都必须套用。如果你的组件里只有一两个状态、没有副作用、也没有跨组件共享的需求,硬套机制只会增加代码量。比如一个纯展示组件:

function ProductCard({ product }) { return <div>{product.name} - {product.price}</div>; }

这种组件完全不需要 Hook。很多人学了一套模式之后就忍不住所有组件都用自定义 Hook 包一层,反而把原来简单的代码搞复杂了。合理的评估标准是这样的:当组件里出现两个以上的 useState、一个以上无关联的 useEffect、或者多个组件共享同一份数据时,才值得引入这种组合与拆分策略。

3. 核心逻辑解析:状态、副作用与跨组件通信

3.1 状态管理的正确姿势:多个状态放一个 useState 还是拆开

这是每个 React 开发者都会纠结的问题。我见过很多代码把每个字段单独拆成一个 useState,也见过有人把整个表单对象塞进一个 useState 里。两种方式各有适用场景。

独立的 useState 适合这样几种情况:状态之间没有联动,更新频率差异大,或者状态值本身就是独立实体。比如一个视频播放器,当前播放时间、音量、播放/暂停状态都是互相独立的,拆开更清晰。

对象式 useState 适合字段之间天然关联、经常整体更新的场景。比如表单的输入值,一个 Form 里有 name、email、age 三个字段,用户每次输入更新其中一个,如果拆成三个独立状态,处理函数要写三个,最后提交时还得组装一遍;如果用一个对象,每次 setState 时用展开运算符合并更新即可。

不过对象式 useState 有一个非常容易踩的坑:直接修改对象属性不会触发渲染。很多人会在回调里写成state.name = 'xxx',然后发现页面没有任何变化。React 的比较机制靠的是引用变化,不是深度比较,只有当你创建了一个新对象(比如setState({ ...state, name: 'xxx' })),才能触发更新。这个坑的关键点就在这里,任何 Hook 使用中都绕不开。

再考虑到状态之间的协调,我会把业务强相关的字段聚合为一个状态对象,业务无关的独立字段拆开。比如列表页的 loading、error、refreshing 都与请求状态强相关,聚合为一个 state 对象更合适,因为它们变化的时机是同构的。

3.2 useEffect 的依赖数组:不是约束,是数据流契约

useEffect 的依赖数组是初学者最容易搞混的地方。它本质上是在描述这个副作用依赖哪些数据:当依赖项变化时,副作用重新执行;依赖项不变则不执行。很多人把依赖数组当成“这个 effect 要用到的变量列表”,漏了某个变量之后,组件运行起来出现陈旧数据。

我习惯把依赖数组理解成“数据流的一个节点”。每当渲染发生,React 会比较依赖数组里每一项的新旧值,如果有变化,就触发 effect 重新执行。这里的核心是,你必须把 effect 内部引用的所有外部变量都放进依赖数组,否则 React 无法知道 effect 是否需要重新执行。为了这一点,我通常会遵守两条规则:

  1. 凡是在 effect 里读取的 props 或 state,都必须出现在依赖里;
  2. 如果 effect 内部使用的函数是组件内部声明的,优先用 useCallback 包一层,确保函数引用稳定,然后将其加入依赖。

实际操作中,当涉及深层嵌套的复杂状态时,依赖数组并不能帮你做深度比较。useEffect(() => { }, [obj])里面 obj 的任何属性变化都不会触发 effect,因为 obj 的引用没有变。这种情况要么拆散依赖项,要么用 useMemo 创建派生数据。

3.3 useContext:跨组件通信的边界感

Context 提供了一种直接跨层级传递数据的能力,避免 props 层层透传。但 useContext 也有明显边界:一旦某个 Context 的值变化,所有消费它的组件都会重新渲染。这正是性能问题的源头之一。如果你的 Context 里放了一个数据对象,每次父组件更新时,如果 new 出来的对象引用变了,所有子组件会无条件重新渲染。

为了控制这种影响,常见的做法是把 Context 按职责拆成若干独立的小 Context。比如 user 信息一个 Context,主题设置一个 Context,权限数据一个 Context。这样更新 user 时不会把主题相关的所有组件也拖下水。另外可以将 Context 的 value 用 useMemo 包裹,避免无意义的引用变化。

3.4 自定义 Hook 的状态隔离机制

很多人写自定义 Hook 时最大的疑问是:多个组件都用同一个 Hook,它们的状态会不会互相串?答案是不会。每个组件在调用 Hook 时,Hook 内部 useState 创建的都是独立的 state,由该组件实例单独持有。这有点像是每个组件从工厂领取了一套独立工具,工具型号相同,但归各自所有。

这个特性带来了一个自然的分层能力:当你把某些逻辑抽成自定义 Hook 后,组件之间天然隔离,互不污染。比如两个列表页都用了 useFetchList,它们的 data、loading 各管各的,不会有任何通信。真正需要共享的部分,再通过 Context 或者状态管理库显式地共享,这样的设计心智负担最小。

4. 实操过程:从组件代码到完整页面,一步步怎么搭

4.1 第一步:先定数据结构,再动手写 Hook

很多人的习惯是一上来就写组件 JSX,写着写着发现需要状态了,才补 useState。这种反向流程容易让组件代码长成一棵没有章法的结构。我建议的顺序是反的:先想清楚页面需要哪些数据,数据的形态是什么,再定义 Hook 的接口。

假设要做一个后台管理系统里的用户列表页面。需要的数据大致如下:

  • 用户列表 users
  • 当前加载状态 loading
  • 加载失败时的错误 error
  • 当前页码 page
  • 每页条数 pageSize

这就是一个非常标准的请求状态集合。按照前面说的组合思路,可以把“需要 loading、error 等并列状态”作为一个整体管理,也可以用单一 state 对象管理。我较为推荐用单一对象,原因也很实际:这组状态的变更总是同步发生的。发起请求时 loading 变 true、error 清空;请求结束时 loading 变 false、data 填充。把它们放进同一个 useState 之后,状态变更逻辑就收敛在 Hook 内部,返回给组件的是一个不可分割的数据快照。

4.2 第二步:自定义 Hook 里如何组织 useEffect

以刚才的用户列表为例,我们写一个 useUserList,它接收请求参数并返回列表状态和操作函数:

function useUserList(params) { const [state, setState] = useState({ users: [], loading: false, error: null, page: 1, pageSize: 10 }); useEffect(() => { let cancelled = false; async function loadUsers() { setState(prev => ({ ...prev, loading: true, error: null })); try { const query = new URLSearchParams({ page: String(state.page), pageSize: String(state.pageSize) }).toString(); const res = await fetch(`/api/users?${query}`); const json = await res.json(); if (!cancelled) { setState(prev => ({ ...prev, users: json.data, loading: false })); } } catch (e) { if (!cancelled) { setState(prev => ({ ...prev, error: e.message, loading: false })); } } } loadUsers(); return () => { cancelled = true; }; }, [state.page, state.pageSize]); return { ...state }; }

这段代码里最关键的不是 fetch 本身,而是cancelled标记。它在组件卸载时被置为 true,这样异步请求即使晚到了也不会对已卸载组件调用 setState,避免了 React 报“在卸载组件上更新状态”的警告。虽然 React 18 之后这类警告没那么常见了,但清理副作用依然是一个好习惯。

同时依赖数组是[state.page, state.pageSize],这意味着页码变化时自动重新拉取。这种把“依赖驱动”的思维贯穿到代码里的方式,是 Hook 组合写法的精髓。你不需要在点击事件里手动调用 fetch,只需要改 page,请求就被 useEffect 自动触发。

4.3 第三步:把翻页、搜索这类操作接到组件上

有了 useUserList,组件内部逻辑就非常干净了:

function UserListPage() { const { users, loading, error, page, pageSize } = useUserList(); const [keyword, setKeyword] = useState(''); const handlePageChange = (nextPage) => { // 这里触发的状态更新会通过 useEffect 自动重新拉数据 }; const handleSearch = () => { // 把 keyword 传给 useUserList,变成请求参数 }; return ( <div> <input value={keyword} onChange={(e) => setKeyword(e.target.value)} /> <button onClick={handleSearch}>搜索</button> {loading && <div>加载中...</div>} {error && <div>加载失败:{error}</div>} <ul> {users.map(user => ( <li key={user.id}>{user.name}</li> ))} </ul> <button onClick={() => handlePageChange(page - 1)}>上一页</button> <button onClick={() => handlePageChange(page + 1)}>下一页</button> </div> ); }

这种设计让组件只做两件事:把用户输入事件传递给 Hook,把 Hook 返回的数据渲染出来。业务逻辑全都封装在 Hook 里,组件要测试或者复用就特别简单。将来如果要把这个列表页改成弹窗里的选择器,只需要复用 useUserList 即可,组件层整个替换都不会影响数据逻辑。

4.4 第四步:额外处理的分页参数修改问题

上面的代码有个细节要留意:page 和 pageSize 放在 useState 内部了,所以组件内部 handlePageChange 无法直接修改。如果想要外部控制分页,需要把 useUserList 接收的 params 提升为参数:

function useUserList({ page, pageSize }) { const [state, setState] = useState({ users: [], loading: false, error: null }); useEffect(() => { let cancelled = false; // 请求逻辑 return () => { cancelled = true; }; }, [page, pageSize]); return { ...state }; }

这两种写法分别被称为“内部状态”和“受控状态”。内部状态写法简单、适合自包含的场景;受控状态写法更灵活,适合需要外部参与状态决策的场景。实际项目里切换很频繁,这取决于页面是否需要上层控制列表行为。比如 tab 切换时多个列表共享同一个分页状态,就适合用受控写法。

4.5 第五步:跨组件共享筛选条件,用 Context 衔接

假设系统里有多个页面都要展示当前的筛选条件,并且筛选条件变化后各页面联动更新。这种情况下,把筛选条件放在 Context 里比放在各自组件的 state 里更合理。建一个 FilterContext:

const FilterContext = createContext(null); function FilterProvider({ children }) { const [filters, setFilters] = useState({ keyword: '', status: 'all' }); const updateFilters = useCallback((nextFilters) => { setFilters(prev => ({ ...prev, ...nextFilters })); }, []); const value = useMemo(() => ({ filters, updateFilters }), [filters, updateFilters]); return <FilterContext.Provider value={value}>{children}</FilterContext.Provider>; } function useFilters() { const context = useContext(FilterContext); if (!context) { throw new Error('useFilters 必须在 FilterProvider 内使用'); } return context; }

这里有个容易被忽略的细节:value 里放了一个updateFilters函数,如果不包useCallback,每次 Provider 重新渲染时 value 都会变化,所有消费 FilterContext 的子组件都会跟着重渲染。这个问题的根源是 Context 的 value 比较逻辑是引用比较,不是值比较。所以useMemo在这里并不是表演,而是实打实的性能保障。

业务组件里调用 useFilters 获取筛选条件,再传给 useUserList,数据流就串起来了:

function UserListPage() { const { filters } = useFilters(); const { users, loading, error, page } = useUserList({ page: 1, keyword: filters.keyword, status: filters.status }); // ... }

这样用户在某一个页面改了筛选条件,其他订阅了 FilterContext 的页面会自动感知并更新数据。这种“Context 负责共享,Hook 负责状态推导”的分工模式,恰恰是 React 函数组件时代最常用的组合思路。

4.6 一个完整页面的编排示例

把上面这些组合起来,模拟一个完整的用户管理页面骨架:

function UserManagePage() { const { filters } = useFilters(); const { users, loading, error, page } = useUserList({ page: 1, keyword: filters.keyword, status: filters.status }); const [selectedUserId, setSelectedUserId] = useState(null); useEffect(() => { if (selectedUserId) { document.title = `正在查看用户 ${selectedUserId}`; } else { document.title = '用户管理'; } }, [selectedUserId]); return ( <div> <SearchBar /> <UserTable users={users} loading={loading} error={error} /> <Pagination page={page} /> {selectedUserId && <UserDetailModal id={selectedUserId} />} </div> ); }

这个页面组件里只剩两类状态:来自 Context 的筛选条件、一个本地选中的用户 ID。其他业务细节全被 Hook 收走。这样写的组件拿到代码评审会上,任何人一眼就能看出数据从哪里来、到哪里去,维护成本直线下降。

5. 实操中的常见报错与排查手册

5.1 依赖数组漏项导致的“数据总是旧的”

这是 Hooks 使用中最高频的问题之一。表现是:effect 里 reads 了某个 props,但依赖数组没写,props 更新后 effect 不执行,界面还是显示旧数据。

排查方法很简单:把 effect 里用到的所有变量列出来,挨个核对依赖数组。如果你用的是现代编辑器和 ESLint 的 react-hooks 插件,它通常能自动提示缺失依赖。但依赖插件不等于依赖正确,有时插件会建议把整个外部函数加入依赖,而那个函数本身没有 useCallback 包裹,引用了及时更新的状态,导致 effect 频繁触发。这种情况要自己分析是加依赖还是稳定函数。

5.2 无限循环:setState 在 effect 里触发

典型的错误写法:

useEffect(() => { setState(prev => ({ ...prev, users: getData() })); }, [state.page]);

如果 getData() 每次返回新数组,那么 state 变化后 effect 再次执行,再次 setState,无限循环。解决方式通常是:让依赖更新与效果触发保持单向。setState 本身不应该成为 effect 执行的直接依赖,除非这是有意为之的解耦逻辑。

5.3 组件卸载后警告

前面提到的 cancelled 标记解决的就是这个问题。在 useEffect 的清理函数里,给一个 flag 标记异步操作的取消状态。另外,如果你的请求库已经支持 AbortController,直接用也可以。

5.4 useContext 使用时容易遇到的三个问题

第一个是忘记包裹 Provider,导致 useContext 返回 null 或抛错。我的习惯是在自定义 Hook 里做显式判断并抛出明确错误信息,而不是让调用方在空值里挣扎。

第二个是 value 引用频繁变化导致大范围重渲染,解决方案就是前面提到的 useMemo 包 value。

第三个是过度使用 Context 导致调试困难。当你打开 React DevTools 发现组件树被一层层 Provider 包裹得密不透风,就该反思哪些状态可以挪到其他层级或合成到单一 Provider 里。实际项目中我倾向于按领域拆开,而不是无脑包一层全部塞进去。

5.5 常见问题速查表

问题原因解决思路
数据更新后界面不刷新setState 直接修改了对象用展开运算符生成新对象
effect 多次执行依赖了引用变化的函数或对象用 useCallback 或 useMemo 稳定引用
组件卸载后还 setState异步请求没有取消在清理函数里设置 cancelled 标记
useContext 返回 undefined组件不在 Provider 内部在 Hook 内抛出显式错误
状态更新触发两次请求React 开发模式严格模式 double-invoke用请求标识或 useEffect 依赖控制幂等性

6. 从这套组合模式还能扩展到什么地方

如果这个“Hooks 3”的组合思路你已经掌握了,自定义 Hook 还能继续做不少事。

比较常见的是数据请求 Hook 的泛化。可以把 useFetchList 升级成 useRequest,接收 URL、请求参数、转换函数、缓存策略。这样整个项目里的所有请求都可以统一走一个逻辑,统一处理 loading、取消、错误重试。不同业务页面之间只存在参数差异,代码高度复用。

再比如表单场景,把输入值、校验规则、错误信息、脏标记封装成 useFormState,配合 Context 可以做一个全局表单状态管理。这套东西用 Hooks 做起来比传统 class 组件时代轻量太多。

更深一步,还能把主题切换、权限判断、路由联动都封装成自定义 Hook。权限场景往往会按角色从接口拉一份权限列表,封装成 usePermission 之后,任何组件里调用一次就能判断是否可操作。这比在组件里到处 useMemo 计算要清晰不少。

当然,每次引入新抽象都是有代价的。如果一个自定义 Hook 本身的逻辑比它封装的业务还要复杂,那封装就失去了意义。我在实际项目里的判断标准始终是:代码的可读性有没有提升,出现问题时能不能更快定位,改动需求时是不是只需要动一处。如果答案都是肯定的,这种抽法才是值得的。

最终这段实践沉淀下来的结论很简单:Hooks 并不是越多越好,也不是越封装越高级,而是要用它们把数据流梳理清楚,把变化收敛到可控范围。React 的函数组件时代,代码的核心沉淀路径就是状态、副作用和共享数据的组合设计,理解了这条主线之后,再多新 Hook 出现,你也能很快吸收进自己的体系里。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 17:46:19

动态反演+ESO+自适应:无人机姿态控制全家桶复现指南

最近我在复现一套无人机姿态控制方案&#xff0c;课题名称里串了四个关键词——动态反演、扩展状态观测器ESO、反馈线性化、自适应——翻译成大白话就是&#xff1a;用逆模型把非线性抵消掉&#xff0c;再用观测器把内外扰动估出来&#xff0c;最后用自适应律把参数不确定性也兜…

作者头像 李华
网站建设 2026/10/21 17:46:00

开源GPU OCR新方案:不依赖CUDA,C#与Web双端可用的Vulkan推理实践

1. 这个项目到底在解决什么问题第一次看到“开源 GPU OCR&#xff1a;不依赖 CUDA&#xff0c;C# 和 Web 都能用的 lw.PPOCR.Vulkan”这个标题&#xff0c;我脑子里蹦出来的第一个念头是&#xff1a;终于有人把 OCR 推理从 CUDA 的绑定里拆出来了。做过 OCR 落地的朋友应该都有…

作者头像 李华
网站建设 2026/10/21 17:45:46

若依项目接口测试实战:从调通到权限与数据隔离的深度验证

若依项目-接口测试这个系列&#xff0c;上一篇我们解决了最基础的登录鉴权、Token获取和用户信息核对&#xff0c;算是把接口测试的门票拿到了手里。但说实话&#xff0c;业务接口测试和登录态测试完全不是一个量级——登录通了只是入场&#xff0c;真正值钱的是把权限控制、数…

作者头像 李华
网站建设 2026/10/21 17:44:29

jmap 堆内存诊断实战:从 histo 到 dump 的线上排查指南

1. 堆内存诊断到底在诊断什么 线上服务跑着跑着突然变慢&#xff0c;接口响应从几十毫秒飙到几秒&#xff0c;监控面板上内存曲线一路向上不回头&#xff0c;这时候你登录机器&#xff0c;第一反应往往是“是不是堆爆了”。但“堆爆了”这三个字太笼统&#xff0c;它可能是老年…

作者头像 李华
网站建设 2026/10/21 17:43:31

Local Figma Agent MCP:让AI Agent直接读取设计稿的本地化方案

1. 当设计稿成为Agent的"盲区"&#xff1a;一个被低估的协作断点 做前端或者全栈的朋友大概都有过这种体验&#xff1a;产品经理甩过来一个Figma链接&#xff0c;说"就按这个做"&#xff0c;然后你打开一看&#xff0c;几十个画板、上百个组件、各种嵌套的…

作者头像 李华
网站建设 2026/10/21 17:40:53

AI对话四象限:分清场景,让AI真正好用起来

作为长期折腾各种大语言模型应用的人&#xff0c;我经常被问到同一个问题&#xff1a;为什么有时候觉得 AI 特别聪明&#xff0c;一句话就能给出惊艳的回答&#xff1b;有时候又觉得它蠢得离谱&#xff0c;反复纠正都答不到点上。这个问题困扰了我很久&#xff0c;直到我用&quo…

作者头像 李华