常见问题
树组件为什么没有显示数据?
按顺序检查:
data是否为数组,而不是单个对象- 数据字段名是否为默认的
id、label、children;如果不是,是否通过tree-props配置了字段映射 - 组件外层容器是否有可用高度;组件默认
height: 100%,父容器高度为 0 时不会显示
每个节点的 key 必须在整棵树中全局唯一。默认使用节点的 id 作为 key;配置了 tree-props.id 后,则使用映射后的字段。key 重复会导致展开、选择和列表渲染互相覆盖。
修改 data 后,页面为什么没有更新?
为了避免万级节点场景下深度监听整棵树,组件只监听 data 根数组的引用变化。只执行 push、splice 或直接修改节点字段,不会触发组件重新解析;修改完成后还需要替换根数组:
treeData.value[0].children.push(newNode);
treeData.value = [...treeData.value];节点选中状态在数据重新解析时按 key 匹配。这里可以简单分成两种用法:
- 绑定了
v-model(受控值):数据刷新后,以当前v-model中的 key 为准重新计算选中状态。 - 没有绑定
v-model(非受控状态):default-checked-keys只提供初始值,之后由组件内部记录用户的选择;数据刷新后,相同 key 的节点会保留原来的选中状态。
key 表示节点的唯一身份,不是节点的显示文字。例如节点的 id 仍为 user-1,只把 name、label 或其他展示内容改了,组件仍会认为它是同一个节点,选中状态不会改变;如果 key 也变了,就会被当作一个新节点,不会继承原节点的选中状态。
受控和非受控是什么意思?
“受控”就是绑定了 v-model,选中值由外部变量提供;“非受控”就是没有绑定 v-model,运行时选中状态由组件自己保存。用户点击时,组件会先更新界面,再通知外部;父组件拒绝新值时如何恢复,见下方对应问题。
运行时的展开状态与选中状态是两套状态。替换 data 后如果还希望保留用户已经展开的节点,请开启 cache-expanded-keys。
tree-props 的映射配置发生响应式变化时,组件也会重新解析整棵树;但它只负责声明字段名,不会改变 data 不做深度监听的约定。
选中节点后,为什么 v-model 的值没有更新?
按顺序检查:
- 是否传入了
selectable。用户点击产生选择时必须启用该属性;未启用时,组件只展示树,不会因为点击而更新选中值。 - 是否点击了选择图标。默认点击箭头只会展开或收起,点击节点文字也不会选中。若希望点击整行时选中,可开启
check-on-click-node;若只允许点击叶子节点行时选中,可使用check-on-click-leaf。 v-model的初始值类型是否与选择模式一致。
单选模式是传入 selectable、不传 multiple。此时 v-model 是单个节点的 key,也就是该节点的 id(或 tree-props.id 映射字段值),例如字符串 "frontend" 或数字 1001;没有选中项时为 null:
import { ref } from "vue";
const selectedKey = ref<string | number | null>(null);多选模式需要同时传入 selectable 和 multiple,此时 v-model 是 key 数组:
const checkedKeys = ref<Array<string | number>>([]);<!-- 单选:selectedKey 是单个 key 或 null -->
<uni-tree-view v-model="selectedKey" selectable :data="treeData" />
<!-- 多选:checkedKeys 是 key 数组 -->
<uni-tree-view v-model="checkedKeys" selectable multiple :data="treeData" />如果使用的是单向绑定 :model-value,而不是 v-model,则需要自行监听 update:modelValue 并更新外部变量。
父组件不接受新的选中值时,界面为什么没有自动恢复?
用户选择节点时,组件会先更新内部显示状态,再触发 update:modelValue 和 check-change。因此,即使父组件经过校验后没有保存新值,组件内部也不会自动回到旧状态。
需要拒绝本次选择时,请通过 ref 调用 setCheckedKeys 恢复,并使用防重入标记,避免恢复操作再次进入同一段校验逻辑。下面示例使用 check-strictly,当选中数量超过 3 个时取消刚刚操作的节点:
<uni-tree-view
ref="treeRef"
v-model="value"
selectable
multiple
check-strictly
:data="data"
@check-change="handleCheckChange"
/>import { ref } from "vue";
import type { TreeCheckChangePayload, TreeKey, UniTreeViewExposed } from "uni-tree-view/shared";
const treeRef = ref<UniTreeViewExposed>();
const value = ref<TreeKey[]>([]);
const maxChecked = 3;
let rollingBack = false;
function handleCheckChange(payload: TreeCheckChangePayload) {
if (rollingBack || payload.keys.length <= maxChecked) {
return;
}
rollingBack = true;
try {
treeRef.value?.setCheckedKeys(payload.node.id, false);
} finally {
rollingBack = false;
}
}如果需要恢复一组复杂的父子选中状态,请在同一个防重入区间内完成清空和重新设置,避免恢复操作再次触发校验。
勾选父节点后,为什么子节点也会一起变化?
多选模式默认启用父子联动:
- 勾选父节点,会勾选它下面的所有可选子节点。
- 只勾选部分子节点时,父节点显示为半选。
- 所有可选子节点都选中后,父节点显示为全选。
如果希望父节点和子节点各自独立,开启 check-strictly:
<uni-tree-view selectable multiple check-strictly :data="data" />禁用节点为什么不能被全选、清空或父子联动改变?
checked-disabled 控制禁用节点是否允许改变选中状态,默认值为 false。默认情况下,用户点击、全选、清空、父子联动、实例方法和外部更新 v-model,都不会改变禁用节点当前的选中状态。
如果业务上需要允许这些操作改变禁用节点,请显式开启:
<uni-tree-view selectable multiple checked-disabled :data="data" />禁用节点为什么仍然出现在 v-model 或返回结果中?
节点能否改变选中状态,与选中后是否出现在返回结果中,是两件不同的事:
checked-disabled控制禁用节点的选中状态能不能被改变。pack-disabled-key控制已经选中的禁用节点是否包含在返回结果中,默认值为true。
pack-disabled-key 会影响:
v-modelcheck-change的keys和nodesgetCheckedKeys()和getCheckedNodes()
如果不希望已选中的禁用节点出现在这些结果中,可设置:
<uni-tree-view selectable multiple :pack-disabled-key="false" :data="data" />这只会把禁用节点从返回结果中排除,不会取消它在组件内部和界面上的选中状态。
在非严格多选模式下,禁用父节点仍会汇总子节点状态:部分子节点选中时显示半选,全部可选子节点选中时显示全选,但父节点本身不能直接操作。开启 check-strictly 后,父子状态互相独立,不再进行汇总。
开启虚拟渲染后,为什么会滚动错位?
虚拟模式要求视口高度和每行高度都是固定的 px 数值。virtual-height 和 virtual-item-height 当前不接受 rpx、%、vh 或 calc()。
内置节点会使用 virtual-item-height 作为实际行高。如果插槽内容比默认节点更高,需要同步调大该值,并避免使用高度会动态变化的内容,否则计算位置与实际位置不一致,就会出现滚动错位。
节点较少、内容总高度不足 virtual-height 时,组件会渲染全部节点,但滚动区域仍保持设置的固定高度,因此底部可能出现空白。这是定高虚拟列表的预期行为;需要高度随内容变化时,请关闭 virtual。
小程序中自定义样式为什么没有生效?
组件启用了 virtualHost,传给组件的普通 class 可以作用于组件根节点。如果需要定制每个节点行,请通过 node-class 传入稳定的外部类名;主题色、缩进和节点内容分别使用 theme-color、indent 与插槽进行定制。
不要直接依赖组件内部类名,同时需要注意小程序的组件样式隔离规则。
懒加载子节点失败后,怎么重新加载?
监听 load-error 事件提示用户,并通过 ref 调用 retryLoad(key) 重试:
<uni-tree-view ref="treeRef" load-mode :load-api="loadApi" @load-error="onError" />function onError({ node, error }) {
uni.showToast({ title: "加载失败", icon: "none" });
// 稍后重试:treeRef.value.retryLoad(node.id)
}如何把树组件放进弹窗选择器?
组合项目中已有的弹窗组件即可。下面以 wot-ui 为例:
<wd-popup v-model="show" position="bottom">
<view style="height: 60vh;">
<uni-tree-view v-model="value" selectable multiple :data="data" />
</view>
</wd-popup>还有其他问题?
提交 Issue 时,请附上最小复现示例,并说明运行平台、uni-app 版本、数据样例、期望行为和实际行为。