更新子组件内的道具,使其也在父容器上更新

Posted

技术标签:

【中文标题】更新子组件内的道具,使其也在父容器上更新【英文标题】:Updating a prop inside a child component so it updates on the parent container too 【发布时间】:2019-01-27 23:57:39 【问题描述】:

所以我有一个像这样的简单模板:

<resume-index>
    <div v-for="resume in resumes">
        <resume-update inline-template :resume.sync="resume" v-cloak>
            //...my forms etc
        <resume-update>
    </div>
<resume-index>

现在,在resume-updatecomponent 内部,我正在尝试更新内部的道具,以便外部不会被覆盖,我的代码就是这样;

import Multiselect from "vue-multiselect";
import __ from 'lodash';

export default 
    name: 'resume-update',
    props: ['resume'],
    components: 
        Multiselect
    ,

    data: () => (
        form: 
            name: '',
            level: '',
            salary: '',
            experience: '',
            education: [],
            employment: []
        ,
        submitted: 
            form: false,
            destroy: false,
            restore: false
        ,
        errors: []
    ),

    methods: 
        update(e) 
            this.submitted.form = true;
            axios.put(e.target.action, this.form).then(response => 
                this.resume = response.data.data
                this.submitted.form = false;
            ).catch(error => 
                if (error.response) 
                    this.errors = error.response.data.errors;
                
                this.submitted.form = false;
            );
        ,
        destroy() 
            this.submitted.destroy = true;
            axios.delete(this.resume.routes.destroy).then(response => 
                this.resume = response.data.data;
                this.submitted.destroy = false;
            ).catch(error => 
                this.submitted.destroy = false;
            )
        ,
        restore() 
            this.submitted.restore = true;
            axios.post(this.resume.routes.restore).then(response => 
                this.resume = response.data.data;
                this.submitted.restore = false;
            ).catch(error => 
                this.submitted.restore = false;
            )
        ,
        reset() 
            for (const prop of Object.getOwnPropertyNames(this.form)) 
                delete this.form[prop];
            
        
    ,

    watch: 
        resume: function() 
            this.form = this.resume;
        ,
    ,

    created() 
        this.form = __.cloneDeep(this.resume);
    

当我提交表单并更新 this.resume 时,我得到以下信息:

[Vue 警告]:避免直接改变一个 prop,因为它的值会是 每当父组件重新渲染时被覆盖。相反,使用 基于道具值的数据或计算属性。道具存在 变异:“简历”

我尝试将计算添加到我的文件中,但这似乎不起作用:

computed: 
    resume: function() 
        return this.resume
    

那么,我该如何更新道具呢?

【问题讨论】:

不要更新道具。使用 $emit 将事件发回给父级。 【参考方案1】:

一种解决方案:

模拟v-model

作为 Vue Guide said:

v-model 本质上是用于更新用户输入数据的语法糖 事件,以及对一些边缘情况的特殊照顾。

语法糖如下:

directive=v-model 将绑定值,然后监听input 事件以进行更改,如v-bind:value="val" v-on:input="val = $event.target.value"

那么步骤:

    创建一个你想同步到父组件的 prop = value

    在子组件内部,创建一个数据端口=internalValue,然后使用Watcher将最新的prop=value同步到data property=intervalValue

    如果intervalValue发生变化,发出一个输入事件来通知父组件

下面是一个简单的演示:

Vue.config.productionTip = false
Vue.component('container', 
  template: `<div>
              <p><button @click="changeData()">value</button></p>
             </div>`,
  data() 
    return 
      internalValue: ''
    
  ,
  props: ['value'],
  mounted: function () 
    this.internalValue = this.value
  ,
  watch: 
    value: function (newVal) 
      this.internalValue = newVal
    
  ,
  methods: 
    changeData: function () 
      this.internalValue += '@'
      this.$emit('input', this.internalValue)
    
  
)

new Vue(
  el: '#app',
  data () 
    return 
      items: ['a', 'b', 'c']
    
  
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <div>
    <p>items
    <container v-for="(item, index) in items" :key="index" v-model="items[index]">
    </container>
  </div>
</div>

或使用其他道具名称代替(以下演示使用道具名称=item):

您也可以使用其他事件名称代替事件名称=输入

其他步骤类似,但您必须在事件上 $on 然后实现您自己的处理程序,如下面的演示。

Vue.config.productionTip = false
Vue.component('container', 
  template: `<div>
              <p><button @click="changeData()">item</button></p>
             </div>`,
  data() 
    return 
      internalValue: ''
    
  ,
  props: ['item'],
  mounted: function () 
    this.internalValue = this.item
  ,
  watch: 
    item: function (newVal) 
      this.internalValue = newVal
    
  ,
  methods: 
    changeData: function () 
      this.internalValue += '@'
      this.$emit('input', this.internalValue)
      this.$emit('test-input', this.internalValue)
    
  
)

new Vue(
  el: '#app',
  data () 
    return 
      items: ['a', 'b', 'c']
    
  ,
  methods: 
    syncChanged: function (target, index, newData) 
      this.$set(target, index, newData)
    
  
)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
  <div>
    Event Name=input
    <p>items</p>
    <container v-for="(item, index) in items" :key="index" :item="item" @input="syncChanged(items, index,$event)">
    </container>
  </div>
  <hr> Event Name=test-input
    <container v-for="(item, index) in items" :key="index" :item="item" @test-input="syncChanged(items, index,$event)">
    </container>
</div>

【讨论】:

【参考方案2】:

我通常使用vuex 来管理我将在多个组件中使用的变量,就像错误所说的那样,使用计算属性将它们加载到各个组件中。然后使用store对象的mutations属性来处理变化

在组件文件中

computed: 
    newProfile: 
      get() 
        return this.$store.state.newProfile;
      ,
      set(value) 
        this.$store.commit('updateNewProfile', value);
      
    ,

在 vuex 商店中

state: 
    newProfile: 
      Name: '',
      Website: '',
      LoginId: -1,
      AccountId: ''
    
,
mutations: 
    updateNewProfile(state, profile) 
      state.newProfile = profile;
    

【讨论】:

这也是我最近发现自己在做的事情。可能有更好的方法来做到这一点,但我不知道。 如果它只是父子数据,他们应该使用事件发射器。 Vuex 更多的是服务于全局状态,而不仅仅是简单的父子数据突变

以上是关于更新子组件内的道具,使其也在父容器上更新的主要内容,如果未能解决你的问题,请参考以下文章

从父 Vue 更新子组件中的 prop 值

仅在父状态更新后如何接收道具?

Vue cli 3道具(父到子)子在父变量更改后永远不会更新

VueJS 在不更改父数据的情况下编辑道具的正确方法

Vue 道具没有在子组件中正确更新

在父组件的值更改时更新子组件中的值