一、先上示例代码:
按业务功能优先、文件类型其次原则来组织项目文件结构。
- src
- helpers
- components # 类型,没有数据状态、或完全内部数据状态封闭的组件,能独立、能复用
+ LineChart
+ BarChart
+ PieChart
- views # 业务,调用组件组装成一个个页面,不能复用,可给组件分发数据状态
+ Report
+ Document
+ Dashbord
- Resources
- index.jsx
- List
- index.jsx
- useController.jsx
- service.jsx
- Detail
- index.jsx
- useController.jsx
- service.jsx
// service.jsx
// Service 层
// + 负责声明数据请求,只是声明,并未执行
// + 处理数据
import api from '../../helpers/api';
export default class ResourceService {
constructor(){}
async getResource (requestData) {
const url = 'api/getResource';
const response = await api.get(url, requestData);
const responseData = this.handleGetResponseData(response);
return responseData;
}
handleGetResponseData(data) {
// handle data
return data;
}
}
// useController.jsx
// 第一个 Controller 区域
// + 要是负责数据连接
// + 声明数据发起,只是声明,并没发起请求
// + 处理 view 中交互逻辑,如显示、隐藏、Loading
// + 它在 ECMAScript 模块中,可以复用,算是一个变相的 service
import { useState, useEffect } from 'react';
import ResourceService from './service';
export default function useGetResource (id) {
const [resource, setResource] = useState(null);
useEffect(() => {
const fetchData = async () => {
let resourceService= new ResourceService();
let requestData = await resourceService.getResource(id);
setResource(requestData);
};
fetchData();
}, [id])
return resource;
}
// Controller + Template
import React from 'react';
import { useParams } from 'react-router-dom';
import useGetResource from './useController'
export default function Response() {
// 第二个 Controller 区域
// + 负责数据绑定、事件绑定
// + 负责发起数据请求
const { id } = useParams();
const { resource } = useGetResource(id);
// 视图层 View/Template
return (
<>
<section>{resource.name}</section>
<!-- 或,调用其它组件 -->
</>
);
}
二、抛问题:
1、有的一个需求,其实就是对一个资源进行流程上的管理,如 OA 中的事务,一条资源,贯穿不同的视图。
这时,公用的 service
应该怎样处理?
2、有的一个需求,比较复杂,需要在当前页面中组合多个视图,如 “航班”:出发/到达城市+日历(含节假日) → Search → 日历菜单(含特价机票) → 航班列表。各个之间如何拆、装?
三、代码分层:
其实,什么东西拆,拆解、分割到最后的,都是一个个单独的同性质、不同性质的资源,这样,就能隔离、复用;
页面,只是组装视图、映射数据、绑定事件。
React 中数据、业务逻辑、视图显示实现及各个之间映射,是需要开发者自己提炼的。React 就只有 JSX 、State(Props)、组件生命周期、渲染机制,React 只是一个库,它没有 template
,没有 controller
,,也没有 service
;也就没有 di
机制管理 provider
和 inject
。
开发者需要自己借助 ECMAScript 模块机制,约定 service
、controller
、template
等分层。
template
,分成两类,一类是用于以隔离、或以复用的组件,一类用来组装组件页面化;
service
,只负责数据获取和数据逻辑处理;
controller
,会存在两个 controller
,一个声明,一个发起,代码中已经详细叙述;
model
,按业务功能拆,每个业务需求,就需要一个对应的容器(一个数据结构)model
,用来描述业务,可以用来存放后端接口数据,也就是一个 stage
。
四、接口设计
RPC、RESTful、GraphQL 形式不强制;
只是最终数据结构需要强制一下;
用一个示例说一下:浏览器打开 https://api.github.com/repos/vuejs/vue
;
这个接口设计,除了有 vue
这个资源的数据信息外,还提供了 "owner"
对象、"organization"
对象,将相关性对象组合成一个完整的资源数据,内部不同性质的对象挂载在各自的属性中,而不是杂糅在一个扁平结构、或者嵌套在一个多层结构中。
后端接口设计,应该这样尽可能的分割成最小粒度,然后按资源、性质进行组合、包装。