传统方式做运算

方式1:直接在插值表达式里面做运算

  1. <div id="app">
  2. 姓:<input type="text" v-model="firstName">
  3. <br>
  4. 名:<input type="text" v-model="lastName">
  5. <br>
  6. 全名:<span>{{firstName.slice(0,3)}}-{{lastName}}</span>
  7. </div>
  8. </body>
  9. <script>
  10. Vue.config.productionTip = false;
  11. const vm = new Vue({
  12. el: '#app',
  13. data: {
  14. firstName: '张',
  15. lastName: '三'
  16. }
  17. });
  18. </script>

Vue不推荐在插值表达式里加入过多的运算逻辑。

方式2:计算逻辑配置成method,然后将method配置到插值表达式位置调用

  1. <body>
  2. <div>
  3. methods实现
  4. </div>
  5. <div id="app">
  6. 姓:<input type="text" v-model="firstName">
  7. <br>
  8. 名:<input type="text" v-model="lastName">
  9. <br>
  10. <!-- 在插值表达式里调用method -->
  11. 全名:<span>{{fullName()}}</span>
  12. <br>
  13. </div>
  14. </body>
  15. <script>
  16. Vue.config.productionTip = false;
  17. const vm = new Vue({
  18. el: '#app',
  19. data: {
  20. firstName: '张',
  21. lastName: '三'
  22. },
  23. methods: {
  24. fullName() {
  25. // 因为method的this和data的this都是指的Vue实例vm,所以此处可以获取到vm的firstName、lastName
  26. return this.firstName + "-" + this.lastName;
  27. }
  28. },
  29. });
  30. </script>

计算属性

  1. <body>
  2. <div>
  3. 计算属性实现
  4. </div>
  5. <div id="app">
  6. 姓:<input type="text" v-model="firstName">
  7. <br>
  8. 名:<input type="text" v-model="lastName">
  9. <br>
  10. 全名:<span>{{fullName}}</span>
  11. <br>
  12. 第二次读取全名:<span>{{fullName}}</span>
  13. <br>
  14. </div>
  15. </body>
  16. <script>
  17. Vue.config.productionTip = false;
  18. const vm = new Vue({
  19. el: '#app',
  20. data: {
  21. firstName: '张',
  22. lastName: '三'
  23. },
  24. // 计算属性
  25. computed: {
  26. fullName: {
  27. get() { // 计算属性需要有个getter,在该方法中编写计算逻辑
  28. console.log('fullName被读取了');
  29. console.log(this); // 此处的this是vm
  30. return this.firstName + "-" + this.lastName;
  31. }
  32. }
  33. }
  34. });
  35. </script>

计算属性是计算出来的,在vm._data中不存在。

计算属性在通过getter获取完值之后会缓存下来,如果内容不变,下次再读取时将不再进行计算。但是当其依赖的变量发生变化时,计算属性的getter会再次被调用。

计算属性如果只是为了读取,那么只编写getter即可。但是如果需要修改计算属性,那么还需要编写setter:

(一般情况下,计算属性是计算完用来读取的,很少有修改计算属性的)

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. firstName: '张',
  5. lastName: '三'
  6. },
  7. // 计算属性
  8. computed: {
  9. fullName: {
  10. get() {
  11. console.log('fullName被读取了');
  12. console.log(this); // 此处的this是vm
  13. return this.firstName + "-" + this.lastName;
  14. },
  15. // setter非必须,只有fullName会被修改时才需要该方法
  16. set(value) {
  17. console.log('setter方法执行了:' + value);
  18. }
  19. }
  20. }
  21. });

当计算属性只有getter时,可以进行简写:

  1. computed: {
  2. // fullName属性只有getter,可以直接简写成fullName(),表示就是该属性的getter
  3. fullName() {
  4. console.log('fullName被读取了');
  5. console.log(this); // 此处的this是vm
  6. return this.firstName + "-" + this.lastName;
  7. }
  8. }

监视属性(侦听属性)

  1. 通过vm对象的$watch()或watch配置来监视指定的属性
  2. 当属性变化时,回调函数自动调用,在函数内部进行计算

@click等事件绑定方法可以直接编写一些简单的实现:

  1. <body>
  2. <div id="app">
  3. <button @click="changeWeather">切换天气</button>
  4. </div>
  5. </body>
  6. <script>
  7. Vue.config.productionTip = false;
  8. const vm = new Vue({
  9. el: '#app',
  10. data: {
  11. isHot: true
  12. },
  13. methods: {
  14. changeWeather() {
  15. this.isHot = !this.isHot;
  16. }
  17. },
  18. })
  19. </script>

可以简写为:

  1. <!-- 直接在绑定事件上编写简单的语句 -->
  2. <button @click="isHot = !isHot">切换天气</button>

此时,如果想监控isHot属性的修改,可以使用watch配置监视:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. isHot: true
  5. },
  6. // .......
  7. // 配置监视
  8. watch: {
  9. isHot: { // 要监视的属性
  10. handler(newValue, oldValue) { // 当isHot发生改变时被调用
  11. console.log(`isHot改变了,从${oldValue}变成了${newValue}`);
  12. }
  13. }
  14. }
  15. })

其他配置项:

  1. // 配置监视
  2. watch: {
  3. isHot: { // 要监视的属性
  4. handler(newValue, oldValue) { // 当isHot发生改变时被调用
  5. console.log(`isHot改变了,从${oldValue}到${newValue}`);
  6. },
  7. immediate: true // 页面打开就直接先执行一次handler方法,默认false
  8. }
  9. }

计算属性也是可以被watch监测的。

通过vm也可以配置监视:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. isHot: true
  5. },
  6. computed: {
  7. weather() {
  8. return this.isHot ? "炎热" : "凉爽"
  9. }
  10. },
  11. methods: {
  12. changeWeather() {
  13. this.isHot = !this.isHot;
  14. }
  15. }
  16. });
  17. // 通过vm的$watch方法进行监视
  18. // 参数1,要监视的属性
  19. // 参数2,配置项
  20. vm.$watch('isHot', {
  21. handler(newValue, oldValue) { // 当isHot发生改变时被调用
  22. console.log(`isHot改变了,从${oldValue}到${newValue}`);
  23. },
  24. immediate: true
  25. });

将被监视的属性变化时,回调函数自动调用,进行相关操作。

监视的属性必须存在,才能进行监视。

vm.$watch()方式监视的属性不存在时不报错

监视多级结构中某个属性的变化:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. isHot: true,
  5. numbers: {
  6. a: 1,
  7. b: 1
  8. }
  9. },
  10. watch: {
  11. // 监视多级结构中某个属性的变化
  12. 'numbers.a': { // 监视 numbers里面的a属性的变化
  13. handler(newValue, oldValue) {
  14. console.log('a被改变了')
  15. }
  16. }
  17. }
  18. });

监视对象的里面的属性变化:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. isHot: true,
  5. numbers: {
  6. a: 1,
  7. b: 1
  8. }
  9. },
  10. // 配置监视
  11. watch: {
  12. numbers: {
  13. handler() {
  14. console.log('numbers改变了');
  15. },
  16. // 开启深度监视,默认false
  17. // 如果不开启深度监视,只有numbers这个对象本身变化了才能监视到。numbers里面的a、b属性值变化无法检测到
  18. // 开启了深度监视后,numbers里面的属性值变化也可以被监视到
  19. deep:true
  20. }
  21. }
  22. });

监视属性简写:

当配置项里只有handler()时,就可以使用简写形式

  1. watch: {
  2. isHot(newValue, oldValue) {
  3. console.log(`isHot改变了,从${oldValue}到${newValue}`);
  4. }
  5. }

vm.$watch形式监视的简写:

  1. vm.$watch('isHot', function (newValue, oldValue) {
  2. console.log(`isHot改变了,从${oldValue}到${newValue}`);
  3. });

计算属性和监视属性对比

监视属性可以开启异步。

例如:

监视属性可以使用以下写法:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. firstName:'张',
  5. lastName:'三',
  6. fullName:'张-三'
  7. },
  8. watch: {
  9. firstName(newValue) {
  10. // firstName修改后,延迟1秒再修改fullName的值
  11. setTimeout(() => {
  12. this.fullName = newValue + "-" + this.lastName;
  13. }, 1000);
  14. }
  15. }
  16. });

计算属性因为是return,所以不能开启异步:

  1. const vm = new Vue({
  2. el: '#app',
  3. data: {
  4. firstName: '张',
  5. lastName: '三'
  6. },
  7. computed: {
  8. fullName: {
  9. get() {
  10. // 此处这么写是错误的,运行不出来的
  11. // 此处的return 会将返回值返回给setTimeout的回调函数,不能返回给get
  12. setTimeout(() => {
  13. return this.firstName + "-" + this.lastName;
  14. }, 1000);
  15. }
  16. }
  17. }
  18. });

还需要注意的是:在监视属性中,使用setTimeout延迟时,里面的回调函数必须写成箭头函数的形式:

  1. watch: {
  2. firstName(newValue) {
  3. // 此处的setTimeout里面回调函数必须写成箭头函数
  4. setTimeout(() => {
  5. this.fullName = newValue + "-" + this.lastName;
  6. }, 1000);
  7. }
  8. }

因为setTimeout里面的回调函数不是VUE管理的函数,而是js引擎管理的。所以如果里面不写成箭头函数,那么里面的回调函数的this就将指向window。里面的回调函数写成箭头函数后,箭头函数没有this,向外找的时候就会找到vue实例的this。

总结:

所有被Vue管理的函数,最好写成普通函数,这样this指向的才是vm或组件实例对象。

不是vue管理的函数(例如定时器的回调函数、ajax的回调函数、promise的回调函数),最好写成箭头函数,这样this指向的才是vm或组件实例对象。