Vue.js 更换道具

Vue.js Changing props

我对如何更改组件内部的属性有点困惑,假设我有以下组件:

{
    props: {
        visible: {
            type: Boolean,
            default: true
        }
    },
    methods: {
         hide() {
              this.visible = false;
         }
    }
} 

虽然有效,但会发出以下警告:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible" (found in component )

现在我想知道处理这个问题的最佳方法是什么,显然 visible 属性 在 DOM 中创建组件时传入: <Foo :visible="false"></Foo>

阅读您的最新评论后,您似乎担心 show/hide parent 上的警报的逻辑。因此我建议如下:

parent

# template
<alert :alert-visible="alertVisible"></alert>

# script
data () {
  alertVisible: false,
  ...
},
...

然后在 child 警报上,您将 $watch prop 的值并将所有逻辑移动到警报中:

child(警报)

# script
data: {
  visible: false,
  ...
},
methods: {
  hide () {
    this.visible = false
  },
  show () {
    this.visible = true
  },
  ...
},
props: [
  'alertVisible',
],
watch: {
  alertVisible () {
    if (this.alertVisible && !this.visible) this.show()
    else if (!this.alertVisible && this.visible) this.hide()
  },
  ...
},
...

如果道具只对这个child组件有用,给child一个propinitialVisible,和 data 类似 mutableVisible,并且在 created 钩子(组装组件的数据结构时调用)中,简单地 this.mutableVisible = this.initialVisible

如果该道具由 parent 组件的其他 child 人共享,您需要将其设为 parent的 data 使其可供所有 children 使用。然后在child、this.$emit('visibleChanged', currentVisible)通知parent更改visible。在 parent 的模板中,使用 <ThatChild ... :visibleChanged="setVisible" ...>。看看指南:http://vuejs.org/v2/guide/components.html

引用your fiddle

中的代码

不知何故,你应该决定一个 州居住的地方,而不是两个。我不知道将它放在 Alert 中还是只放在 parent 中更适合你的用例,但你应该选择一个。

如何决定状态所在

parent 或任何同级组件是否依赖于状态?

  • 是:那么它应该在parent(或者在一些外部状态管理中)
  • 否:那在组件本身的状态下更容易
  • 有点两者:见下文

在极少数情况下,您可能需要组合。也许你想给 parent 和 child 隐藏 child 的能力。那么你应该在 parent 和 child 中都有状态(所以你不必在 child 中编辑 child 的道具)。

例如,如果满足以下条件,child 是可见的:visible && state_visible,其中 visible 来自道具并反映 parent 状态中的值,并且 state_visible 来自 child 的状态。

我不确定这是否是您想要的行为,但这是一个片段。我有点假设您实际上只想在单击 child.

时调用 parent 组件的 toggleAlert

var Alert = Vue.component('alert', {
  template: `
        <div class="alert" v-if="visible && state_visible">
        Alert<br> 
        <span v-on:click="close">Close me</span>
      </div>`,
  props: {
    visible: {
      required: true,
      type: Boolean,
      default: false
    }
  },
  data: function() {
    return {
      state_visible: true
    };
  },
  methods: {
    close() {
      console.log('Clock this');
      this.state_visible = false;
    }
  }
});

var demo = new Vue({
  el: '#demo',
  components: {
    'alert': Alert
  },
  data: {
    hasAlerts: false
  },
  methods: {
    toggleAlert() {
      this.hasAlerts = !this.hasAlerts
    }
  }
})
.alert {
  background-color: #ff0000;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="demo" v-cloak>
  <alert :visible="hasAlerts"></alert>

  <span v-on:click="toggleAlert">Toggle alerts</span>
</div>

也许它看起来像 hack 并且违反了单一数据源的概念,但它的工作) 此解决方案正在创建本地代理变量并从 props 继承数据。接下来使用代理变量。

Vue.component("vote", {
    data: function() {
        return {
            like_: this.like,
            dislike_: this.dislike,
        }
    },

    props: {
        like: {
            type: [String, Number],
            default: 0
        },
        dislike: {
            type: [String, Number],
            default: 0
        },
        item: {
            type: Object
        }
    },

    template: '<div class="tm-voteing"><span class="tm-vote tm-vote-like" @click="onVote(item, \'like\')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{like_}}</span></span><span class="tm-vote tm-vote-dislike" @click="onVote(item, \'dislike\')"><span class="fa tm-icon"></span><span class="tm-vote-count">{{dislike_}}</span></span></div>',

    methods: {
        onVote: function(data, action) {
            var $this = this;
            // instead of jquery ajax can be axios or vue-resource
            $.ajax({
                method: "POST",
                url: "/api/vote/vote",
                data: {id: data.id, action: action},
                success: function(response) {
                    if(response.status === "insert") {
                        $this[action + "_"] = Number($this[action + "_"]) + 1;
                    } else {
                        $this[action + "_"] = Number($this[action + "_"]) - 1;
                    }
                },
                error: function(response) {
                    console.error(response);
                }
            });
        }
    }
});

使用组件并传递 props

<vote :like="item.vote_like" :dislike="item.vote_dislike" :item="item"></vote>

根据the Vue.js component doc

When the parent property updates, it will flow down to the child, but not the other way around. So, how do we communicate back to the parent when something happens? This is where Vue’s custom event system comes in.

使用子项的 $emit('my-event) 向父项发送事件。使用 v-on:my-event(或 @my-event)在父级内部的子级声明上接收事件。

工作示例:

// child

Vue.component('child', {
  template: '<div><p>Child</p> <button @click="hide">Hide</button></div>',
  methods: {
    hide () {
      this.$emit('child-hide-event')
    }
  },
})

// parent

new Vue({
  el: '#app',
  data: {
    childVisible: true
  },
  methods: {
    childHide () {
      this.childVisible = false
    },
    childShow () {
      this.childVisible = true
    }
  }
})
.box {
  border: solid 1px grey;
  padding: 16px;
}
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
<div id="app" class="box">
  <p>Parent | childVisible: {{ childVisible }}</p>
  <button @click="childHide">Hide</button>
  <button @click="childShow">Show</button>
  <p> </p>
  <child @child-hide-event="childHide" v-if="childVisible" class="box"></child>
</div>

为了帮助任何人,我遇到了同样的问题。我刚刚将 v-model="" 中的 var 从道具数组更改为数据。记住道具和数据之间的区别,我的情况是改变它不是问题,你应该权衡你的决定。

例如:

<v-dialog v-model="dialog" fullscreen hide-overlay transition="dialog-bottom-transition">

之前:

export default {
    data: function () {
        return {
            any-vars: false
        }
    },
    props: {
            dialog: false,
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}

之后:

export default {
    data: function () {
        return {
            dialog: false
        }
    },
    props: {
            notifications: false,
            sound: false,
            widgets: false
        },
    methods: {
        open: function () {
            var vm = this;

            vm.dialog = true;
        }
    }
}

我想知道为什么当警告有提示时却被其他人错过了

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "visible" (found in component )

尝试根据子组件中接收到的 prop 创建一个计算 属性 作为

computed: {
  isVisible => this.visible
}

并在您的子组件中使用此计算并将更改发送给您的父组件。