简体   繁体   English

Vue.js 更改道具

[英]Vue.js Changing props

I'm a bit confused about how to change properties inside components, let's say I have the following component:我对如何更改组件内部的属性有点困惑,假设我有以下组件:

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

Although it works, it would give the following warning:虽然它有效,但它会给出以下警告:

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.避免直接改变 prop,因为只要父组件重新渲染,该值就会被覆盖。 Instead, use a data or computed property based on the prop's value.相反,使用基于道具值的数据或计算属性。 Prop being mutated: "visible" (found in component )正在变异的道具:“可见”(在组件中找到)

Now I'm wondering what the best way to handle this is, obviously the visible property is passed in when created the component in the DOM: <Foo :visible="false"></Foo>现在我想知道处理这个问题的最佳方法是什么,显然在 DOM 中创建组件时传入了visible属性: <Foo :visible="false"></Foo>

Referencing the code in your fiddle引用小提琴中的代码

Somehow, you should decide on one place for the state to live, not two.不知何故,你应该决定一个国家居住的地方,而不是两个。 I don't know whether it's more appropriate to have it just in the Alert or just in it's parent for your use case, but you should pick one.我不知道将它放在Alert中还是放在它的父级中是否更适合您的用例,但您应该选择一个。

How to decide where state lives如何决定国家在哪里

Does the parent or any sibling component depend on the state?父组件或任何兄弟组件是否依赖于状态?

  • Yes: Then it should be in the parent (or in some external state management)是:那么它应该在父级(或在某些外部状态管理中)
  • No: Then it's easier to have it in the state of the component itself否:那么更容易让它处于组件本身的状态
  • Kinda both: See below两者兼而有之:见下文

In some rare cases, you may want a combination.在极少数情况下,您可能需要组合。 Perhaps you want to give both parent and child the ability to hide the child.也许你想让父母和孩子都能够隐藏孩子。 Then you should have state in both parent and child (so you don't have to edit the child's props inside child).然后你应该在父母和孩子中都有状态(所以你不必在孩子里面编辑孩子的道具)。

For example, child can be visible if: visible && state_visible , where visible comes from props and reflects a value in the parent's state, and state_visible is from the child's state.例如,child 可以在以下情况下可见: visible && state_visible ,其中visible来自 props 并反映父级状态中的值,而state_visible来自子级状态。

I'm not sure if this is the behavour that you want, but here is a snippet.我不确定这是否是您想要的行为,但这里有一个片段。 I would kinda assume you actually want to just call the toggleAlert of the parent component when you click on the child.我有点假设您实际上只想在单击子组件时调用父组件的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>

If the prop is only useful for this child component , give the child a prop like initialVisible , and a data like mutableVisible , and in the created hook (which is called when the component's data structure is assembled), simply this.mutableVisible = this.initialVisible .如果 prop 只对这个子组件有用,则给孩子一个类似于initialVisibleprop ,和一个类似于mutableVisibledata ,并在created钩子中(在组装组件的数据结构时调用),只需this.mutableVisible = this.initialVisible

If the prop is shared by other children of the parent component , you'll need to make it the parent's data to make it available for all children.如果 prop 被父组件的其他子组件共享,您需要将其设为父组件的data以使其可用于所有子组件。 Then in the child, this.$emit('visibleChanged', currentVisible) to notify the parent to change visible .然后在 child 中, this.$emit('visibleChanged', currentVisible)通知 parent 改变visible In parent's template, use <ThatChild ... :visibleChanged="setVisible" ...> .在父母的模板中,使用<ThatChild ... :visibleChanged="setVisible" ...> Take a look at the guide: https://v2.vuejs.org/v2/guide/components.html看看指南: https ://v2.vuejs.org/v2/guide/components.html

According to the Vue.js component doc :根据Vue.js 组件文档

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.这就是 Vue 的自定义事件系统的用武之地。

Use $emit('my-event) from the child to send an event to the parent.使用来自孩子$emit('my-event)将事件发送给父母。 Receive the event on the child declaration inside the parent with v-on:my-event (or @my-event ).使用v-on:my-event (或@my-event )在父级内的子级声明中接收事件。

Working example:工作示例:

 // 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>

After a read of your latest comments it seems that you are concerned about having the logic to show/hide the alerts on the parent.在阅读了您的最新评论后,您似乎担心有逻辑来显示/隐藏父级警报。 Therefore I would suggest the following:因此,我建议如下:

parent父母

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

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

Then on the child alert you would $watch the value of the prop and move all logic into the alert:然后在子警报上,您将 $watch 道具的值并将所有逻辑移动到警报中:

child (alert)孩子(警告)

# 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()
  },
  ...
},
...

To help anybody, I was facing the same issue.为了帮助任何人,我面临着同样的问题。 I just changed my var that was inside v-model="" from props array to data.我刚刚将 v-model="" 中的 var 从 props 数组更改为数据。 Remember the difference between props and data, im my case that was not a problem changing it, you should weight your decision.记住道具和数据之间的区别,我的情况是改变它不是问题,你应该权衡你的决定。

Eg:例如:

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

Before:前:

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;
        }
    }
}

After:后:

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

            vm.dialog = true;
        }
    }
}

Maybe it looks like on hack and violates the concept of a single data source, but its work) This solution is creating local proxy variable and inherit data from props.也许它看起来像 hack 并且违反了单一数据源的概念,但它的工作原理)这个解决方案是创建本地代理变量并从 props 继承数据。 Next work with proxy variable.接下来使用代理变量。

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);
                }
            });
        }
    }
});

use component and pass props使用组件并传递道具

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

I wonder why it is missed by others when the warning has a hint我想知道为什么警告有提示时会被其他人错过

Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders.避免直接改变 prop,因为只要父组件重新渲染,该值就会被覆盖。 Instead, use a data or computed property based on the prop's value.相反,使用基于道具值的数据或计算属性 Prop being mutated: "visible" (found in component )正在变异的道具:“可见”(在组件中找到)

Try creating a computed property out of the prop received in the child component as尝试从子组件中收到的 prop 创建一个计算属性

computed: {
  isVisible => this.visible
}

And use this computed in your child component as well as to emit the changes to your parent.并在您的子组件中使用此计算结果,并将更改发送给您的父组件。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM