Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Vue 中 props 是 Object 可以直接修改吗

Vue 中 props 是 Object 可以直接修改吗

作者头像
windliang
发布于 2023-08-18 06:12:20
发布于 2023-08-18 06:12:20
1.1K00
代码可运行
举报
文章被收录于专栏:windliang的博客windliang的博客
运行总次数:0
代码可运行

好久没有在知乎上看到好问题了,前几天看到一个,把回答同步过来。

https://www.zhihu.com/question/609822540/answer/3099837968

确实是一个很有争议的问题,团队里也经常讨论这个问题,下边分享下我的想法,也不一定是最佳实践。

首先,不要修改 prop 的值肯定是一条比较好的实践,保证数据的流向明确。

官方文档中也有明确指出:https://vuejs.org/guide/components/props.html#one-way-data-flow

One-Way Data Flow

All props form a one-way-down binding between the child property and the parent one: when the parent property updates, it will flow down to the child, but not the other way around. This prevents child components from accidentally mutating the parent's state, which can make your app's data flow harder to understand.

In addition, every time the parent component is updated, all props in the child component will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do, Vue will warn you in the console:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export default {
  props: ['foo'],
  created() {
    // ❌ 警告!prop 是只读的!
    this.foo = 'bar'
  }
}

There are usually two cases where it's tempting to mutate a prop:

1. The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards. In this case, it's best to define a local data property that uses the prop as its initial value:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Vue3
const props = defineProps(['initialCounter'])

// counter only uses props.initialCounter as the initial value;
// it is disconnected from future prop updates.
const counter = ref(props.initialCounter)

// Vue2
props: ['initialCounter'],
data: function () {
  return {
    counter: this.initialCounter
  }
}

2. The prop is passed in as a raw value that needs to be transformed. In this case, it's best to define a computed property using the prop's value:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// Vue3
const props = defineProps(['size'])

// computed property that auto-updates when the prop changes
const normalizedSize = computed(() => props.size.trim().toLowerCase())

// Vue2
props: ['size'],
computed: {
  normalizedSize: function () {
    return this.size.trim().toLowerCase()
  }
}

为了避免修改 prop 的值,可以在 data 中初始化为 prop 的值然后再去使用或者定义 computed 属性拿到 prop 值再去使用。

当然,上边的写法也仅仅对原始值生效,如果 props 定义成一个 Array 或者 Object,如果把 Object 的值直接赋值给 data:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
props: ['initialCounterObj'],
data: function () {
  return {
    counterObj: this.initialCounterObj
  }
}

当去修改 counterObj 中的值,虽然看起来没有修改 props 的值,但因为 Objeact 传递进来的是引用,修改 counterObj 的值的时候外部的相应的对象也跟着修改了。

针对这种情况,可以将 Object 摊开,变为一个个原始值。

通过 .sync

父组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<child field1.sync="obj.field1" field2.sync="obj.field2"></child>

子组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export default {
  props: ['field1', 'field2'],
  methods: {
      updateField1(newVal){
           this.$emit('update:field1', newVal)
      },
      updateField2(newVal){
           this.$emit('update:field2', newVal)
      }
  }
}

通过 get set

https://stackoverflow.com/questions/59992698/vuejs-best-practices-for-passing-form-data-to-child-and-back-to-parent?rq=4

父组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<child v-model="obj"></child>

子组件

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
export default {
  props: {
    value: {
      type: Object,
      default: () => ({})
    }
  },

  computed: {
    field1: {
      get() { return this.value.field1 },
      set(field1) { this.$emit('input', {...this.value, field1 })}
    },
    field2: {
      get() { return this.value.field1 },
      set(field2) { this.$emit('input', {...this.value, field2 })}
    }
  }
}

另外一种更暴力的写法就是题主讲到的方案三 「不可变对象型」,每次修改前都把整个对象(或数组)克隆一遍,修改新的对象,再通过 emit 事件把新的对象(这里最好也再克隆一下)传出去。

上边的方案都可以保证不去修改 props 的值。

看下官方对于 props 是 Object/Array 的态度:

Mutating Object / Array Props

When objects and arrays are passed as props, while the child component cannot mutate the prop binding, it will be able to mutate the object or array's nested properties. This is because in JavaScript objects and arrays are passed by reference, and it is unreasonably expensive for Vue to prevent such mutations.

The main drawback of such mutations is that it allows the child component to affect parent state in a way that isn't obvious to the parent component, potentially making it more difficult to reason about the data flow in the future. As a best practice, you should avoid such mutations unless the parent and child are tightly coupled by design. In most cases, the child should emit an event to let the parent perform the mutation.

关键句:you should avoid such mutations unless the parent and child are tightly coupled by design.

因此对于表单场景,我认为符合 parent and child are tightly coupled by design ,很多时候由于表单越来越大,一个 Vue 文件会变得巨大,此时想要拆部分表单出来成为一个组件,这种情况下采用题主所说的方案一「直接修改型」我认为是更佳的,不然的话不管采用什么方式保证不修改 props 都会增加很多代码,反而增加了很多理解成本。

更进一步,对于 Object/Array,是否修改 props 取决于当前组件的通用性,如果这个组件专门为了某个父组件使用或者专门服务于某个页面,并且为了不修改 props 会增加很多工作量,这种情况下直接修改 props 我认为是合适的。

但如果这个组件可能用给其他人,此时修改 props ,如果使用方不清楚的话就可能引发问题。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 windliang 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
校招前端二面高频vue面试题
在Vue中,对响应式处理利用的是Object.defineProperty对数据进行拦截,而这个方法并不能监听到数组内部变化,数组长度变化,数组的截取变化等,所以需要对这些操作进行hack,让Vue能监听到其中的变化。 那Vue是如何实现让这些数组方法实现元素的实时更新的呢,下面是Vue中对这些方法的封装:
bb_xiaxia1998
2022/12/20
1.5K0
2023前端二面高频vue面试题集锦1
可以看到,组件内部只有一个动态节点,剩余一堆都是静态节点,所以这里很多 diff 和遍历其实都是不需要的,造成性能浪费
bb_xiaxia1998
2023/01/02
1.2K0
vue-自定义组件传值
项目中,我们经常会遇到自定义组件传值的问题,方法很多种,但是原理很简单,下述文档总结实际项目中使用的传值方式。
庞小明
2018/08/01
6360
vue-自定义组件传值
vue组件引用传值的最佳实践
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。
奋飛
2020/05/28
1.8K0
Vue基础:组件--组件及组件通信
组件可以扩展 HTML 元素,封装可重用的代码。在较高层面上,组件是自定义元素,Vue.js 的编译器为它添加特殊功能。在有些情况下,组件也可以是原生 HTML 元素的形式,以is特性扩展。
奋飛
2019/08/15
1.9K0
大话大前端时代(一) —— Vue 与 iOS 的组件化
今年大前端的概念一而再再而三的被提及,那么大前端时代究竟是什么呢?大前端这个词最早是因为在阿里内部有很多前端开发人员既写前端又写 Java 的 Velocity 模板而得来,不过现在大前端的范围已经越来越大了,包含前端 + 移动端,前端、CDN、Nginx、Node、Hybrid、Weex、React Native、Native App。笔者是一名普通的全职 iOS 开发者,在接触到了前端开发以后,发现了前端有些值得移动端学习的地方,于是便有了这个大前端时代系列的文章,希望两者能相互借鉴优秀的思想。谈及到大前端,常常被提及的话题有:组件化,路由与解耦,工程化(打包工具,脚手架,包管理工具),MVC 和 MVVM 架构,埋点和性能监控。笔者就先从组件化方面谈起。网上关于前端框架对比的文章也非常多(对比 React,Vue,Angular),不过跨端对比的文章好像不多?笔者就打算以前端和移动端(以 iOS 平台为主)对比为主,看看这两端的不同做法,并讨论讨论有无相互借鉴学习的地方。
一缕殇流化隐半边冰霜
2018/08/30
8490
大话大前端时代(一) —— Vue 与 iOS 的组件化
10个Vue开发技巧助力成为更好的工程师(二)
更新 prop 在业务中是很常见的需求,但在子组件中不允许直接修改 prop,因为这种做法不符合单向数据流的原则,在开发模式下还会报出警告。因此大多数人会通过 $emit 触发自定义事件,在父组件中接收该事件的传值来更新 prop。
WahFung
2020/08/24
1.1K0
教你快速学会vue-property-decorator结合vue的使用
当我们在vue单文件中使用TypeScript时,引入vue-property-decorator之后,script中的标签就变为这样:
前端老鸟
2022/03/07
1.7K0
Vue 3 Props 类型
为什么需要 props 类型呢?就比如我们子组件需要用到父组件的数据,我们到底该使用何种方式传递进去呢?我们都知道在原生 DOM 中有一种 data- 属性,可以将数据绑定,所以类似这种方式,props 就应运而生了。
公众号---人生代码
2020/11/03
4.2K0
Vue 3 Props 类型
Vue+Vue-Router+Vuex+SSR项目
Vue-Vue-Router-Vuex-SSR Vue+Webpack工程流搭建 Vue+Vue-Router+Vuex项目架构 服务端渲染 现在的前端框架是纯客户端渲染的,(请求🤴网站的时候,返回的html是没有什么内容的),存在问题是没有办法seo, 白屏时间较长。需要等待js加载完成,执行完成之后才会显示内容。 服务端渲染解决这些问题。 webpack升级注意 ⚠️ :1. 版本变化 2. 配置变化 3. 插件变化 vue-loader配置 const isDev = process.env.NOD
达达前端
2022/04/13
9000
前端面试5家公司,被经常问到的vue面试题
在HTML中 slot 元素 ,作为 Web Components 技术套件的一部分,是Web组件内的一个占位符
bb_xiaxia1998
2022/09/22
1.1K0
vue3.0快速上手教程之vue--组件02
props是自定义属性,组件之间可以通过props属性去自定义一些属于自己的属性,并通过这个属性来进行组件之间的数据传输。
张哥编程
2024/12/13
2490
vue3.0快速上手教程之vue--组件02
Vue中组件间通信的方式
这种组件通信的方式是我们运用的非常多的一种,props以单向数据流的形式可以很好的完成父子组件的通信,所谓单向数据流,就是数据只能通过props由父组件流向子组件,而子组件并不能通过修改props传过来的数据修改父组件的相应状态,所有的prop都使得其父子prop之间形成了一个单向下行绑定,父级prop的更新会向下流动到子组件中,但是反过来则不行,这样会防止从子组件意外改变父级组件的状态,导致难以理解数据的流向而提高了项目维护难度。实际上如果传入一个基本数据类型给子组件,在子组件中修改这个值的话Vue中会出现警告,如果对于子组件传入一个引用类型的对象的话,在子组件中修改是不会出现任何提示的,这两种情况都属于改变了父子组件的单向数据流,是不符合可维护的设计方式的。 正因为这个特性,而我们会有需要更改父组件值的需求,就有了对应的emit,当我们在组件上定义了自定义事件,事件就可以由vm.emit触发,回调函数会接收所有传入事件触发函数的额外参数,
WindRunnerMax
2021/02/02
3.1K0
【初级】个人分享Vue前端开发教程笔记
每天学习编程,让你离梦想更新一步,感谢不负每一份热爱编程的程序员,不论知识点多么奇葩,和我一起,让那一颗四处流荡的心定下来,一直走下去,加油,2021加油!
达达前端
2021/02/04
4.9K0
2022前端经典vue面试题(持续更新中)
一个SPA应用的路由需要解决的问题是 页面跳转内容改变同时不刷新 ,同时路由还需要以插件形式存在,所以:
bb_xiaxia1998
2022/09/16
1K0
Vue.js源码逐行代码注解src下core下instance
达达前端
2023/10/08
3490
Vue3 的 7 种和 Vue2 的 12 种组件通信,值得收藏
如果父组件是混合写法,子组件纯 Vue3 写法的话,是接收不到父组件里 data 的属性,只能接收到父组件里 setup 函数里传的属性。
Swift社区
2024/10/10
2910
Vue3 的 7 种和 Vue2 的 12 种组件通信,值得收藏
[译]如何用 Typescript 写一个完整的 Vue 应用程序
译者推荐:Typescript 和 Vue 都是现在前端必备的知识,本文基本覆盖了目前 Vue 2.x 的一些基础用法的 Typescript 版本实现,感兴趣的了解一下,更好的迎接 Vue 3.0
GopalFeng
2020/09/24
2.2K0
[译]如何用 Typescript 写一个完整的 Vue 应用程序
一文看完vue3的变化之处
之前在非组件的情况下创建实例可以使用对象,但是现在所有情况下都只能使用一个返回对象的函数。
街角小林
2022/06/15
3.1K0
老司机读书笔记——Vue学习笔记
在默认情况下,v-model 在每次 input 事件触发后将输入框的值与数据进行同步 (除了上述输入法组合文字时)。你可以添加 lazy 修饰符,从而转变为使用 change 事件进行同步:
老司机Wicky
2018/08/22
3.4K0
老司机读书笔记——Vue学习笔记
相关推荐
校招前端二面高频vue面试题
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验