Vue3中将Prop传递给数据项以能够操作值,而不是实际建模数据项,为什么?

huangapple go评论62阅读模式
英文:

Vue3 Prop passing to data item to be able to manipulate values, not modelling data item actually, why?

问题

我正在使用vue3,想知道如何正确传递数据。

我的组件结构是一个表格(通过pinia存储加载项目):XTableComponent
XTableComponent 有一个子组件:XModalComponent。在渲染的表格中,每一行都有一个按钮。点击该按钮会将当前项目存储在一个数据项中。

XTableComponent

<template>
   <!-- 在每一行中都有一个带有 @click 的按钮,将当前项目作为参数 -->
   <x-modal-component v-if="currentItem" :item="currentItem" ref="x-modal"></x-modal-component>
</template>

<script>
export default {
   data: () => {
      return {
        currentItem: {},
        itemListStore: useItemListStore()
      }
   },
   computed: {
      itemList() {
         return this.itemListStore.list
      }
   },
   methods: {
     showModal(item){
         this.currentItem = item
         this.$refs['x-modal'].show()
     }
   }
}
</script>

我的子组件看起来有点像这样:

XModalComponent

<template>
 <!-- ... -->
<input v-model:value="innerItem.something" type="text">
<button @click="save">保存</button>
</template>
<script>
export default {
   props: {
      item: Object
   },
   data: () => {
      return {
         innerItem: {}
      }
    },
   mounted() {
      this.innerItem = this.item
   },
   methods: {
      save() {
         console.log(this.innerItem) // 这不会显示“something”的修改值
      }
   }
}
</script>

现在,如果我在子组件中修改输入并触发点击事件,值不会更改在我的数据项中...

在vue3中,我在响应性、代理和传递 props 方面做错了什么?

p.s. 这里的代码有点伪代码,所以请原谅我拼写错误或明显遗漏的部分。

p.p.s. 我对vue2相当熟悉,所以也许我混合了一些概念。请告诉我。

p.p.p.s. 我的表格渲染正确,模态窗口看起来也不错。我已经仔细检查了所有名称和拼写错误。

英文:

I am using vue3 and wonder how to pass data the correct way.

My Component structure is one table (items loaded via pinia store): XTableComponent
The XTableComponent has a child: XModalComponent. In the rendered table I have a button in each row. @click on that stores the current item in a data item

XTableComponent:


&lt;template&gt;
   ...that mentioned table in each line a button with @click and the item in the iteration as param
   &lt;x-model-component v-if=&quot;currentItem&quot; :item=&quot;currentItem ref=&quot;x-modal&quot;&gt;&lt;/x-modal-component&gt;
&lt;/template&gt;

&lt;script&gt;
export default {
   data: () =&gt; {
      return {
        currentItem: {},
        itemListStore: useItemListStore()
      }
   },
   computed: {
      itemList() {
         return this.itemListStore.list
      }
   methods: {
     showModal(item){
         this.currentItem = item
         this.$refs[&#39;x-modal&#39;].show()
     }
   }

}
&lt;/script&gt;

My Child component looks a bit like this:

XModalComponent:

&lt;template&gt;
 ....
&lt;input v-model:value=&quot;innerItem.something&quot; type=&quot;text&quot;&gt;
&lt;button @click=&quot;save&quot;&gt;save&lt;/button&gt;

&lt;/template&gt;
&lt;script&gt;
export default {
   props: {
      item: Object
   },
   data: () =&gt; {
      return {
         innerItem: {}
      }
    }
   mounted() {
      this.innerItem = item
   },
   methods: {
      save() {
         console.log(this.innerItem) //this does not show the manipulated value of `something`
      }
   }
}
&lt;/script&gt;

Now, if I manipulate the input in my child component, and trigger a click event, the value does not get changed on my data item ...

what did I get wrong in vue3 with reactiveness, proxeis and passing props?

p.s. my code is kind of pseude code here, so please be fair with me on typos, or obvious parts
that are missing

p.p.s. I am used to vue2 quite well, so maybe I mix concepts. please tell me that too.

p.p.p.s. my table renders correctly, the modal looks fine. i double checked all names and typos.

答案1

得分: 1

因此,正如我们所发现的,问题出在innerItem.something与输入绑定的方式,以及对v-model指令的一些混淆之处。

回顾一下,v-model指令是一种设置组件上属性并监听更新值事件的简写方式。

在Vue 2中,它是:

&lt;child-component
  :value=&quot;myValue&quot;
  @input=&quot;(nevValue) =&gt; myValue = newValue&quot;
/&gt;

它等同于

&lt;child-component v-model=&quot;myValue&quot;/&gt;

它允许父级和子级都可以更改变量("双向绑定")。请注意,属性名称和事件与HTML输入元素的属性名称和事件匹配("value"属性和"input"事件),可能是因为它代表了最熟悉的情况,其中一个值绑定到输入:

&lt;input type=&quot;text&quot; v-model=&quot;myText&quot;/&gt;

然而,为了允许在组件上进行多个双向绑定,Vue 2还引入了第二种方式,允许绑定到子组件的任何属性,而不仅仅是"value"。这就是.sync修饰符:

&lt;child-component :childComponentProp.sync=&quot;myVar&quot;/&gt;

它等同于:

&lt;child-component
  :childComponentProp=&quot;myVar&quot;
  @update:childComponentProp=&quot;(newValue) =&gt; myVar = newValue&quot;
/&gt;

在Vue 3中,他们决定统一这两种方式,去掉.sync,而是允许将属性名称传递给v-model,类似于将插槽名称传递给v-slot指令,即v-model:childComponentProp=&quot;myVar&quot;,与v-slot一样,v-model独自等同于v-model:modelValue。所以它等同于:

&lt;my-component
  :modelValue=&quot;myValue&quot;
  @update:modelValue=&quot;(nevValue) =&gt; myValue = newValue&quot;
/&gt;

但上述方式仅适用于Vue组件。在HTML输入元素上使用v-model时,它仍然与Vue 2中的行为相同,绑定到"value"属性和"input"事件。它仍然等同于:

&lt;input :value=&quot;myValue&quot; @input=&quot;(nevValue) =&gt; myValue = nevValue&quot;/&gt;

然而,这种行为是普通v-model(即不带属性名称的情况)的特例。我认为这就是混淆的根源。

当明确使用v-model:value时,它会绑定到@update:value事件,也就是说:

&lt;input v-model:value=&quot;innerItem.something&quot; type=&quot;text&quot;&gt;

等同于:

&lt;input type=&quot;text&quot;
  :value=&quot;innerItem.something&quot;
  @update:value=&quot;(newValue) =&gt; innerItem.something = newValue&quot; 
/&gt;

但是普通HTML元素不会发送该事件。

长话短说,当绑定到本地输入元素时,您必须使用v-model=而不是v-model:value=。这样有意义吗?希望有所帮助。

英文:

So, as we figured out, the problem came from the way innerItem.something was bound to the input, and some confusion around the v-model directive.

As a recap, the v-model directive is short-hand for setting a prop on a component and listening to an event which updates the value.

In Vue 2, that was:

&lt;child-component
  :value=&quot;myValue&quot;
  @input=&quot;(nevValue) =&gt; myValue = newValue&quot;
/&gt;

which is equivalent to

&lt;child-component v-model=&quot;myValue&quot;/&gt;

and it allows a variable to be changed by parent as well as child ("two-way binding"). Note that property name and event matches that of a HTML input element (the "value" attribute and the "input" event), probably because it represents the most familiar case, where a value is bound to an input:

&lt;input type=&quot;text&quot; v-model=&quot;myText&quot;/&gt;

However, to allow for multiple two-way bindings on a component, Vue 2 also introduced a second way, which allows to bind to any of the child components props, not just "value". This is the .sync modifier:

&lt;child-component :childComponentProp.sync=&quot;myVar&quot;/&gt;

which is equivalent to:

&lt;child-component
  :childComponentProp=&quot;myVar&quot;
  @update:childComponentProp=&quot;(newValue) =&gt; myVar = newValue&quot;
/&gt;

In Vue 3, they decided to unify the two, dropping .sync and instead allowing to pass a prop name to v-model similar to how slot names are passed to the v-slot directive, i.e. v-model:childComponentProp=&quot;myVar&quot;, and similar as v-slot alone is equivalent to v-slot:default, v-model alone is equivalent to v-model:modelValue. So it is equivalent to:

&lt;my-component
  :modelValue=&quot;myValue&quot;
  @update:modelValue=&quot;(nevValue) =&gt; myValue = newValue&quot;
/&gt;

But the above only applies for Vue components. When using v-model on an HTML input element, it sill behaves like in Vue 2 and binds to the "value" attribute and the "input" event. It is still equivalent to:

&lt;input :value=&quot;myValue&quot; @input=&quot;(nevValue) =&gt; myValue = nevValue&quot;/&gt;

However, that behavior is a special case of plain v-model (i.e. without a prop name). And I think this is where the confusion comes from.

Using v-model:value explicitly binds to the @update:value event, i.e. this

&lt;input v-model:value=&quot;innerItem.something&quot; type=&quot;text&quot;&gt;

is equivalent to:

&lt;input type=&quot;text&quot;
  :value=&quot;innerItem.something&quot;
  @update:value=&quot;(newValue) =&gt; innerItem.something = newValue&quot; 
/&gt;

but that event is not sent by a plain HTML element.

So long long story short, you have to use v-model= instead of v-model:value= when binding to a native input element.

Does that make sense? Hope it helps.

huangapple
  • 本文由 发表于 2023年2月19日 01:46:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/75495233.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定