2

我观察到,当一个来自的普通属性data()和一个派生自它的计算属性通过事件传递时,前者保持其反应性,而后者失去它。

我为它设置了以下测试用例(如果您愿意,也可以作为 JSFiddle):

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>

计算结果与正常属性有何不同,从而导致这种行为差异发生?

我从之前的问题中了解到,像这样传递反应对象是不好的做法,我应该使用 getter 函数,但是我仍然想知道为什么会出现这种差异。

4

2 回答 2

1

我观察到,当来自 data() 的普通属性和派生自它的计算属性通过事件传递时,前者保持其反应性,而后者失去它。

对象变量(数组是对象)包含对对象的引用(或句柄)。当您将一个对象变量分配给另一个对象变量时,将复制句柄,并且两个变量都是一个对象的句柄。对一个对象的操作将被另一个“看到”。

所以之后

foo = [1];
bar = foo;
foo.push(2);

foo因为它们指的是同一个对象bar,所以都是。传递值的工作方式相同,但用简单的赋值更容易说明。[1, 2]

应该清楚的是

foo = [1];
bar = foo;
foo = [1, 2];

为 分配一个新句柄foo,因此它不再引用相同的数组bar

在您的示例中,arrComputed每次arrData更改时都会创建一个新的 Array 对象。因此,当您通过时arrComputed,您将传递它上次运行时创建的句柄。当arrData更改时,arrComputed创建一个新的 Array 对象,但旧句柄的接收者没有得到它,并且与其句柄关联的 Array 永远不会更新。

于 2018-03-14T18:21:56.930 回答
1

我不希望这会奏效。通过返回您的结果,map()您将反应数组的副本传递给其他组件。此副本不会对原始数组的更改做出反应——它是一个完全不同的数组。即使计算属性在更改时更新arrData,它也不会自动向第二个组件发送另一个事件,告诉它更新它的数据数组。我认为您需要在两个组件上都有一个计算属性——您可以使用 mixin 或过滤器以 DRY 方式执行此操作(在这种情况下可能更合适)。

可以在计算函数中触发事件。但这对我来说似乎很老套:

arrComputed() { 
   let newArray = this.arrData.map((e) => e.toUpperCase());
   EventBus.$emit('pass', this.arrData, newArray);
   return newArray
}
于 2018-03-14T17:22:24.370 回答