简体   繁体   English

VueJS 嵌套组件不会在操作时更新属性

[英]VueJS nested component doesn't update property on action

This example of the official guide works properly (of course).这个官方指南的例子可以正常工作(当然)。

Then I tried by my own by making it a component, it also works.然后我自己尝试将其作为一个组件,它也可以工作。

But once nested in a parent component, the message can not be reversed, and I can't figure out why.但是一旦嵌套在父组件中,消息就无法反转,我不知道为什么。

Vue.component('todo-item', {
  props: ['todo'],
  template: '<div>' +
            '<li>{{ todo.text }}</li>' +
            '<button v-on:click="reverseMessage">Reverse Message</button>' +
            '</div>',
  methods: {
  reverseMessage: function () {
    var reversed = this.todo.text.split('').reverse().join('');
    console.log('tried with ' + reversed);
    this.todo.text = reversed;
  }
 }
});

Here is the JSFiddle : https://jsfiddle.net/37y1ukjh/这是 JSFiddle: https ://jsfiddle.net/37y1ukjh/

Look at this fiddle .看看这个小提琴

This is because ofone-way data flow .这是因为单向数据流 You shouldn't attempt to modify a prop's value.您不应该尝试修改道具的值。

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.所有的 props 在子属性和父属性之间形成一个单向向下的绑定:当父属性更新时,它会向下流向子属性,但不会反过来。 This prevents child components from accidentally mutating the parent's state, which can make your app's data flow harder to reason about.这可以防止子组件意外改变父组件的状态,这会使您的应用程序的数据流更难推理。

Couple options:几个选项:
Define a local data property that uses the prop's initial value as its initial and use that local property定义一个使用 prop 的初始值作为其初始值的本地数据属性并使用该本地属性

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

Define a computed property that is computed from the prop's value and use that computed property定义一个根据 prop 的值计算的计算属性并使用该计算属性

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

Another option is to use an event to fire a parent method to change the prop but doesn't seem to be necessary in this instance.另一种选择是使用事件触发父方法来更改道具,但在这种情况下似乎没有必要。

The other answers have noted that the reason your code doesn't work is because you shouldn't mutate properties.其他答案指出,您的代码不起作用的原因是您不应该改变属性。 However, the reason the code in the fiddle doesn't work is because eee is passed a static array.但是,小提琴中的代码不起作用的原因是因为eee传递了一个静态数组。 In other words, since the value for eee is bound to an object hard coded into the property, that value is never converted into a reactive object.换句话说,由于eee的值绑定到硬编码到属性中的对象,因此该值永远不会转换为反应对象。

Had you instead passed eee a data property, the array would have been converted into a reactive array and your code would work as you expect.如果您改为传递eee数据属性,则该数组将被转换为反应数组,并且您的代码将按预期工作。

 Vue.component('todo-item', { props: ['todo'], template: '<div>' + '<li>{{ todo.text }}</li>' + '<button v-on:click="reverseMessage">Reverse Message</button>' + '</div>', methods: { reverseMessage: function() { var reversed = this.todo.text.split('').reverse().join(''); this.todo.text = reversed; } } }); Vue.component('todo-list', { props: ['eee'], template: '<div><todo-item' + ' v-for="item in eee"' + ' v-bind:todo="item"' + ' v-bind:key="item.id">' + ' </todo-item></div>', }); var app7 = new Vue({ el: '#app-7', data: { todos: [{ id: 0, text: 'Vegetables' }, { id: 1, text: 'Cheese' }, { id: 2, text: 'Whatever else humans are supposed to eat' }] } })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.js"></script> <div id="app-7"> <ol> <todo-list v-bind:eee="todos"></todo-list> </ol> </div>

Note the change to the template:注意模板的变化:

<todo-list v-bind:eee="todos"></todo-list>

And the Vue:和 Vue:

var app7 = new Vue({
  el: '#app-7',
  data:{
    todos: [{ id: 0, text: 'Vegetables' },{ id: 1, text: 'Cheese' },{ id: 2, text: 'Whatever else humans are supposed to eat' }]
  }
})

Thats all I changed in the code to make it work as expected.这就是我在代码中所做的所有更改,以使其按预期工作。

This is how reactive objects and arrays passed as properties work in Vue.这就是在 Vue 中作为属性传递的响应式对象和数组的工作方式。 Fromthe documentation :文档中

Note that objects and arrays in JavaScript are passed by reference, so if the prop is an array or object, mutating the object or array itself inside the child will affect parent state.请注意,JavaScript 中的对象和数组是通过引用传递的,因此如果 prop 是数组或对象,则在子对象内部改变对象或数组本身会影响父状态。

Vue will not complain if you mutate an object or array in this way.如果您以这种方式改变对象或数组,Vue不会抱怨。 Where Vue will complain is if you attempted to set the object or array to a completely different object or array.如果您试图将对象或数组设置为完全不同的对象或数组,Vue 会抱怨的地方。

Whether that behavior is desired is a design decision.是否需要这种行为是设计决策。 Sometimes you may want to take advantage of it and other times not.有时您可能想利用它,有时则不想。

The issue is that you are attempting to modify the props of the component.问题是您正在尝试修改组件的道具。 Props should not be modified : instead, you can:不应修改道具:相反,您可以:

  1. Store it as a data property, and将其存储为数据属性,并且
  2. Modify it (in this case, reverse it) whenever the button is clicked.每当单击按钮时修改它(在这种情况下,反转它)。

Although this approach may sound unnecessarily verbose, the advantage here is that you simply use the text from the component's data in the template, and it will be reactive :) changes to the data will be reflected in the DOM itself.虽然这种方法听起来可能不必要地冗长,但这里的优点是您只需使用模板中组件数据中的文本,它会是反应性的:) 对数据的更改将反映在 DOM 本身中。

Vue.component('todo-item', {
    props: ['todo'],
    data: function() {
        // Store inherited props in data
        return {
            text: this.todo.text
        };
    },
    template: '<div>' +
                '<li>{{ text }}</li>' +
                '<button v-on:click="reverseMessage">Reverse Message</button>' +
              '</div>',
    methods: {
      reverseMessage: function () {
        // Reverse text in data and overwrite it
        this.text = this.text.split('').reverse().join('');
      }
    }
  });

See example here: https://jsfiddle.net/teddyrised/37y1ukjh/3/ , or the proof-of-concept code snippet below:请参阅此处的示例: https ://jsfiddle.net/teddyrised/37y1ukjh/3/,或下面的概念验证代码片段:

 Vue.component('todo-item', { props: ['todo'], data: function() { return { text: this.todo.text }; }, template: '<div>' + '<li>{{ text }}</li>' + '<button v-on:click="reverseMessage">Reverse Message</button>' + '</div>', methods: { reverseMessage: function () { this.text = this.text.split('').reverse().join(''); } } }); Vue.component('todo-list', { props: ['eee'], template: '<div><todo-item' + ' v-for="item in eee"' + ' v-bind:todo="item"' + ' v-bind:key="item.id">' + ' </todo-item></div>' }); var app7 = new Vue({ el: '#app-7' })
 <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script> <div id="app-7"> <ol> <todo-list v-bind:eee="[{ id: 0, text: 'Vegetables' },{ id: 1, text: 'Cheese' },{ id: 2, text: 'Whatever else humans are supposed to eat' }]"> </todo-list> </ol> </div>

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

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