Vue 组件的 Props 到底能不能改?

单向数据流

Vue 推崇单向数据流这个概念,也就是数据流向必须是从父到子。子组件想要修改数据必须 emit 一个事件,父组件接收到事件后,由父组件修改数据传回到子组件。

我在 stackblitz 写了个例子,使用版本为 Vue@3.4.5。可以看到在,props proxy 的外层 handler 是 ReadonlyReactiveHandler:

于是当你想直接修改 props 的时候会被狠狠拦住,提示 VM383:1 [Vue warn] Set operation on key "msg" failed: target is readonly.。

留个后门

也是上面的图,能看到在 ReadonlyReactiveHandler 底下的目标数据仍然是原来的响应式数据(就是同一个,=== 为 true),所以底层的数据仍然是可以改的,并且是符合正常响应式表现的。

官方文档本身就有提到这种直接修改 props 里的对象或数组的行为,态度是不推荐,因为这么做会导致数据流混乱,造成数据不知道在哪被改了的情况。但是文档也有提到,如果父子组件本身就是设计得紧密耦合的话,直接改也不是不行。

虽然 readonly() 本来就可以把整个响应式对象设置为只读,不这么做的原因据文档说是考虑到会有性能问题,而且确实会有少数这样的需求。于是子组件直接修改 props 的后门就留下来了。

P.S. 回想起来好像以前也跟 Vuex 那种数据更新哲学有关,总之大家都习惯了单向数据流的设定。

直接改还是香

我常常遇到直接把一个对象传到 v-model 的情况,子组件会对 modelValue 对象做一些修改,例如其中一个字段修改会重置另一个字段。因为上面提到的,props 底层可以直接改,那么就是直接就改好了,再加个 emit 是不是有点多此一举。

即使是非对象值得情况,好像 Vue 本身就在纵容这个行为。v-model 和 defineModel() 都在简化单向数据流麻烦的操作,也是只需要一个等号就把数据更新到父组件了。确实,在大多数情况我都根本不在乎他是不是单向数据流,此时此刻只想修改数据,仅此而已。

computed? readonly!

其实早在 Vue2 就在用"直接改 props"这个"feature",但是为什么直到现在才水这么一篇呢?直接原因其实是在工作中突然遇到了"明明是 props 的底层对象,却总是提示 readonly 的情况"。

一开始以为是可写 computed 导致内层只读,但测试了一下发现不是,computed 只是一个快照,里面的东西如果本来是响应式的,那么他就是响应式的,如果本来是普通对象,那便是普通对象,跟 props 一样 computed 也是只有最外层只读。

那如果 computed 本身不会让数据里层变成只读,那就只能说这个数据从来源上就已经是只读了......事实也是如此,那是从父组件的父组件传来的一个 useRequest 的数据,本身就不可改。

Vue 提供了 readonly(),可以把响应式数据限制为只读(并且深层也只读),不太常用,但在写 use 函数的时候可以用它提醒用户不能修改这个响应式数据,这个数据完全由 use 接管,所以写库的时候挺实用的。

最后回答标题,结论就是,只有 props 本身只读,内部对象保持原样。如果本来就是响应式数据,那么响应式特性依然存在,改了会更新页面......但文档上还是不推荐直接改。

相关链接

相关推荐
溪语流沙3 分钟前
【每天一个CSS | Day11】赛博朋克故障风文字:红青分离 + 切片抖动
前端·css
liangshanbo121540 分钟前
React Fiber:从“解决卡顿”到完整更新链路
前端·javascript·react.js
Timmy1 小时前
前端转全栈笔记:讲框架之前,先把 TypeScript 这关过了
前端·人工智能·node.js
计算机魔术师1 小时前
年收入650亿、亏420亿,Anthropic凭什么敢叫价万亿?
前端
Frag0ut1 小时前
Chrome Canary 最新实验功能:AI Mode、按设备筛选历史记录与 Gemini 自动改密
前端·人工智能·chrome·dev·beta·canary·最新实验功能
雪芽蓝域zzs1 小时前
第3节:el-select 弹窗定位、滚动穿透与挂载容器(popper-append-to-body /teleported)
vue.js·elementui
anew___1 小时前
《从零手写操作系统 (19):Ext2文件系统实战——从内存到磁盘的跨越》
java·服务器·前端·数据库·算法
吴声子夜歌1 小时前
Nginx应用与运维——Nginx Web服务应用实战(HTTPS安全服务器的搭建)
运维·前端·nginx
liangshanbo12152 小时前
Monorepo 工程化面试题:如何设计 Web + Node.js 中间层 + Shared + Scripts 的 Monorepo?
前端·node.js
IT_陈寒2 小时前
Redis集群切换主节点时,服务竟然全员掉线
前端·人工智能·后端