前端工程化 - 良好的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 只是顺手的事。

posted @ 2026-02-11 14:09  MT-Jaxon  阅读(23)  评论(0)    收藏  举报