为什么 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 的响应式系统和父组件的访问已经设定好了,因此暴露的方法不再能正确传递。

相关推荐
JAMES费7 分钟前
python机器人Agent编程——多Agent框架的底层逻辑(上)
开发语言·python·机器人
赵锦川13 分钟前
微信小程序设置屏幕安全距离
前端·javascript·vue.js
JovaZou23 分钟前
[Python学习日记-67] 封装
开发语言·python·学习
hlsd#23 分钟前
go 集成swagger 在线接口文档
开发语言·后端·golang
Java Fans29 分钟前
深入探索R语言在机器学习中的应用与实践
开发语言·机器学习·r语言
草原上唱山歌35 分钟前
C++需要学习哪些内容?
开发语言·c++·学习
lly20240640 分钟前
SQLite Where 子句
开发语言
GISer_Jing44 分钟前
Javascript——设计模式(一)
前端·javascript·设计模式
yqcoder1 小时前
react 中 useCallback Hook 作用
前端·javascript·react.js
Clrove.111 小时前
C++初阶——queue
开发语言·c++