VueJs,计算属性和观察者之间的区别?

ser*_*kan 39 vue.js vuejs2

在Vue.js文档中有一个如下例子:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

上面的代码是必要的和重复的.将其与计算属性版本进行比较:

var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})
Run Code Online (Sandbox Code Playgroud)

观察者比计算属性更合适的情况是什么?我该如何决定选择哪个?文档一直说它更"通用",但并没有真正实现其目的.

Amr*_*pal 41

计算属性

计算属性样本:

computed: {
   val () {
     return this.someDataProperty * someOtherVariable
   }
}
Run Code Online (Sandbox Code Playgroud)

这段特殊的代码是做什么的?

  1. 它创建了一个val为组件命名的属性(在原型<vueInstanece>.hasOwnProperty('val')上将显示false).

  2. 它有一个依赖树,在这种情况下由反应属性(数据属性,其他计算属性)组成:this.someDataProperty这意味着依赖关系发生变化的时刻,将重新计算计算属性.

  3. 虽然辩论过,但不能将争论传递给它.所以像

    computed: {
      val (flag) {
        return (flag === 1) 
          ? this.someDataProperty * someOtherVariable 
          : this.someDataProperty * 5
        }
    }
    
    Run Code Online (Sandbox Code Playgroud)

    无法做到

[编辑]请参阅:https://vuejs.org/v2/guide/computed.html#Computed-Setter

守望者

观察者样本:

watch: {
   val (n, o) {
     console.log(n, o)
   }
}
Run Code Online (Sandbox Code Playgroud)
  1. 它不会创建任何新属性,但会监视对被动属性的更改.

  2. 仅监视一个特定属性,与计算任何依赖属性更改可能导致重新计算的属性不同.

  3. 有新旧价值的论据.


因此,如果符合以下条件,计算属性将成为可能:

您想要一个始终依赖于其他属性的属性.就像模板的文本格式一样,这甚至是代码中的示例.

或者减少变量长度,因为这很常见:

this.$store.state.someProperty.someNestedProperty.someDeeplyNestedProperty
Run Code Online (Sandbox Code Playgroud)

可以简化为:

computed: {
  someDeeplyNestedProperty () {
     return this.$store.state.someProperty.someNestedProperty.someDeeplyNestedProperty
  }
}
Run Code Online (Sandbox Code Playgroud)

不仅减少可变大小,每次商店更新,您将拥有最新的价值someDeeplyNestedProperty.


看守,如果你想看看一个反应性质已经改变,以良好的价值,知道你已经准备好要执行的操作是有用的.

喜欢:

watch: {
  somethingSelected() {
    this.router.push('someOtherRoute')
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 在 OP 的情况下, watch 设置在两个数据属性上,这两个属性都是字符串,所以我的意思不清楚。需要明确的是,您可以查看 N 个数据属性,但如果其中一个是对象示例:`person: { name: "Jack", age: 30, friends: [{name: "Sally", age:21}] } ` 那么对 person 的观察将不会观察对象整个深度内的变化,您可以观察 `person.name` 或 `person.age` 或 `person.friends` (2认同)

Lin*_*org 22

计算属性具有非常特定的用途:组合从其他数据派生的新数据.只要您拥有某些数据并在模板中使用它之前需要对其进行转换,过滤或以其他方式对其进行操作,就会使用它们.

计算属性总是必须返回一个值,不应该有任何副作用,并且它们必须是同步的.

因此,在某些情况下计算属性对您没有帮助,例如:您的组件接收到prop,并且每当prop更改时,您的组件都必须发出ajax请求.为此,你需要一个观察者.

监视器与计算属性一样频繁使用,因此您应该始终考虑计算属性是否可以解决您的问题,并且如果不是这种情况,则只考虑观察者(或有时是方法).


Hum*_*mad 11

Vue.js是被动的

这意味着它能够对用户输入和数据更改等内容做出反应.我建议阅读反应系统,以便更好地了解Vue在观察到数据变化时使用的机制.有三种主要方法可以让您的组件利用Vue的反应特性.这些是方法,计算属性和观察者.如果没有仔细审查,这些选项可能看似可以互换(在某些方面它们是可以的),但每个选项都有其最佳用例场景.为了帮助说明这些示例,我将制作一个小型评分应用程序,允许教师为班级中的学生输入测试成绩,查看平均成绩并设置脚手架以进行自动保存功能.

方法

TL; DR - 当您想要更改组件的状态或发生的事件时,使用方法,该事件不一定与要变异的实例数据相关.方法可以接受参数但不跟踪任何依赖性.当您使用方法时,它通常会在组件中创建一些副作用,并且每次重新加载组件时都会运行方法.这意味着如果UI经常更新,则此方法(以及组件上的任何其他方法)也将运行.这可能会导致性能问题或UI中的延迟.

以下是我们评分应用的开始.我知道,没有验证或任何东西,它不漂亮.我们的数据对象(学生姓名和分数)中有一小组测试.我们可以使用一种方法将另一个测试对象添加到我们的数据属性'tests'中.

new Vue({
  el: "#app",
  data: {
    newTest: {
      studentName: '',
      score: 0
    },
    tests: [{
      studentName: "Billy",
      score: 76
    }, {
      studentName: "Suzy",
      score: 85
    }, {
      studentName: "Johnny",
      score: 89
    }, {
      studentName: "Emma",
      score: 93
    }]
  },
  methods: {
    addTestScore: function() {
      this.tests.push({
        studentName: this.newTest.studentName,
        score: this.newTest.score
      });
      this.newTest.studentName = '';
      this.newTest.score = 0;
    }
  }
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

<body>
  <div id="app">
    <ul>
      <li v-for="test in tests">
        {{test.studentName}} - {{test.score}}
      </li>
    </ul>
    <span>Student</span>
    <input v-model="newTest.studentName">
    <span>Score</span>
    <input v-model="newTest.score">
    <button @click="addTestScore">Add </button>
  </div>
</body>
Run Code Online (Sandbox Code Playgroud)

计算属性

TL; DR - 当您想要改变依赖于另一个正在更改的属性的属性时,请使用计算属性.计算属性通常依赖于其他数据属性.对依赖属性的任何更改都将触发计算属性的逻辑.计算属性根据其依赖项进行高速缓存,因此只有在依赖项发生更改时才会重新运行.(例如,返回新Date()的计算属性将永远不会重新运行,因为逻辑永远不会运行超过1次.)默认情况下,计算属性是getter,但如果需要可以设置setter函数来实现类似的功能.

在我们的评分应用中,我们希望在输入更多数据时跟踪平均测试分数.让我们添加一个名为"average"的计算属性,它将返回数据集中测试的平均分数.每当我们添加另一个测试分数时,"平均"计算属性都会更新.

new Vue({
  el: "#app",
  data: {
    newTest: {
      studentName: '',
      score: 0
    },
    tests: [{
      studentName: "Billy",
      score: 76
    }, {
      studentName: "Suzy",
      score: 85
    }, {
      studentName: "Johnny",
      score: 89
    }, {
      studentName: "Emma",
      score: 93
    }]
  },
  computed: {
    average: function() {
      var sum = this.tests.reduce(function(acc, test) {
        return acc + Number(test.score);
      }, 0);
      return (sum / this.tests.length).toFixed(2);
    }
  },
  methods: {
    addTestScore: function() {
      this.tests.push({
        studentName: this.newTest.studentName,
        score: this.newTest.score
      });
      this.newTest.studentName = '';
      this.newTest.score = 0;
    }
  }
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>

<body>
  <div id="app">
    <span>Average Score: {{average}}</span>
    <ul>
      <li v-for="test in tests">
        {{test.studentName}} - {{test.score}}
      </li>
    </ul>
    <span>Student</span>
    <input v-model="newTest.studentName">
    <span>Score</span>
    <input v-model="newTest.score">
    <button @click="addTestScore">Add</button>
  </div>
</body>
Run Code Online (Sandbox Code Playgroud)

WATCHERS

TL; DR - 当您需要在特定数据属性上发生更改时执行某些逻辑时使用观察程序.被监视的属性仅对一个属性起作用.当您希望执行异步或昂贵的操作以响应更改的数据时,这非常有用.请记住,观察者仅在特定数据属性发生更改时才会更改.

让我们假装我们的小评分应用程序的最终用户是一个有300个测试评级的教授.这可能需要很长时间.如果我们的最终用户到达测试堆的末尾并忘记手动点击保存,则自动保存功能会很好.在我们的代码中,我们将一个观察者添加到我们之前创建的计算属性"average"中.每当它被改变时(由于添加了新的测试分数并且平均值被更新),让我们调用一个新的"自动保存"方法,该方法可用于调用API并保存我们的测试分数.

new Vue({
  el: "#app",
  data: {
    newTest: {
      studentName: '',
      score: 0
    },
    tests: [{
      studentName: "Billy",
      score: 76
    }, {
      studentName: "Suzy",
      score: 85
    }, {
      studentName: "Johnny",
      score: 89
    }, {
      studentName: "Emma",
      score: 93
    }]
  },
  watch: {
    average: function() {
      this.autosave();
    }
  },
  computed: {
    average: function() {
      var sum = this.tests.reduce(function(acc, test) {
        return acc + Number(test.score);
      }, 0);
      return (sum / this.tests.length).toFixed(2);
    }
  },
  methods: {
    addTestScore: function() {
      this.tests.push({
        studentName: this.newTest.studentName,
        score: this.newTest.score
      });
      this.newTest.studentName = '';
      this.newTest.score = 0;
    },
    autosave: function() {
    //pretend we are calling our backend to save the data
      console.log('calling api, saving data');
    }
  }
});
Run Code Online (Sandbox Code Playgroud)
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>

<body>
  <div id="app">
    <span>Average Score: {{average}}</span>
    <ul>
      <li v-for="test in tests">
        {{test.studentName}} - {{test.score}}
      </li>
    </ul>
    <span>Student</span>
    <input v-model="newTest.studentName">
    <span>Score</span>
    <input v-model="newTest.score">
    <button @click="addTestScore">Add</button>
  </div>
</body>
Run Code Online (Sandbox Code Playgroud)