嘿,大家好!👋 花了一段时间构建React应用后,我发现随着应用变大,保持一个整洁的架构变得至关重要。今天,我来分享一下我们在React中使用MVVM(M-V-VM)模式的经验,它帮我们团队省去了无数麻烦,让代码库更易于管理。
這兒有您需要知道的好處!来啦!🎯
1. 你的代码变得超级有组织
— 数据、逻辑和UI组件分离明确
— 代码的每一部分都有明确的任务并能胜任
— 再不用纠结“这个逻辑该放哪里”了
2. 测试变得简单
— 业务逻辑被隔离在ViewModel中
— UI组件完全是展示性的
— 可以独立测试每个部分,无需模拟整个系统
3. 超强化的复用性
— ViewModels 可在不同组件间重复使用
— 逻辑保持一致
— 减少复制粘贴,保持单一事实来源
4. 合乎逻辑的状态管理
— 应用程序中清晰的数据流
— 可预测的状态更新
— 当出现问题时更容易调试(问题总是会出现的,不是吗?)
让我们构建一个简单的电子商务产品列表页面,它带有过滤器和排序功能。这里是如何用MVVM(模型-视图-视图模型)这样构建。
目录结构 src/
├── pages/
│ └── ProductsPage/
│ ├── index.tsx # 主页面组件
│ ├── index.hook.ts # 页面钩子
│ ├── index.store.ts # 状态管理逻辑
│ ├── ViewModel.ts # ViewModel逻辑
│ ├── types.ts # TypeScript类型定义
│ ├── components/
│ │ ├── ProductGrid/
│ │ ├── FilterPanel/
│ │ └── SortingOptions/
│ └── styles/
第一步:定义你的模型
// models/Product.model.ts
export interface Product {
id: string; // 产品ID
name: string; // 产品名称
price: number; // 产品价格
category: string; // 产品分类
inStock: boolean; // 是否有库存
}
// 产品接口定义了产品的ID, 名称, 价格, 分类和是否有库存信息。
export interface FilterOptions {
category: string[]; // 分类选项
minPrice: number; // 最小价格
maxPrice: number; // 最大价格
inStock: boolean; // 是否有库存
}
// 筛选项接口定义了分类选项, 最小价格, 最大价格和是否有库存信息。
2. 设置您的店铺
// pages/产品页面/index.store.ts
import { create } from ‘zustand’;
interface 产品页面状态接口 {
产品: 产品[];
筛选条件: 过滤选项;
更新产品: (产品: 产品[]) => void;
更新筛选条件: (筛选条件: 过滤选项) => void;
}
const 使用产品仓库 = create<产品页面状态接口>((更新) => ({
产品: [],
筛选条件: {
类别: [],
minPrice: 0,
maxPrice: 1000,
库存状态: false
},
更新产品: (产品) => 更新({ 产品 }),
更新筛选条件: (筛选条件) => 更新({ 筛选条件 })
}));
3, 创建您的视图模型
// pages/ProductsPage/ViewModel.ts
class ProductsViewModel {
private store: ProductsStore;
private uiStore: UIStore;
constructor(store: ProductsStore, uiStore: UIStore) {
this.store = store;
this.uiStore = uiStore;
}
public async 获取产品() {
try {
this.uiStore.showLoader();
const { data } = await ProductsAPI.getProducts(this.store.filters);
this.store.setProducts(data);
} catch (error) {
toast.error('获取产品时出错');
} finally {
this.uiStore.hideLoader();
}
}
public updateFilters(filters: Partial<FilterOptions>) {
this.store.setFilters({
...this.store.filters,
...filters
});
}
public shouldShowEmptyState(): boolean {
return !this.uiStore.isLoading && this.getFilteredProducts().length === 0;
}
public shouldShowError(): boolean {
return !!this.uiStore.error;
}
public shouldShowLoading(): boolean {
return this.uiStore.isLoading;
}
public shouldShowProductDetails(): boolean {
return !!this.uiStore.selectedProductId;
}
}
4. 自定义挂钩(Custom Hook)
// pages/产品页面/index.hook.ts
const useProductsPage = () => {
const productsStore = useProductsStore();
const uiStore = useUIStore();
const viewModel = new ProductsViewModel(productsStore, uiStore);
// isRefreshing 和 refreshDone 用于处理页面特有的情况,且位于视图模型之外
return {
viewModel,
isRefreshing: uiStore.isRefreshing,
refreshDone: () => uiStore.setRefreshing(false),
};
};
5. 视图组件插件
// pages/ProductsPage/index.tsx
const ProductsPage: FC = () => {
const { viewModel, isRefreshing, refreshDone } = useProductsPage();
useEffect(() => {
viewModel.fetchProducts();
}, [viewModel]);
useEffect(() => {
if (isRefreshing) {
viewModel.fetchProducts();
refreshDone();
}
}, [isRefreshing]);
return (
<div className="products-page">
<FilterPanel />
<ProductGrid />
<SortingOptions />
</div>
);
};
我不容易学到的一些最佳实践 😄
1. 让视图模型保持专注
// 好
class ProductsViewModel {
fetchProducts() { /* … */ }
updateFilters() { /* … */ }
sortProducts() { /* … */ }
}
// 例如,坏 — 职责混淆
class ProductsViewModel {
fetchProducts() { /* … */ }
updateUserProfile() { /* … */ }
handleCheckout() { /* … */ }
}
2. 正确进行清理
// 使用 useEffect 来监听 viewModel 的变化。当 viewModel 发生变化时,会创建一个新的 AbortController 实例,并使用该实例的信号来调用 viewModel 的 fetchProducts 方法获取产品数据。在组件卸载时,会调用 controller 的 abort 方法来取消正在进行的请求。
useEffect(() => {
const controller = new AbortController();
viewModel.fetchProducts(controller.signal);
return () => controller.abort();
}, [viewModel]);
3. 不要缓存ViewModels
// 好
const viewModel = new ProductsViewModel(store);
// 不好 — 这会破坏响应性
const viewModel = useMemo(() => new ProductsViewModel(store), [store]);
不太完美的地方(说实话)😕
1. 更多的样板文件
— 需要编写更多的初始代码
— 需要处理更多的文件
— 新成员的学习曲线更陡峭
2. 可能有些过分
— 对于简单的 CRUD 应用来说,这可能有些过分
— 小项目可能无法充分展现其优势
— 正确设置需要一定的时间
3. 团队一致同意是必要的
— 每个人都需要理解和遵守这个模式
— 需要一致的规范
— 文档变得非常重要
React中的MVVM模式并不是万能药,但它真的极大地提升了我们团队的效率和代码质量。从一个小功能开始试试,看看感觉怎么样。记住,目标是让代码更易维护,让生活更轻松!
大家可以在评论区留言提问哦。祝愉快编程!🚀
优化项请继续阅读更多内容,了解我们如何解决视图模型实例的重复问题,并让垃圾回收器保持愉快。
在这里找到实现MVVM架构的代码库here,不过建议你先读一下系列中的下一篇再动手。
通俗易懂 🚀感谢你加入我们__In Plain English_社区!在你离开前:
- 记得为作者鼓掌👏️并关注她/他
- 关注我们: X | LinkedIn | YouTube | Discord | 电子报 | 节目
- 在Differ上免费创建一个AI驱动博客。
- 更多内容请看PlainEnglish.io
共同学习,写下你的评论
暂无评论
作者其他优质文章