博客 / 詳情

返回

前端工程化 - 良好的feature-based-目錄結構與具體示例

良好的feature-based-目錄結構與具體示例

背景

先拆”業務邊界”,不是拆組件

從業務角度來説,這個訂單頁其實有3個部分:

  1. 核心 - 瀏覽能力
    • 訂單列表
    • 基礎篩選
    • 分頁
  2. Extension - 可選 - 插件能力
    • 高級篩選
    • 導出
    • 狀態變更
  3. Detail - 按需能力
    • 訂單詳情單牀

重構目錄結構

把 “ 按技術類型” 改為 “按業務角色”

如果一開始就能拆成這樣,其實80%的問題就已經解決了.

src/
├── features/
│   ├── user/
│   │   ├── pages/
│   │   │   ├── UserList.vue
│   │   │   └── UserDetail.vue
│   │   │
│   │   ├── components/
│   │   │   ├── UserTable.vue
│   │   │   └── UserForm.vue
│   │   │
│   │   ├── api/
│   │   │   └── user.api.ts
│   │   │
│   │   ├── store/
│   │   │   └── user.store.ts
│   │   │
│   │   ├── hooks/
│   │   │   └── useUser.ts
│   │   │
│   │   ├── types/
│   │   │   └── user.types.ts
│   │   │
│   │   └── index.ts
│   │
│   ├── order/
│   │   ├── pages/
│   │   ├── components/
│   │   ├── api/
│   │   ├── store/
│   │   └── index.ts
│   │
│   └── product/
│       └── ...
│
├── shared/
│   ├── components/
│   │   ├── BaseTable.vue
│   │   ├── BaseModal.vue
│   │   └── BaseButton.vue
│   │
│   ├── hooks/
│   │   └── useRequest.ts
│   │
│   ├── utils/
│   └── styles/
│
├── router/
│   ├── routes/
│   │   ├── user.routes.ts
│   │   ├── order.routes.ts
│   │   └── product.routes.ts
│   │
│   └── index.ts
│
├── app.vue
└── main.ts

讓 Page 變成 “純裝配層”

// OrderPage.vue(正確版本)

<template>
  <div class="order-page">
    <!-- Core:必須同步 -->
    <BaseFilter />
    <OrderList @select="openDetail" />

    <!-- Extension:按需 -->
    <Suspense>
      <AdvancedFilter v-if="showAdvanced" />
    </Suspense>

    <Suspense>
      <ExportPanel v-if="canExport" />
    </Suspense>

    <Suspense>
      <StatusAction v-if="canChangeStatus" />
    </Suspense>

    <!-- Detail:用户觸發 -->
    <Suspense>
      <OrderDetailDialog
        v-if="showDetail"
        :order-id="currentOrderId"
        @close="closeDetail"
      />
    </Suspense>
  </div>
</template>
<script setup lang="ts">
import { defineAsyncComponent } from 'vue'

// core 同步加載
import BaseFilter from '../core/BaseFilter.vue'
import OrderList from '../core/OrderList.vue'

// extension 異步加載
const AdvancedFilter = defineAsyncComponent(
  () => import('../extensions/advanced-filter/AdvancedFilter.vue')
)
const ExportPanel = defineAsyncComponent(
  () => import('../extensions/export/ExportPanel.vue')
)
const StatusAction = defineAsyncComponent(
  () => import('../extensions/status/StatusAction.vue')
)

// detail 異步
const OrderDetailDialog = defineAsyncComponent(
  () => import('../detail/OrderDetailDialog.vue')
)

import { useOrderCore } from '../core/useOrderCore'
import { useOrderDetail } from '../detail/useOrderDetail'

const {
  showAdvanced,
  canExport,
  canChangeStatus
} = useOrderCore()

const {
  showDetail,
  currentOrderId,
  openDetail,
  closeDetail
} = useOrderDetail()
</script>
  • Page 裏沒有業務規則
  • 沒有 if 權限判斷
  • 只負責 “拼裝能力”

把 規則 收斂到 對應邊界

// useOrderCore.ts(核心邏輯)
export function useOrderCore() {
  const showAdvanced = ref(false)

  const canExport = computed(() => {
    return permission.value.includes('order:export')
  })

  const canChangeStatus = computed(() => {
    return permission.value.includes('order:status')
  })

  return {
    showAdvanced,
    canExport,
    canChangeStatus
  }
}

規則集中,邊界生效

Extension 是“插件”,不污染 Core

不需要知道 OrderList的存在

// extensions/export/useExport.ts
export function useExport() {
  const exportOrders = async () => {
    // 導出邏輯
  }

  return { exportOrders }
}

// ExportPanel.vue

<template>
  <button @click="exportOrders">導出</button>
</template>

<script setup>
import { useExport } from './useExport'
const { exportOrders } = useExport()
</script>

Detail 完全獨立

Detail 不依賴 Core,Core 也不依賴 Detail

// detail/useOrderDetail.ts
export function useOrderDetail() {
  const showDetail = ref(false)
  const currentOrderId = ref<string | null>(null)

  const openDetail = (id: string) => {
    currentOrderId.value = id
    showDetail.value = true
  }

  const closeDetail = () => {
    showDetail.value = false
  }

  return {
    showDetail,
    currentOrderId,
    openDetail,
    closeDetail
  }
}

正確的拆分,是先讓業務“各司其職”,
lazy 只是順手的事。

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.