相同逻辑不同模板:如何实现多组件复用同一方法?
复用逻辑、分离视图的组件优化方案
针对桌面端和移动端组件逻辑一致但布局不同的场景,核心思路是将业务逻辑与视图渲染彻底分离,让逻辑只维护一次,视图各自实现。以下是几种主流的最佳实践:
1. 自定义Hook/组合式API(推荐)
这是React和Vue当前主推的方案,把通用逻辑封装成独立的可复用单元,在不同视图组件中导入使用。
React 示例
// 封装公共逻辑:useItemActions.js import { useState } from 'react'; export const useItemActions = () => { const [items, setItems] = useState([]); const handleCreate = (newItem) => { setItems([...items, newItem]); }; const handleRename = (id, newName) => { setItems(items.map(item => item.id === id ? {...item, name: newName} : item)); }; const handleDelete = (id) => { setItems(items.filter(item => item.id !== id)); }; return { items, handleCreate, handleRename, handleDelete }; };
桌面端组件只负责渲染:
// DesktopItemManager.jsx import { useItemActions } from './useItemActions'; const DesktopItemManager = () => { const { items, handleCreate, handleRename, handleDelete } = useItemActions(); return ( <div className="desktop-layout"> <button onClick={() => handleCreate({id: Date.now(), name: '新建项'})}>创建</button> {items.map(item => ( <div key={item.id} className="desktop-item"> <input value={item.name} onChange={(e) => handleRename(item.id, e.target.value)} /> <button onClick={() => handleDelete(item.id)}>删除</button> </div> ))} </div> ); };
移动端组件只需替换布局部分,逻辑完全复用。
Vue 3 示例
// 封装公共逻辑:useItemActions.js import { ref } from 'vue'; export function useItemActions() { const items = ref([]); const handleCreate = (newItem) => { items.value.push(newItem); }; const handleRename = (id, newName) => { const target = items.value.find(i => i.id === id); if (target) target.name = newName; }; const handleDelete = (id) => { items.value = items.value.filter(i => i.id !== id); }; return { items, handleCreate, handleRename, handleDelete }; }
桌面端组件:
<template> <div class="desktop-layout"> <button @click="handleCreate({id: Date.now(), name: '新建项'})">创建</button> <div v-for="item in items" :key="item.id" class="desktop-item"> <input v-model="item.name" @blur="handleRename(item.id, item.name)" /> <button @click="handleDelete(item.id)">删除</button> </div> </div> </template> <script setup> import { useItemActions } from './useItemActions'; const { items, handleCreate, handleRename, handleDelete } = useItemActions(); </script>
2. 高阶组件(HOC)
把公共逻辑封装成高阶组件,接收视图组件作为参数,将逻辑方法通过props传递给视图组件。
// withItemActions.js import { useState } from 'react'; const withItemActions = (WrappedComponent) => { return (props) => { const [items, setItems] = useState([]); const handleCreate = (newItem) => setItems([...items, newItem]); const handleRename = (id, newName) => setItems(items.map(item => item.id === id ? {...item, name: newName} : item)); const handleDelete = (id) => setItems(items.filter(item => item.id !== id)); return <WrappedComponent {...props} items={items} handleCreate={handleCreate} handleRename={handleRename} handleDelete={handleDelete} />; }; };
使用时直接包裹视图组件:
// DesktopItemManager.jsx const DesktopItemManager = ({ items, handleCreate, handleRename, handleDelete }) => { return ( <div className="desktop-layout"> {/* 桌面端布局 */} </div> ); }; export default withItemActions(DesktopItemManager);
3. 基础组件+插槽传递视图
定义一个仅处理逻辑的基础组件,通过插槽(或渲染回调)让桌面端/移动端组件传入各自的视图结构。
// BaseItemManager.jsx import { useState } from 'react'; const BaseItemManager = ({ children }) => { const [items, setItems] = useState([]); const handleCreate = (newItem) => setItems([...items, newItem]); const handleRename = (id, newName) => setItems(items.map(item => item.id === id ? {...item, name: newName} : item)); const handleDelete = (id) => setItems(items.filter(item => item.id !== id)); return children({ items, handleCreate, handleRename, handleDelete }); };
使用时传入视图:
// DesktopItemManager.jsx const DesktopItemManager = () => { return ( <BaseItemManager> {({ items, handleCreate, handleRename, handleDelete }) => ( <div className="desktop-layout"> {/* 桌面端布局 */} </div> )} </BaseItemManager> ); };
最佳实践总结
- 优先选择自定义Hook(React)/组合式API(Vue):逻辑封装灵活,可读性高,避免嵌套问题,是当前前端框架的推荐方案。
- 严格遵循单一职责:逻辑组件只处理业务逻辑,视图组件只负责布局渲染,降低耦合度。
- 集中维护逻辑:所有业务操作(创建、重命名、删除)统一在一处修改,彻底避免重复劳动。
内容的提问来源于stack exchange,提问作者E1dar
相关产品推荐
相关产品推荐

