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

单向数据流

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

我在 stackblitz 写了个例子,使用版本为 [email protected]。可以看到在,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-modeldefineModel() 都在简化单向数据流麻烦的操作,也是只需要一个等号就把数据更新到父组件了。确实,在大多数情况我都根本不在乎他是不是单向数据流,此时此刻只想修改数据,仅此而已。

computed? readonly!

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

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

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

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

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

相关链接

相关推荐
Gazer_S12 分钟前
【Web 应用缓存与部署优化指南】
前端·缓存
老李笔记39 分钟前
VUE element table 列合并
javascript·vue.js·ecmascript
滿40 分钟前
Vue3 Element Plus 表格默认显示一行
javascript·vue.js·elementui
好了来看下一题42 分钟前
TypeScript 项目配置
前端·javascript·typescript
江城开朗的豌豆1 小时前
Vue的双向绑定魔法:如何让数据与视图‘心有灵犀’?
前端·javascript·vue.js
江城开朗的豌豆1 小时前
Vue权限控制小妙招:动态渲染列表的优雅实现
前端·javascript·vue.js
@菜菜_达2 小时前
CSS a标签内文本折行展示
前端·css
霸王蟹2 小时前
带你手写React中的useReducer函数。(底层实现)
前端·javascript·笔记·学习·react.js·typescript·前端框架
托尼沙滩裤2 小时前
【Vue3】实现屏幕共享惊艳亮相
前端·javascript·vue.js
啃火龙果的兔子2 小时前
前端八股文-vue篇
前端·javascript·vue.js