为什么 Vue3 封装 Table 组件丢失 expose 方法呢?

在实际开发中,我们通常会将某些常见组件进行二次封装,以便更好地实现特定的业务需求。然而,在封装 Table 组件时,遇到一个问题:Table 内部暴露的方法,在封装之后的组件获取不到。

代码展示为:

TypeScript 复制代码
const MyTable = defineComponent({
  name: 'MyTable',
  props: ElTable.props,
  emits: Object.keys(ElTable.emits || {}),
  setup(props, { slots, attrs, expose }) {
    const tableRef = ref<InstanceType<typeof ElTable>>();
    return () => (
      <div class='table-container'>
        <ElTable
          ref={tableRef}
          {...props}
          {...attrs}
          v-slots={slots}
        >
          {slots.default && slots.default()}
        </ElTable>
      </div>
    );
  },
});

在 Element Plus 的 ElTable 组件中,有很多暴露的方法,如下:

官方文档:Table 表格 | Element Plus

但使用上面封装的 MyTable 打印组件实例时,只有 table 本身的方法:

下面简单分析一下原因。

1. expose 未正确定义或调用

原因分析:

在 Vue 3 中,expose 用于暴露子组件的方法和属性,使父组件能够访问和操作它们。如果封装了 ElTable 组件,但是没有正确定义 expose 或者没有通过 ref 正确引用子组件实例,那么 expose 的方法无法生效。

如果在 setup 中使用 expose API 或者 expose 的位置不对,那么暴露的方法就无法通过 ref 访问到。🌰:

TypeScript 复制代码
expose({
  setCurrentRow: tableRef.value?.setCurrentRow, // 错误:此时 tableRef.value 还可能为 null
  clearSelection: tableRef.value?.clearSelection // 错误
});

解决方法:确保在 setup 中正确使用 expose 并且在暴露方法时,确保 tableRef 已经指向 ElTable 的实例。

2. ElTable 组件实例未传递给 tableRef

原因分析:

在封装代码中,ElTable 内部 expose 的方法(例如:setCurrentRow、clearSelection 等)可能会因为 ref 没有正确透传而丢失。Vue3 中,ref 的默认行为不能直接传递组件的 expose 方法到父组件中(本质)。

解决方法:手动暴露,确保 ElTable 组件通过 ref 绑定到 tableRef。

3. 组件生命周期中的调用顺序问题

原因分析:

expose 需要在 setup 函数中定义,而 tableRef 需要在组件挂载后才能被正确引用。如果在组件生命周期的某个不合适的时间调用 expose,比如,在 setup 函数之外或者组件渲染之前,可能导致tableRef 无法正确指向组件实例,从而无法暴露方法。

解决方法:使用 nextTick 来确保组件渲染完成后再执行某些操作。

综上,代码如下:

TypeScript 复制代码
import { defineComponent, ref } from 'vue';
import { ElTable } from 'element-plus';

export default defineComponent({
  name: 'MyTable',
  props: ElTable.props,
  emits: Object.keys(ElTable.emits || {}),
  setup(props, { attrs, slots, expose }) {
    const tableRef = ref<InstanceType<typeof ElTable>>();
    // 确保暴露方法时,tableRef 已经引用了正确的实例
    expose({
      setCurrentRow: (...args: Parameters<InstanceType<typeof ElTable>['setCurrentRow']>) =>
        tableRef.value?.setCurrentRow(...args),
      clearSelection: (...args: Parameters<InstanceType<typeof ElTable>['clearSelection']>) =>
        tableRef.value?.clearSelection(...args),
    });
    return () => (
      <div class="table-container">
        <ElTable ref={tableRef} {...props} {...attrs} v-slots={slots}>
          {slots.default && slots.default()}
        </ElTable>
      </div>
    );
  }
});

现在再看一下 组件实例,已经存在了需要的方法。

注意,在 table 内部存在很多暴露的方法,要想让我们封装的 MyTable 和 ElTable 可以通用,那么需要将方法全部暴露出来:

TypeScript 复制代码
export default defineComponent({
  name: 'MyTable',
  props: ElTable.props,
  emits: Object.keys(ElTable.emits || {}),
  setup(props, { attrs, slots, expose }) {
    const tableRef = ref<InstanceType<typeof ElTable>>();
    const ExposedMethods = [
      "clearSelection",
      "getSelectionRows",
      "toggleRowSelection",
      "toggleAllSelection",
      "toggleRowExpansion",
      // ...
    ];
    // 将方法透传
    const exposedMethods: Record<string, Function> = {};
    ExposedMethods.forEach(method => {
      exposedMethods[method] = (...args: any[]) => {
        return tableRef.value?.[method](...args);
      };
    });
    // 使用 expose 透传所有方法
    expose(exposedMethods);
    return () => (
      <div class="table-container">
        <ElTable ref={tableRef} {...props} {...attrs} v-slots={slots}>
          {slots.default && slots.default()}
        </ElTable>
      </div>
    );
  }
});

现在,所有的方法均存在,后续可以直接使用我们封装的 MyTable 组件。

4. 注意

有一个坑,不能将 expose 放在 onMounted 内部执行,为什么呢?

TypeScript 复制代码
onMounted(() => {
  const exposedMethods: Record<string, Function> = {};
  ExposedMethods.forEach(method => {
    exposedMethods[method] = (...args: any[]) => {
      return tableRef.value?.[method](...args);
    };
  });
  // 使用 expose 透传所有方法
  expose(exposedMethods);
});
  1. expose 在 setup 函数内部调用时,会立即暴露方法或属性给外部访问。 如果将 expose 放入 onMounted 中,setup 函数的执行已经结束,因此暴露的方法不会被 Vue 视为暴露的实例方法。

  2. onMounted 是一个生命周期钩子函数,它会在组件挂载后执行,但此时 Vue 的响应式系统和父组件的访问已经设定好了,因此暴露的方法不再能正确传递。

相关推荐
是立不是利24 分钟前
写给设计师的 CSS 博客美学指南
前端·css
统计学小王子34 分钟前
数学建模国赛倒计时4天——《统计模型精讲(混合效应模型R语言实现)》
开发语言·数学建模·r语言
知无不研38 分钟前
c语言中循环的介绍与简单应用
c语言·开发语言·算法·循环·for·while
郝学胜-神的一滴43 分钟前
Qt 高级编程 045:坐标体系深度实战
开发语言·c++·windows·python·qt·程序人生
dsyyyyy11011 小时前
JS复习,难点,易错点总结
开发语言·javascript·ecmascript
默_笙1 小时前
🏝 Docker 就是"房地产开发":从施工图纸到小区物业的容器化指南
前端·javascript
计算机魔术师1 小时前
Rohan Paul 谈用时间跨度衡量智能体能力,并引用 OpenAI 自动化研究实习生里程碑
前端
kyriewen1 小时前
我手写了个极简版 React Router——才搞懂 v6 为什么砍了这么多 API
前端·javascript·程序员
a1117761 小时前
Shirone 二次元博客主题 开源
前端·开源
IT_陈寒1 小时前
Python的切片赋值把我坑惨了,这不是bug是特性
前端·人工智能·后端