问题
I observed that when a normal property that came from data()
and a computed property that is derived from it are passed through an event, the former keeps its reactivity while the latter loses it.
I set up the following test case for it (also as a JSFiddle if you prefer that):
const EventBus = new Vue();
Vue.component('comp', {
data() {
return {
arrData: ['a', 'b']
};
},
computed: {
arrComputed() {
return this.arrData.map((e) => e.toUpperCase());
}
},
template: `
<div>
<div>Original array: {{ arrData }}</div>
<div>Computed array: {{ arrComputed }}</div>
<button @click="remove('a')">Remove a</button>
<button @click="remove('b')">Remove b</button>
<button @click="pass">Pass through event</button>
<button @click="reset">Start over soft</button>
<button @click="resetHard">Start over hard</button>
</div>`,
methods: {
remove(name) {
name = name.toLowerCase();
if(this.arrData.indexOf(name) != -1) {
this.$delete(this.arrData, this.arrData.indexOf(name));
}
},
pass() {
EventBus.$emit('pass', this.arrData, this.arrComputed);
},
reset() {
this.$set(this.arrData, 0, 'a');
this.$set(this.arrData, 1, 'b');
},
resetHard() {
this.arrData = ['a','b'];
}
}
});
Vue.component('othercomp', {
data() {
return {
items1: [],
items2: []
}
},
mounted() {
EventBus.$on('pass', this.receive);
},
template: `
<div>
<div>Original array: {{items1}}</div>
<div>Computed array: {{items2}}</div>
</div>`,
methods: {
receive(items1, items2) {
this.items1 = items1;
this.items2 = items2;
}
}
});
var app = new Vue({
el: '#app',
components:['comp', 'othercomp']
})
<script src="//unpkg.com/vue@latest/dist/vue.js"></script>
<div id="app">
<comp></comp>
<othercomp></othercomp>
</div>
How is a computed different from a normal property so that this difference in behaviour occurs?
I learned from a previous question that passing reactive objects around like this is bad practice and that I should use a getter function instead, however I'd still like to know why this difference occurs.
回答1:
I would not expect this to work. By returning the results of map()
you are passing a copy of the reactive array to the other component. This copy will not react to changes of the original array — it's a totally different array. And even though the computed property will update when arrData
changes, it won't automatically send another event to the second component telling it to update it's data array. I would think you'll need a computed property on both components — you could do this in a DRY fashion with a mixin or filter (which is probably more appropriate in this case).
You could fire the event in the computed function. But this seems pretty hacky to me:
arrComputed() {
let newArray = this.arrData.map((e) => e.toUpperCase());
EventBus.$emit('pass', this.arrData, newArray);
return newArray
}
回答2:
I observed that when a normal property that came from data() and a computed property that is derived from it are passed through an event, the former keeps its reactivity while the latter loses it.
An object variable (an Array is an object) contains a reference (or handle) to the object. When you assign one object variable to another, the handle is copied, and both variables are handles to one object. Object operations on one will be "seen" by the other.
So after
foo = [1];
bar = foo;
foo.push(2);
both foo
and bar
, since they refer to the same object, would be [1, 2]
. Passing values works the same way, but it's easier to illustrate with simple assignments.
It should be clear that
foo = [1];
bar = foo;
foo = [1, 2];
assigns a new handle to foo
, so it no longer references the same array that bar
does.
In your example, arrComputed
creates a new Array object every time arrData
changes. So when you pass arrComputed
, you are passing the handle it created the last time it ran. When arrData
changes, arrComputed
creates a new Array object, but the recipient of the old handle doesn't get that, and the Array associated with its handle is never updated.
来源:https://stackoverflow.com/questions/49283457/difference-between-computed-property-and-normal-data-in-vue-js