You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vue3中Provide/Inject与this.$slots.default的响应性差异及解决方案

问题解答

一、this.$slots.default访问vnode与存入数组的核心差异

  1. $slots是实时响应的动态集合
    Vue的$slots属于组件渲染上下文的一部分,每次组件触发重新渲染时,$slots.default都会返回当前最新的vnode列表——它内部绑定了组件的依赖追踪机制,当slot内容(比如子组件的props)变化时,$slots会自动更新,确保拿到的是最新状态的vnode。

  2. 存入数组的vnode是静态快照
    当你把this.$.vnode或从slot中拿到的vnode存入数组,本质是保存了某一时刻的vnode引用:

  • vnode本身不是响应式对象,即使数组用reactive包裹,也只能追踪数组的增删改操作,无法感知vnode内部props的变化;
  • this.$.vnode是组件内部的私有实例,它的更新由Vue渲染引擎管控,手动存储后脱离了原有的响应链路,后续props变更不会同步到数组中的vnode副本。

二、通过Provide/Inject确保vnode变化被正确响应的解决方案

方案1:传递响应式属性而非直接传递vnode

放弃传递整个vnode,转而把Column的核心配置(如字段名、表头、可见性等)用响应式对象包装后传递给DataTable,让DataTable直接监听这些属性的变化:

<!-- Column组件 -->
<script setup>
import { defineProps, inject, toRefs } from 'vue';

const props = defineProps(['field', 'header', 'visible']);
const registerColumn = inject('registerColumn');

// 传递响应式的props引用
registerColumn(toRefs(props));
</script>
<!-- DataTable组件 -->
<script setup>
import { reactive, provide } from 'vue';

const columns = reactive([]);

const registerColumn = (columnProps) => {
  columns.push(columnProps);
};

provide('registerColumn', registerColumn);

// 后续基于columns中的响应式props渲染表格,属性变更会自动触发更新
</script>

方案2:Column属性变更时主动通知DataTable

在Column组件中监听自身props的变化,当属性更新时调用DataTable提供的方法主动同步:

<!-- Column组件 -->
<script setup>
import { defineProps, inject, watch, onMounted } from 'vue';

const props = defineProps(['field', 'header']);
const registerColumn = inject('registerColumn');
const updateColumn = inject('updateColumn');
const columnKey = Symbol();

onMounted(() => {
  // 注册时传递唯一标识和初始props
  registerColumn(columnKey, { ...props });
});

// 深度监听props变化,主动通知更新
watch(props, (newProps) => {
  updateColumn(columnKey, { ...newProps });
}, { deep: true });
</script>
<!-- DataTable组件 -->
<script setup>
import { reactive, provide } from 'vue';

const columnsMap = reactive(new Map());

const registerColumn = (key, props) => {
  columnsMap.set(key, props);
};

const updateColumn = (key, newProps) => {
  columnsMap.set(key, newProps);
};

provide('registerColumn', registerColumn);
provide('updateColumn', updateColumn);

// 渲染时从columnsMap中获取最新配置
</script>

方案3:结合slot的响应性与Provide/Inject标记

如果必须基于vnode处理,可以保留$slots的响应性,同时用Provide/Inject给Column打标识,让DataTable在每次渲染时从slot中筛选出目标vnode:

<!-- Column组件 -->
<script setup>
import { provide } from 'vue';

// 提供标识,标记这是一个DataTable Column
provide('isDataTableColumn', true);
</script>
<!-- DataTable组件 -->
<script setup>
import { computed } from 'vue';

// 每次渲染都从slot中筛选出标记过的Column vnode
const tableColumns = computed(() => {
  return (this.$slots.default?.() || []).filter(vnode => {
    // 从vnode的组件实例中获取注入的标识
    return vnode.component?.provides.isDataTableColumn;
  });
});
</script>

这种方式既保留了slot的自动响应性,又支持Column被自定义组件包裹的场景。


内容的提问来源于stack exchange,提问作者Antonio Dell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 19:40:41