引用类型 props 默认值必须用工厂函数返回新对象,Vue 中写 default: () => ({}),React 中在组件内解构兜底,禁用 defaultProps 字面量,配合类型校验与运行时验证确保隔离。

直接给对象类型 props 写字面量默认值(比如 default: { theme: 'light' })会导致多个组件实例共享同一内存地址,修改一处,其他地方跟着变——这不是 bug,是 JavaScript 引用类型的本质行为。避坑核心就一条:所有引用类型(Object、Array、Date、自定义类实例等)的默认值,必须由函数返回。
Vue 中正确写法:工厂函数必须每次返回新对象
Vue 会在开发模式下对非工厂函数的对象默认值发出警告,但不能只依赖这个提示。关键看写法是否真正隔离:
-
✅ 推荐:直接在
default字段里写箭头函数并返回字面量config: { type: Object, default: () => ({ theme: 'light', size: 'md' }) } -
✅ 安全扩展:需要复用结构时,把基础字段抽成常量,但工厂函数里仍要显式构造新对象
const baseConfig = { size: 'md' };<br>config: { type: Object, default: () => ({ ...baseConfig, theme: 'light' }) } -
❌ 危险写法:外部定义对象再返回引用
const DEFAULT_CONFIG = { theme: 'light' };<br>config: { type: Object, default: () => DEFAULT_CONFIG } // 所有实例共用同一个对象
React 中不依赖 defaultProps,改在组件内兜底
React 的 defaultProps 同样禁止对象字面量,默认值逻辑应下沉到组件内部,更可控也更符合 Hooks 习惯:
-
✅ 推荐(函数组件):解构时设默认空对象,再合并配置
function Button({ config = {} }) {<br> const finalConfig = { theme: 'primary', size: 'md', ...config };<br> return <button className={finalConfig.theme}>...</button>;<br>} -
✅ 性能优化(可选):用
useMemo避免每次渲染都新建对象(仅当config变化时更新)const finalConfig = useMemo(() => ({ theme: 'primary', size: 'md', ...config }), [config]); -
❌ 不推荐:在
defaultProps里写对象字面量Button.defaultProps = { config: { theme: 'primary' } }; // 多个 Button 实例共享该对象
类型校验 + 工厂函数双保险
光有工厂函数还不够,得让类型系统和运行时一起把关:
- Vue 中用
Object as PropType<MyConfig>显式标注类型,配合validator检查必填字段config: {<br> type: Object as PropType<MyConfig>,<br> default: () => ({}),<br> validator: v => v && 'theme' in v<br>} - React 中用 TypeScript + 运行时校验(如
prop-types或自定义if (process.env.NODE_ENV !== 'production')判断),确保传入对象结构合法 - 避免用
JSON.parse(JSON.stringify())做深拷贝——性能差,且不支持函数、undefined、Date等值
快速验证是否踩坑的小技巧
写完默认值别急着提交,花 30 秒验证一下:
- 在模板中加
{{ props.config === $options.propsData?.config }},不同实例输出true就说明是同一引用 - 在
mounted或useEffect里打印props.config的__proto__或用Object.is()对比两个实例的值 - 单元测试渲染两个组件,修改其中一个的
config.theme,断言另一个未受影响


















