vue 复盘总结二

187 阅读4分钟

计算属性和侦听器

computed 计算属性

computed特性

  • 监听依赖值变化,当被调用时,根据相关的计算逻辑,返回计算结果
  • 具有缓存性,当依赖值变化才会执行计算,依赖值未变化则返回上一次计算结果从而提升性能
  • 计算属性必须同步执行,不能执行异步任务

computed 使用

  • 常规用法
<style lang='scss' scoped>
/* @import url(); 引入公共css类 */
.mk-card {
  border-radius: 4px;
  border: 1px solid #ebeef5;
  background-color: #fff;
  overflow: hidden;
  color: #303133;
  &.is-shadow {
    box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
  }
  &--primary {
    .mk-card__header {
      background: #409eff;
      color: #fff;
    }
  }
  &__header {
    padding: 12px 24px;
    border-bottom: 1px solid #ebeef5;
    box-sizing: border-box;
    display: flex;
    justify-content: space-between;
    align-content: center;
  }
  &__content {
    padding: 20px;
  }
}
.mk-list {
  list-style: none;
  text-align: left;
  &__item {
    display: flex;
    align-items: center;
  }

  &__label {
    width: 100px;
    flex-shrink: 0;
  }
  &__text {
    flex: 1;
    padding-left: 12px;
  }
}
</style>
<template>
  <div :class="classObj" style="width:480px">
    <div class="mk-card__header">标题</div>
    <div class="mk-card__content">
      <ul class="mk-list">
        <li class="mk-list__item" v-for="(item,index) in failedList" :key="index">
          <label class="ml-list__label">{{item.name}} :</label>
          <span class="mk-list__text">{{item.score}}</span>
        </li>
      </ul>
    </div>
  </div>
</template>

<script>
export default {
  name: 'MkCard',
  data() {
    //这里存放数据
    return {
      cardType: 'primary',
      isShadow: true,
      addNum: 1,
      list: [
        {
          name: '张三',
          score: 90,
        },
        {
          name: '李四',
          score: 86,
        },
        {
          name: '王五',
          score: 50,
        },
      ],
    }
  },
  //监听属性 类似于data概念
  computed: {
    //依赖响应式数据,返回最终计算结果
    classObj() {  //该函数为get函数,必须有返回值
      return {
        'mk-card': true,
        ['mk-card--' + this.cardType]: this.cardType,
        'is-shadow': this.isShadow,
      }
    },
    //作为过滤器使用
    failedList() {//该函数为get函数,必须有返回值
      return this.list.filter(({ score }) => {
        return score < 60
      })
    },
  },
}
</script>

  • 计算属性传参

计算属性可以通过闭包来传参,当然,这种使用场景使用计算属性比较少,一般都用方法实现了

<template>
  <ul class="mk-list">
    <li class="mk-list__item" v-for="(item,index) in list" :key="index">
      <label class="ml-list__label">{{item.name}} :</label>
      <span class="mk-list__text">{{item.score}}----{{commentFormatter(item)}}</span>
    </li>
  </ul>
</template>
<script>
export default {
  name: '',
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      list: [
        {
          name: '张三',
          score: 96,
          comment: '优秀',
        },
        {
          name: '李四',
          score: 86,
          comment: '良好',
        }
      ],
    }
  },
  //监听属性 类似于data概念
  computed: {
    //计算属性使用闭包传参
    commentFormatter() {
      return function (item) {
        return item.comment
      }
    },
  },
}
</script>

  • 计算属性getter 和 setter的使用

计算属性有get 和 set 两个函数,当依赖发生改变时,触发get函数;当计算属性发生改变的时候,触发set函数,在set函数中不能直接修改计算属性,会导致死循环,可修改计算属性相关的依赖值,从而触发get函数,从而引起计算属性的重计算,返回最新计算结果。

<template>
  <div class>
    <div>
      firstName :
      <input type="text" v-model="firstName" />
    </div>
    <div>
      lastName :
      <input type="text" v-model="lastName" />
    </div>
    <div>
      fullName :
      <input type="text" v-model="fullName" />
    </div>
  </div>
</template>

<script>
export default {
  name: '',
  //import引入的组件需要注入到对象中才能使用
  components: {},
  data() {
    //这里存放数据
    return {
      firstName: 'Foo',
      lastName: 'Bar',
    }
  },
  //监听属性 类似于data概念
  computed: {
    fullName: {
      // getter
      //当依赖发送改变时会执行
      get: function () {
        console.log('====fullName get=======' + this.firstName)
        return this.firstName + ' ' + this.lastName
      },
      // setter
      //当计算属性发生改变时触发
      set: function (newValue) {
        console.log('====fullName set=======' + newValue)
        var names = newValue.split(' ')
        //修改计算属性相关的依赖属性值
        this.firstName = names[0]
        this.lastName = names[names.length - 1]
      },
    },
  },

 
}
</script>

  • 计算属性的cache缓存属性;默认为true(开启缓存)

computed 默认缓存计算值,当cache设置为false时,每次调用会重新计算属性值

<template>
  <div class></div>
</template>

<script>
export default {
  data() {
    //这里存放数据
    return {}
  },
  //监听属性 类似于data概念
  computed: {
    now: {
      cache: true,
      get: function () {
        return Date.now()
      },
    },
  },
  //生命周期 - 挂载完成(可以访问DOM元素)
  mounted() {
    setInterval(() => {
      // 当缓存开关为false的时候,定时器每次打印的时间都是不一样的
      console.log(this.now)
    }, 1000)
  },
}
</script>

案例中,Date.now()是个可变值,但非响应式依赖,即便是变化,也不会触发set重新计算,每次会返回首次计算的值;当设置cache: false时,则不缓存值,每次调用重新计算

==*注意 :如果不希望用缓存,建议使用方法代替,因为计算属性性能开销比较大,需遍历相关依赖,若无缓存,将不可避免多次执行计算属性的getter *==

watch 侦听器

watch特性

  • 观察和响应 Vue 实例上的数据变动,执行相应的回调
  • watch回调中可执行任何逻辑,可执行异步任务,
  • watch侦听器由handler方法、immediate、deep属性组成

watch的使用

  • 常规用法

watch 简写情况下只需传入handle方法

<template>
  <div>
    <div>
      实时搜索 :
      <input type="text" v-model="searchInfo" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'Test',
  data() {
    //这里存放数据
    return {
      searchInfo: '',
    }
  },
  //监控data中的数据变化
  watch: {
    searchInfo: function (newVal, oldVal) {   //handle函数
      console.log('搜索')
    },
  },
}
</script>

完整写法 ( 该处只提供变化的代码片段 )

//监控data中的数据变化
watch: {
    searchInfo: {
      handler: function (val, oldVal) {
        console.log('搜索')
      },
      deep: true,
      immediate: false,
    },
},
  • watch 中handle回调函数使用

可传入对应的方法名

//监控data中的数据变化
watch: {
    searchInfo: 'toSearch',
},
//方法集合
methods: {
    toSearch() {
      console.log('搜索')
    },
},

可传入回调数组

//监控data中的数据变化
watch: {
    searchInfo: [
      function handle1(newVal, oldVal) {
        console.log('===========handle1')
      },
      function handle2(newVal, oldVal) {
        console.log('===========handle2')
      },
      'toSearch',
    ],
},
//方法集合
methods: {
    toSearch() {
      console.log('搜索')
    },
},

image

可执行异步任务

//监控data中的数据变化
watch: {
    searchInfo: function (newVal, oldVal) {   //handle函数
      setTimeout(() => {
        console.log('搜索')
      }, 500)
    },
},

==*注意 :不应该使用箭头函数来定义 watcher 函数,理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例 *==

  • immediate 的用法

默认为fasle,设置为true时,handle回调将会在侦听开始之后被立即调用

watch: {
    searchInfo: {
      handler: function (newVal, oldVal) {
        console.log('搜索')
      },
      immediate: true,
    },
},
  • deep的用法

默认情况下,handler 只监听属性引用的变化,只监听了一层,但改变对象内部的属性是监听不到的。因此只有赋值,引用地址发生变化的时候才会被监听

<template>
  <div>
    <div>
      实时搜索 :
      <input type="text" v-model="searchInfo" />
    </div>

    <div>
      名称 :
      <!-- watchObj内部属性发送改变,但引用地址未发生改变,不会触发侦听器监测 -->
      <input type="text" v-model="watchObj.name" />
      <button @click="assignment">重新赋值</button>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    //这里存放数据
    return {
      watchObj: {
        name: '张三',
        sex: '男',
        age: 36,
      },
    }
  },
  //监控data中的数据变化
  watch: {
    watchObj: {
      handler: function (newVal, oldVal) {
        console.log('变化')
      },
      deep: true,
    },
  },
  //方法集合
  methods: {
    assignment() {
      //赋值后,引用地址发生改变,触发watchObj 侦听器监测
      this.watchObj = {
        name: '张三',
        sex: '男',
        age: 36,
      }
    },
  },
}
</script>

当deep设置为true的时候,监听器会往下层依次遍历,给所有的属性执行 getter/setter,因此,对象或数组任何属性发生改变会触发对应的监听

watch: {
    watchObj: {
      handler: function (newVal, oldVal) {
        console.log('变化')
      },
      deep: true,
    },
},
  • 监听对象的某个属性变化
watch: {
    'watchObj.name': function (newVal, oldVal) {
      console.log('name变化')
    },
},
  • 监听多个值变化

当需要监听多个值变化触发相关业务逻辑的时候,给每个值增加监听器,会出现多次监听,触发多次业务方法,这个时候可以结合计算属性

computed、watch、methods的对比

名称
特点应用场景
computed1、基于响应性依赖来进行计算,具有缓存性
2、具有 get/set两个属性函数
3、必须同步执行,不能执行异步任务
1、适用于一些重复使用数据或复杂的运算,具体缓存性
2、所需数据依赖于其他的数据
watch1、观察和响应 Vue 实例上的数据变动,执行相应的回调
2、回调中可执行任何逻辑,可执行异步任务
3、watch侦听器由handler方法、immediate、deep属性组成
1、数据变化时执行异步或开销较大的操作
methods1、不具备缓存性
2、可执行异步任务
很广泛

如果觉得这篇文章有用,就给颗小心心哟!