• 博客
  • 项目
  • 碎碎念
  • 留言板
  • 关于我
怎么在 Quasar 里“偷懒”
更新时间:2025/12/08 分类:编程技术
有些关于 Quasar 组件封装的想法,借此机会整理一下。写代码不只是为了实现功能,更多的是为了在这个过程中沉淀出一种通用的解决方案。


如何看待组件封装


不要为了封装而封装。很多人在做中后台系统时,习惯性地把 QTable 包一层,结果包成了“四不像”。 学会做加法,而不是减法。 很多新手封装组件时,喜欢把原有的属性屏蔽掉,只暴露自己认为有用的几个参数。这是不对的。优秀的封装应该是“透明”的,利用 $attrs 和 $slots 进行透传。使用者在使用你的组件时,应该感觉不到“中间层”的存在,Quasar 原有的文档对他依然有效。如果你的封装导致队友必须去读你的源码才知道怎么传参,那这就是一种失败的封装。

透传与插槽转发
<template>
<div class="com-table-wrapper">
<q-table
ref="tableRef"
v-bind="$attrs"
:rows="dataList"
:loading="loading"
@request="onRequest"
>
<template v-for="(_, name) in $slots" #[name]="scope">
<slot :name="name" v-bind="scope || {}" />
</template>

</q-table>
</div>
</template>

逻辑与视图的分离
学会抽离。在 Vue 3 的 Composition API 下,  不要把 API 请求、分页计算、Loading 状态这些业务逻辑死死地写在 .vue 文件的模板里。 UI 只是躯壳,Hook 才是灵魂。 尝试把数据获取和状态管理的逻辑剥离出来,写成一个纯粹的函数。这样组件就只负责“渲染”,而 Hook 负责“思考”。这不仅是为了复用,更是为了让代码结构清晰,当你回头维护时,不需要在一堆 HTML 标签里找那个控制分页的变量到底在哪。

useTable 的设计
// useTable Hook 核心逻辑伪代码
// 接收一个获取列表的 API 函数,返回表格所需的一切状态
export function useTable < T > (apiFn: (params: any) => Promise < ApiResponse < T >> ) {
  // 状态定义:将分散的变量内聚在一起
  const loading = ref(false);
  const dataList = ref < T[] > ([]);
  const pagination = ref({
    page: 1,
    rowsPerPage: 10,
    rowsNumber: 0
  });

  // 核心请求逻辑:统一处理 Loading、参数组装、分页回填
  const onRequest = async (props: any) => {
    const {
      page,
      rowsPerPage
    } = props.pagination;
    loading.value = true;

    try {
      // 自动组装分页参数与查询参数
      const res = await apiFn({
        page,
        size: rowsPerPage,
        ...queryParams
      });

      // 自动回填数据与总条数
      dataList.value = res.list;
      pagination.value.rowsNumber = res.total;

      // 更新分页器状态
      pagination.value.page = page;
      pagination.value.rowsPerPage = rowsPerPage;
    } finally {
      loading.value = false;
    }
  };

  // 暴露出去给组件使用
  return {
    loading,
    dataList,
    pagination,
    onRequest
  };
}

培养配置化思维
学会“偷懒”。中后台系统中有大量的搜索栏和表单,这些东西本质上都是重复的。 为了避免不停地复制粘贴 <q-input> 和 <q-select>。利用 JSON Schema 来生成视图

配置驱动视图
// 1. 定义配置 (Schema)
// 这里的配置决定了视图长什么样,修改这里比修改 HTML 更安全
const searchSchema = [
{ field: 'keyword', label: '搜索名称', type: 'input' },
{ field: 'status', label: '状态', type: 'select', options: [...] },
{ field: 'date', label: '创建时间', type: 'date-picker' }
];

// 2. 视图渲染 (Template)
// ComSearch 组件内部伪代码
<template>
<div class="row q-col-gutter-md">
<div v-for="item in searchSchema" :key="item.field">
<component
:is="getComponentType(item.type)"
v-model="formModel[item.field]"
:label="item.label"
v-bind="item.props"
/>
</div>
</div>
</template>

对类型的敬畏
在封装组件 Props 时,尽量继承 Quasar 原有的类型定义(如 QTableProps)

文档地址
Quasar Quasar封装
联系我