前言
純菜雞記錄
業(yè)務(wù)場景
[圖片上傳失敗...(image-ed53dd-1658375271104)]
- 拆分思路:
- 根據(jù)業(yè)務(wù),可以分成 nav(面包屑+樹狀導(dǎo)航)势就、page(內(nèi)容展示);根據(jù)內(nèi)容展示的不同,還可以繼續(xù)拆分
- 根據(jù)項目結(jié)構(gòu)问裕,可以分為xxx.vue(頁面)、data_xx.ts(接口)孵坚、use_xx.ts(邏輯)粮宛,還有其他文件,如常量管理卖宠、路由管理巍杈、鑒權(quán)管理等
- 拆分方向:
拆分足夠細(xì),盡量使用函數(shù)式編程(有很多方向扛伍,只是選擇了其中的一種)
- 以拆分nav(面包屑+樹狀導(dǎo)航)為例
文件結(jié)構(gòu):
[圖片上傳失敗...(image-670478-1658375271104)]
/*
* bread-crumb.vue
* 面包屑頁面:包含頁面需要的數(shù)據(jù)以及跳轉(zhuǎn)功能
* data_nav.ts
* nav(面包屑+樹狀導(dǎo)航)相關(guān)接口文件及簡單數(shù)據(jù)處理
* index.vue
* nav(面包屑+樹狀導(dǎo)航)入口頁面(布局筷畦、提供各個子頁面所需的數(shù)據(jù)
* interface.ts
* typescript相關(guān)接口定義等
* tree.vue和tree_menu.vue
* 樹狀導(dǎo)航:包含頁面需要的數(shù)據(jù)以及跳轉(zhuǎn)功能
* use_base.ts
* 組裝全局所需的一些字段,如 id 等
* use_nav.ts
* 組裝nav(面包屑+樹狀導(dǎo)航)所需的數(shù)據(jù)
* use_router.ts
* 跳轉(zhuǎn)功能封裝
*/
- 不同業(yè)務(wù)模塊之間的數(shù)據(jù)交互(在交互設(shè)計上刺洒,需要盡可能做到功能分離化)
在nav(面包屑+樹狀導(dǎo)航)模塊定義:
/**
* use_base.ts
* 基本信息鉤子:暴露了基本數(shù)據(jù)base鳖宾,并且provide了可供全局使用的數(shù)據(jù)和方法
*/
import { provide, computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
import { BASE, BASE_ID_OPEN } from './constant'
function fmtId(v?: string) {
return v || ''
}
export function useBase() {
const router = useRouter()
const route = useRoute()
const base = computed(() => {
return {
id: fmtId(route.query.id as string),
parentId: fmtId(route.query.parentId as string)
}
})
provide(BASE, base)
provide(BASE_ID_OPEN, (id: string) => {
router.push({ query: { id } })
})
return {
base
}
}
在內(nèi)容展示模塊使用:
// use_main.ts
import { computed, ComputedRef, inject, watch } from 'vue'
import { BASE, CHAIN, BASE_ID_OPEN } from './constant'
import { TribeData } from './interface'
export function useMain() {
const base = inject<ComputedRef<{ id: string, parentId: string }>>(BASE, computed(() => ({
id: '',
parentId: ''
})))
const openId = inject<(id: string) => void>(BASE_ID_OPEN)
const updateChain = (name: string) => {
const lastChainItem = chain.value[chain.value.length - 1]
lastChainItem.label = name
}
return {
base,
openId,
chain,
updateChain
}
}
這樣,在內(nèi)容展示模塊就可以使用這些基本信息和方法了逆航。
補充:
最近遇到了這樣一個場景(如下圖)
[圖片上傳失敗...(image-4f9ae9-1658375271104)]
頁面上的表格和彈窗中的表格其實會調(diào)同一個接口鼎文,由于頁面上可操作這個表格,進行新增因俐、編輯和刪除等拇惋,而彈窗中的表格是全量數(shù)據(jù)(先不談?wù)故緝?yōu)化)
因此,為保證彈窗中的表格數(shù)據(jù)要與頁面的保持一致抹剩,需要每彈出一次彈窗就要調(diào)一下全量數(shù)據(jù)的接口撑帖。
但是根據(jù)需求分析,有一些狀態(tài)下澳眷,頁面表格是不可操作狀態(tài)的胡嘿,也就意味著,彈窗數(shù)據(jù)不會發(fā)生變化钳踊,此時衷敌,就可以將請求返回的數(shù)據(jù)緩存起來,不必每次點擊查看就發(fā)起請求了箍土。
// use_cache.ts
import { computed, ref, watch, reactive } from 'vue'
import { getXXX } from './data_XXX'
import { showError } from '@/XXX'
import type { IXXX } from './interface'
// 頁面數(shù)據(jù)緩存池
export const poolList: Record<number, IXXX[]> = {}
// 用于顯示更多信息
export function useCache() {
// 是否發(fā)請求
const noCache = ref<boolean>(true)
// 區(qū)分?jǐn)?shù)據(jù)緩存池中不同數(shù)據(jù)的標(biāo)識
const cacheId = ref<number | undefined>()
const cache = reactive<Array<ISpecFunc>>([])
async function echo() {
const id = cacheId.value
if (id === undefined) { return }
if (noCache.value && !(poolList[id])) {
try {
const ret = await getXXX({
id,
current: -1
})
poolList[id] = ret.list
Object.assign(cache, ret.list)
} catch (err) {
showError(err)
}
}
}
return {
noCache,
cacheId,
cache,
echo
}
}
// 彈窗
<script lang="ts" setup>
import { watch, computed } from 'vue'
import { useCache } from './use_cache'
const { noCache, cacheId, cache, echo } = useSpecCache()
const props = defineProps<{
noCache: boolean
specId: number | undefined
visible: boolean
}>()
watch(
() => props.visible,
(val) => {
if (val) {
noCache.value = props.noCache
cacheId.value = props.specId as number
echo()
}
}
)
const isVisible = computed(() => props.visible)
const emit = defineEmits<{
(event: 'close'): void,
}>()
const onclose = () => {
emit('close')
}
</script>
<template>
<el-dialog
v-model="isVisible"
width="860px"
@close="onclose"
>
...
</el-dialog>
</template>
如果后臺配合的話逢享,還可以做的更多,比如后臺返回一個數(shù)據(jù)id吴藻,記錄這份數(shù)據(jù)的更新瞒爬,每一次更新都會改變這個字段,這樣就可以將這個字段與cacheId組合起來區(qū)分?jǐn)?shù)據(jù)緩存池中的不同數(shù)據(jù)