<script setup> 实战模式:大型组件怎么拆?

用 Vue3 <script setup> 写小组件很爽,但遇到大型页面时,逻辑堆在一起就像面条。今天我们从"订单详情页"这个复杂组件入手,讲清楚 <script setup> 的最佳实践:逻辑分层、组合式函数拆分、Prop/Emit 类型定义、与普通 <script> 共存,让你写大组件也井井有条。

🎯 订单详情页需求

  • 渲染订单基础信息、商品列表、物流时间线。
  • 支持操作:修改状态、退款、打印。
  • 需要权限控制 + 国际化。

🧱 目录规划

复制代码
OrderDetail.vue
composables/
  useOrderInfo.ts
  useOrderActions.ts
  useOrderPermission.ts
  • 把逻辑拆到同文件夹下的组合式函数。
  • 组件负责布局 + 调用组合函数。

✍️ OrderDetail.vue 结构

vue 复制代码
<script setup lang="ts">
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
import OrderBase from './components/OrderBase.vue';
import OrderItems from './components/OrderItems.vue';
import OrderTimeline from './components/OrderTimeline.vue';
import { useOrderInfo } from './composables/useOrderInfo';
import { useOrderActions } from './composables/useOrderActions';
import { useOrderPermission } from './composables/useOrderPermission';

const { t } = useI18n();
const route = useRoute();
const orderId = computed(() => route.params.id as string);

const { loading, order, reload } = useOrderInfo(orderId);
const { canEdit, canRefund } = useOrderPermission(order);
const { updateStatus, refund, print } = useOrderActions(order, reload);
</script>
  • <script setup> 直接写逻辑,无需 defineComponent
  • 组合式函数返回的数据直接解构。
  • 所有业务逻辑都在 composable 中实现。

🧩 组合式函数示例

ts 复制代码
// useOrderInfo.ts
import { computed, ref, watch } from 'vue';
import { getOrderDetail } from '@/api/order';

export function useOrderInfo(orderId: Ref<string>) {
  const loading = ref(false);
  const order = ref<Order | null>(null);

  const fetchOrder = async () => {
    if (!orderId.value) return;
    loading.value = true;
    try {
      order.value = await getOrderDetail(orderId.value);
    } finally {
      loading.value = false;
    }
  };

  watch(orderId, fetchOrder, { immediate: true });

  const totalAmount = computed(() =>
    order.value?.items.reduce((sum, item) => sum + item.price * item.qty, 0) || 0
  );

  return { loading, order, totalAmount, reload: fetchOrder };
}
  • 组合式函数以 use 开头。
  • 暴露响应式数据 + 方法。
  • 方便在其他组件复用。

🧠 Props、Emits 类型定义

vue 复制代码
<script setup lang="ts">
interface Props {
  orderId: string;
}

const props = defineProps<Props>();
const emit = defineEmits<{
  (e: 'refresh'): void;
}>();

const handleSuccess = () => emit('refresh');
</script>
  • definePropsdefineEmits<script setup> 中直接调用。
  • TypeScript 自动推断模板里的类型。

🔁 与普通 <script> 共存

vue 复制代码
<script lang="ts">
export default {
  inheritAttrs: false,
};
</script>

<script setup lang="ts">
// ...这里写 setup 逻辑
</script>
  • 当需要 inheritAttrscomponentsdirective 等选项时,可以保留普通 <script>
  • <script setup> 会在选项式 API 之后执行。

🧯 调试技巧

  • defineExpose:暴露内部方法给父组件。

    ts 复制代码
    defineExpose({ reload });
  • onMounted, onUnmounted 与原生生命周期一致。

  • 使用 ESLint 插件 eslint-plugin-vue 保持编码规范。

⚠️ 常见坑

现象 解决
<script setup> 中使用 this undefined 不存在 this,使用组合式变量
逻辑堆积混乱 文件过大 拆到 composables 子目录
defineProps 重复执行 在顶层直接使用 只能在 <script setup> 顶层调用
类型推断失败 未开启 Volar + TS 支持 确保 vue-tsc 生效

🏁 小练习

  1. 把你项目中 200 行以上的组件拆分成 <script setup> + composables。
  2. 尝试写一个 useTable 组合式函数,封装分页、排序逻辑。
相关推荐
嘟嘟071731 分钟前
顺时针螺旋填充 n×n 矩阵:手撕 generateMatrix 的四个 for 循环
javascript·算法·面试
CarIise37 分钟前
C语言基础课程笔记:硬件原理、数据类型、结构体与格式化输入输出详解
c语言·开发语言·笔记
weixin_BYSJ198739 分钟前
springboot技能与工具共享小程序---附源码29657
java·javascript·spring boot·python·小程序·django·php
leoZ23141 分钟前
Vue3 还原一个企业级后台-14-项目总结
开发语言·人工智能·后端·opencv·计算机视觉·数据挖掘·rust
张元清1 小时前
React useSessionStorage Hook:刷新不丢、只属于当前标签页的状态 (2026)
前端·javascript·react.js
weixin_BYSJ19871 小时前
flask民族服饰饰品商城小程序---附源码37399
java·javascript·spring boot·python·小程序·django·php
xiaominlaopodaren1 小时前
three.js最小地图运行时(一):视图状态
javascript·gis·three.js
zlinear数据采集卡1 小时前
数据采集卡从入门到精通(7):流水线型ADC——级级接力,高速与高精的平衡术
开发语言·arm开发·嵌入式硬件·fpga开发·c#
Orange_sparkle1 小时前
从五个 TypeScript 文件看懂 Coding Agent:一次 nano-pi 学习复盘
javascript·学习·typescript
Dr.kangder1 小时前
嵌入式面试总结(二十一)——C语言关键字
c语言·开发语言·面试·职场和发展·架构·虚拟化