先记住这个答案
在运行时 props 选项中,对象或数组默认值通常写成工厂函数,让每个需要默认值的组件实例获得独立容器,避免共享同一个可变引用。工厂应实际创建新对象,不能只是返回模块里的同一个对象。默认值主要在传入值为 undefined 时采用,并非每次渲染都重新执行。还要区分语法:withDefaults 的可变默认值需要注意工厂写法,Vue 3.5 响应式 props 解构中的原生默认表达式则有不同处理,不宜一概而论。
- 默认工厂解决实例间共享引用问题,不等于深复制所有来源
- undefined 会触发默认逻辑,null 通常表示显式传入的值
- 运行时 default、withDefaults 和解构默认表达式不能混为一种语法
直接对象为何可能跨实例共享
组件选项定义通常会被多个实例复用。如果 default 直接指向一个对象,或者工厂返回一个模块级常量,各实例得到的仍可能是同一引用。一个实例通过嵌套修改改变这个对象,其他实例也会看到污染,问题不一定在首次展示时就暴露。
正确工厂在执行时创建新容器。下面的数组默认值会为两个独立实例分别创建数组;如果数组中还引用同一个模块级嵌套对象,隔离仍不完整。是否需要更深复制,应由数据结构和所有权决定,不能把外层新数组误当成所有内容都独立。
import { defineComponent, h } from 'vue';
const TagList = defineComponent({
props: {
tags: {
type: Array,
default: () => []
}
},
setup(props) {
return () => h('span', String(props.tags.length));
}
});两个没有传 tags 的 TagList 实例应得到不同数组引用。父组件若显式给两个实例传同一数组,工厂不会再替父层数据做隔离。
默认值不是每次更新都重建的状态
当 prop 的值为 undefined,包括通常的未传场景,Vue 会采用默认逻辑;显式 null 一般不会因此被替换。对象默认工厂的结果按实例保留,不能把它当作每次重新渲染都会触发的初始化钩子。需要重置编辑内容时,应设计明确的重置动作或输入变化规则。
Function 类型的 prop 还存在重要区别:默认函数可能就是要交给调用者使用的函数值,不应机械地再包一层工厂。讨论默认写法时应先确认 prop 的类型和声明方式,避免把为对象隔离提出的规则推广到所有函数。
区分三种常见默认语法
运行时 defineProps 对象声明采用 default 选项,withDefaults 则为类型式声明补充默认值,这两类场景的可变值需要注意工厂。Vue 3.5 的响应式 props 解构支持原生默认表达式,写解构默认数组时不需要再照搬同样的工厂形式。
迁移代码时先确认 Vue 与编译器版本,再检查实际使用的宏和语法。不能看见默认值里出现数组字面量就立即认定跨实例共享。除了阅读代码,还可以挂载两个实例比较引用、分别更新父层输入,验证隔离与覆盖规则是否符合组件契约。
容易答错的地方
- 只要写成箭头函数就一定独立
- 若箭头函数返回同一个外部对象,引用仍共享。重点是每次需要实例默认值时是否创建了正确的新数据。
- default 可以替换所有空值
- null、空字符串和 undefined 不是同一输入,默认值并非通用的空值清洗规则。排查“Vue 对象数组 prop 默认值工厂函数与实例隔离”时还要核对输入、版本和执行顺序,并用反例确认修复后的边界。
面试官还会怎么问?
工厂返回浅复制对象够不够?
取决于嵌套数据是否允许共享。外层展开不会复制嵌套引用,应根据实际修改范围选择隔离策略。针对“Vue 对象数组 prop 默认值工厂函数与实例隔离”,还应保留最小复现、预期结果和失败路径,避免只凭一次现象下结论。
父层提供对象后还会用默认工厂吗?
正常提供了非 undefined 值时使用父层输入,默认工厂不负责复制或隔离这个输入。针对“Vue 对象数组 prop 默认值工厂函数与实例隔离”,还应保留最小复现、预期结果和失败路径,避免只凭一次现象下结论。
如何验证没有实例间污染?
同时挂载两个实例,检查默认容器身份和独立变化,再加入父层显式共享输入的对照场景,避免混淆两种来源。
参考资料
示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。