前言
最近参与了一个项目,让我印象最深的就是它的 业务逻辑 和 状态管理 ,可以说,第一次见到的时候有些震惊。这个项目是 pnpm 的 workspace 形式,管理多个项目,共享一个 packages 包。
所有的 "js" 都放在这里,如 api、tools和hooks等。
hooks
最开始,我对hooks的解读就是拥有状态、生命周期的特殊函数。最标准的就是在 react 中的 useState ,后面就慢慢地理解成了管理某个状态的钩子,传入一个变量,然后从这个钩子中结构出 变量 和 设置变量。如:
ts
const {count,setCount} = useState(0)
基于这种见解,我们就可以封装一些hooks,如设置白天黑夜主题的 useTheme 的hooks等等,没错,我的想法还停留在 hooks 是公共的函数。
后面在vue3中,或者说 pinia 中,有用到了这种这种形式。在进行状态中,store仓库的命名,在习惯上确实是 use------开头。如有个 useInfo 的仓库。
ts
const {token , setToken} = useInfo('')
还是上面那句话,我的思想还局限于"公共"的概念,就是说业务逻辑是不用这种方式的。
状态管理
这个项目的状态管理,更准确的说法是状态与逻辑管理,就是借鉴 react 的hooks 和 pinia 进行的自定义管理。
如一个任务页面,获取任务列表。这里涉及到四个文件:
- 列表的接口: task/index.ts
- 状态管理的文件:useTask/useTaskStore.ts
- 处理逻辑的文件:useTask/index.ts
- 页面文件:index.vue
列表接口
每一个接口都是一个命名空间,一个文件。
ts
export namespace ReqTaskList {
export const config: REQ_CONFIG_ITEM = {
url: "task/list",
host: HOST_NAME.activity,
method: HTTP_METHOD.get,
};
export interface RequestData {
// TODO: 接口信息待补充
}
export interface itemVo {
current: string;
dyAdAwardLeft: number;
enumType: string;
icon: string;
link: string;
name: string;
rewardDesc: string;
status: number;
target: string;
taskId: number;
}
export interface ResponseData {
dailyTasks: itemVo[];
growthTasks: itemVo[];
weeklyTasks: itemVo[];
}
export async function request(data?: RequestData) {
return await commonRequest<RequestData, ResponseData>(config, data);
}
}
状态管理的文件
这里是定义了全局的任务数据仓库,使用 工厂模式 创建了一个可销毁的单例模式。
在这个文件管理页面所需要的响应式数据。
ts
import { ref } from "vue";
import { ReqTaskList } from "@lv/request";
interface ItemVo extends ReqTaskList.itemVo {
img?: string;
btnText?: string;
}
class TaskStore {
constructor() {}
dailyTasks = ref<ItemVo[]>([]);
growthTasks = ref<ItemVo[]>([]);
weeklyTasks = ref<ItemVo[]>([]);
}
let _taskStore: TaskStore | null = null;
function initTaskStore() {
_taskStore = new TaskStore();
}
function unInstallTaskStore() {
_taskStore = null;
}
function useTaskStore(): TaskStore {
if (!_taskStore) initTaskStore();
return _taskStore as TaskStore;
}
export { initTaskStore, unInstallTaskStore, useTaskStore };
export type { ItemVo };
处理逻辑的文件
所有的业务逻辑都在这个文件里进行,包括接口请求、数据处理等逻辑。
ts
import { onUnmounted, onMounted } from "vue";
import { ReqTaskList, ReqTaskTurntableActivity } from "@lv/request";
import { unInstallTaskStore, useTaskStore, ItemVo } from "./useTaskStore";
import { useUser } from "../useUser";
import { formatImg } from "@lv/tool";
export function useTask() {
const { userId } = useUser();
const taskStore = useTaskStore();
const { dailyTasks, growthTasks, weeklyTasks } = taskStore; // 导出响应式数据
const formatList = (list: ItemVo[]) => {
return list.map((item) => {
const obj = JSON.parse(JSON.stringify(item));
obj.img = formatImg(item.icon);
switch (parseInt(item.status.toString())) {
case 1:
obj.btnText = "领取";
break;
case 0:
obj.btnText = "去完成";
break;
case 2:
obj.btnText = "已领取";
break;
}
return obj;
});
};
// 获取列表数据
const getTaskList = async () => {
try {
const res = await ReqTaskList.request();
dailyTasks.value = formatList(res.data.dailyTasks);
growthTasks.value = formatList(res.data.growthTasks);
weeklyTasks.value = formatList(res.data.weeklyTasks);
} catch (error) {}
};
const getTurntableActivity = async (
params: ReqTaskTurntableActivity.RequestData
) => {
if (!userId.value) return;
try {
const res = await ReqTaskTurntableActivity.request(params);
return res;
} catch (error) {}
};
// 初始化
onMounted(() => {
getTaskList();
});
onUnmounted(() => {
unInstallTaskStore();
});
return {
taskStore,
getTaskList,
getTurntableActivity,
};
}
最后在页面上引入数据并使用就可以了。
vue
<script>
import { useTask } from "@lv/hooks";
const { taskStore, getTaskList } = useTask();
const {
dailyTasks: dailyTasksList,
growthTasks: growthTasksList,
weeklyTasks: weeklyTasksList,
} = taskStore;
</script>
看法
这种处理方法,我说不上来好还是不好,或者说合不合适。现在只是一两个变量和方法,在处理的时候还是比较简单,但仍然是需要在 逻辑管理 和 处理逻辑 这两个文件之间跳转,如果变量和方法多一些,那处理起来我会觉得更加费劲。大家如何看呢?