计算属性、监听器和过滤器

计算属性、监听器和过滤器

1. 计算属性computed

1-1 计算属性

计算属性 computed 选项在 options 中定义,计算属性会在当其依赖属性的值发生变化时,属性的值会自动更新,与之相关的 DOM 元素也会同步更新

依赖属性的值指的是 data 中定义的数据

计算属性本质是一个方法,只不过在使用计算属性时,把计算属性的名称直接作为属性来使用,并不会把计算属性作为一个方法去调用

{ [key: string]: Function | { get: Function, set: Function } }

**Tips:**计算属性的调用不能使用括号

<div id="app">
  <input type="text" v-model="title">
  <!-- 计算属性调用时不使用括号() -->
  <h3>{{getTitle}}</h3>
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        // 计算属性响应式 当data数据改变时计算属性则会改变
        title: 'Hello Computed'
      }
      // 计算属性通过computed选项定义
    },
    computed: {
      // 两种形式的值
      // 1. {key: function} key为字符串
      // 2. {get: function, set: function}
        getTitle() {
          // 初始化执行
          console.log('computed 执行')
          return this.title
        }
      }
  })
</script>

1-2 计算属性缓存vs方法

  • 计算属性是基于它的响应式依赖进行缓存的。只有在计算属性的相关响应式依赖发生改变时才会重新求值。这就意味着只要 data 里的相应依赖的数据没有发生改变,那个多次访问计算属性里相关的函数会立即返回之前的计算结果
  • 定义在 methods 中的方法,每当触发重新渲染时,无论属性是否发生改变,调用方法将总会再次执行函数

计算属性相比较于方法更加优化,并不是所有情况下都使用计算属性,在触发事件时选择对应的方法比较好

计算属性一般在数据量比较大、比较耗时、性能开销比较大的情况下使用,只有在虚拟 DOM 和真实 DOM 不同的情况下才会执行

<div id="app">
  <h3>{{getValue}}</h3>
  <button type="button" @click="getTitle">修改title</button>
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        title: 'Hello Computed'
      }
    },
    computed: {
      getValue() {
        // 初始加载时执行一次
        // 当依赖项data中数据发生改变时执行
        console.log('执行 getValue')
        return this.title;
      }
    },
    methods: {
      getTitle() {
        // 每次调用方法都会执行
        console.log('执行 getTitle')
        this.title = 'Hello Vue'
      }
    }
  })
</script>

1-3 计算属性的get与set

每个计算属性都会有 gettersetter 用来获取属性值和设置属性值,在 Vue 中计算属性默认只有 getter,不过在需要时你也可以提供一个 setter

所有 gettersetterthis 上下文自动地绑定为 Vue 实例

  • get —— 初次读取时会执行一次,访问依赖中的数据时会执行,当依赖的数据发生改变时会被再次执行
  • set —— 用于对 get 中使用到的数据进行操作,计算属性值得修改只能通过 set 方法
    • 通过改变依赖项的数据来响应修改计算属性的值
    • 通过该方法去设置依赖项 data 中的数据然后计算属性响应更新 get 中返回的计算属性值
<div id="app">
  <div>first:<input type="text" v-model="first"></div>
  <div>last:<input type="text" v-model="last"></div>
  <h3>{{getValue}}</h3>
  计算属性:<input type="text" v-model="getValue">
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        first: 'hello',
        last: 'computed'
      }
    },
    // 第二种定义计算属性方式
    // 通过get与set
    computed: {
      getValue: {
        // 用于返回计算属性的值
        get() {
          // get与set中this指向Vue实例对象
          return this.first + ' ' + this.last
        },
        // 用于设置计算属性的值
        set(value) {
          // set可以设置get中使用的数据
          // 如果想直接修改计算属性的值必须通过set函数去修改
          console.log(value)
          // 也可以通过set方法去设置依赖项data中的数据
          let str = value.split(' ')
          this.first = str[0]
          this.last = str[1]
        }
      }
    },
  })
</script>

2. 监听器watch

2-1 监听器与实例属性

监听器通过 watch 选项在 options 中定义用来监听某个数据发生的改变,当需要在数据变化时执行异步或开销较大的操作时推荐使用

监听器以 key/value 形式定义,key 为字符串类型表示被监听的属性名(被监听的对象),value 可以是字符串(方法名称)、函数(可以获取到监听对象改变前的值以及更新后的值)、对象(包含回调函数以及其它 options 选项 —— 初始化监听、深度遍历)、数组

{ [key: string]: string | Function | Object | Array }

<div id="app">
  <div>string:<input type="text" v-model="str"></div>
  <div>function:<input type="text" v-model="func"></div>
  <div>object:<input type="text" v-model="obj"></div>
  <div>array:<input type="text" v-model="arr"></div>
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function () {
      return {
        str: 'Hello Watch',
        func: 'Hello Watch',
        obj: 'Hello Watch',
        arr: 'Hello Watch'
      }
    },
    // 在options选项中定义watch选项
    watch: {
      // watch用来监听属性的变化
      // 属性为字符串类型,值可以是字符串,函数,对象,数组
      // 字符串类型 字符串为方法名
      str: 'changeEvent',
      // function类型 参数1新值 参数2旧值
      func: function (newValue, oldValue) {
        console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
      },
      // object对象类型 options选项回调函数handler, 立即执行immediate,深度监听deep
      obj: {
        handler(newValue, oldValue) {
          console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
        },
        immediate: true,
        deep: true
      },
      // 数组依次调用
      arr: [
        // 方法名
        'changeEvent',
        // 函数
        function handler(newValue, oldValue) {
          console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
        },
        // 对象
        {
          handler(newValue, oldValue) {
            console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
          }
        }
      ]
    },
    methods: {
      // 当title属性值改变时触发
      changeEvent() {
        console.log(this.str)
      }
    }
  })
</script>

2-2 监听器实例

监听器也可以通过实例属性 wm.$watch 来定义,参数属性与 watch 选项中一致

当监听的属性不存在时,不会报错 ,回调函数首次执行返回的值为 undefined

vm.$watch( expOrFn {string | Function}, callback {Function | Object}, [options] {Object} )

  • expOrFn —— 参数是一个监听属性,可以是字符串类型或者函数的返回值
  • callback —— 回调函数,当数据发生变化时执行回调,可以是函数也可以是对象
  • options —— 配置项对象类型,用来设置选项是否初始化监听,是否深度监听
<div id="app">
  <input type="text" v-model="title">
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        title: '监听器实例'
      }
    }
  })
  // 通过vm.$watch实例来监听属性变化
  vm.$watch('title', function(newValue, oldValue){
    console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
  })

  // 当监听的属性不存在时,不会报错回调函数参数返回undefined
  vm.$watch('name', function(newValue, oldValue){
    console.log('新的值 -->' + newValue, '旧的值 -->' + oldValue)
  }, {immediate: true})
</script>

2-3 深度监听与初始化监听

Vuewatch 选项默认不监听对象嵌套深层的属性,只能监听简单数据类型变化以及最外层对象属性

也可以在 watch 中定义属性时将对象深层的属性设置为被监听的属性,watch 中属性值必须为字符串类型

  • deep 选项 —— 为了发现对象内部值的变化,可以在选项参数中指定 deep: true,深度监听
  • immediate 选项 —— 在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调,初始化监听
<div id="app">
  <div>
    简单类型:<input type="text" v-model="title">
    <h3>{{title}}</h3>
  </div>
  <div>对象嵌套:<input type="text" v-model="obj.lesson.name">
    <h3>{{obj.lesson.name}}</h3>
  </div>
  <div>数组嵌套:<input type="text" v-model="arr[1][0]">
    <h3>{{arr[1][0]}}</h3>
  </div>
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function () {
      return {
        title: 'Hello Watch',
        obj: {
          lesson: {
            name: 'vue'
          }
        },
        arr: ['html', ['css', ['js']]]
      }
    },
    watch: {
      title: function (val, old) {
        console.log('执行')
      },
      // 对象中的深层属性不能监听
      obj: {
        handler(val, old) {
          console.log(val, old)
        },
        // 使用deep 深度监听
        deep: true,
        // immediate 初始化监听
        immediate: true
      },
      // 注意属性必须为字符串
      // 可以通过将对象深层属性作为watch监听属性
      'obj.lesson.name': function (val, old) {
        console.log(val, old)
      },
      arr: function (val, old) {
        console.log(val, old)
      }
    }
  })
</script>

2-4 监听器vs计算属性

  • 对于 computed

    • computed 默认第一次加载的时候就开始监听
    • 它支持缓存,只有依赖的数据发生了变化,才会重新计算
    • 不支持异步,当 computed 中有异步操作时,无法监听数据的变化
    • computed 的值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 声明过,或者父组件传递过来的 props 中的数据进行计算的
    • 如果一个属性是由其他属性计算而来的,这个属性依赖其他的属性,一般会使用 computed
    • 如果 computed 属性的属性值是函数,那么默认使用 get 方法,函数的返回值就是属性的属性值;在 computed 中,属性有一个 get 方法和一个set 方法,当数据发生变化时,会调用 set 方法
  • 对于 Watch

    • 它不支持缓存,数据变化时,它就会触发相应的操作
    • 支持异步监听
    • 监听的函数接收两个参数,第一个参数是最新的值,第二个是变化之前的值
    • 当一个属性发生变化时,就需要执行相应的操作
    • 监听数据必须是 data 中声明的或者父组件传递过来的 props 中的数据,当发生变化时,会触发其他操作,函数有两个的参数:
      • immediate:组件加载立即触发回调函数
      • deep:深度监听,发现数据内部的变化,在复杂数据类型中使用,例如数组中的对象发生变化。需要注意的是,deep 无法监听到数组和对象内部的变化
  • 总结:

    • computed 计算属性 : 依赖其它属性值,并且 computed 的值有缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值
    • watch 侦听器 : 更多的是观察的作用,无缓存性,类似于某些数据的监听回调,每当监听的数据变化时都会执行回调进行后续操作
  • 使用场景:

    • 当需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时都要重新计算
    • 当需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用 watch 选项允许执行异步操作 ( 访问一个 API ),限制执行该操作的频率,并在得到最终结果前,设置中间状态,这些都是计算属性无法做到的
<div id="app">
  <input type="text" v-model="getTitle">
  <h3>{{getTitle}}</h3>

  <input type="text" v-model="message">
  <h3>{{message}}</h3>
</div>

<script src="../vue.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        title: 'Hello Vue',
        message: 'Hello World'
      }
    },
    // 1. computed 用于属性的计算
    // 2. computed 属性值会缓存
    // 3. computed 需要return返回值
    // 4. computed 初始加载监听
    // 5. computed 不支持异步
    // 6. computed 产生新属性
    // 7. computed 属性值是一函数
    // 8. computed 一对多情况下使用
    computed: {
      getTitle: {
        get() {
          return this.title;
        },
        set(value) {
          this.title = value;
        }
      }
    },
    // 1. watch 用于监听属性值得变化
    // 2. watch 不会缓存值变化时回调
    // 3. watch 不是必须使用return
    // 4. watch 初始加载监听需要使用immediate属性
    // 5. watch 支持异步
    // 6. watch 不产生新属性
    // 7. watch 属性值可以是字符串,对象,数组,函数
    // 8. watch 一对多情况下使用
    watch: {
      message: {
        handler(newVal, oldVal) {
          this.message = newVal
        },
        deep: true,
        immediate: true 
      }
    }
  })
</script>

3. 过滤器filters

3-1 定义过滤器

过滤器可对数据进行筛选、过滤、格式化,与 computedwatchmethods 不同,它不会改变原始值

通过 Mustache 语法插值或者 v-bind 表达式进行绑定,过滤器通常添加在表达式管道符号 | 后面 {{message | filter}}

  • 可以在组件的选项 options 中定义 filter 过滤器,局部定义
  • 在创建 Vue 实例之前定义过滤器,全局定义

**Tips:**当全局过滤器和局部过滤器重名时,会采用局部过滤器

<div id="app">
  <h3>{{getComputedTime}}</h3>
  <h3>{{getMethodsTime()}}</h3>
  <!-- 插值语法绑定 -->
  <h3>{{time | getTime}}</h3>
  <!-- v-bind指令绑定 -->
  <h3 v-bind:id="title | myFilter">v-bind指令绑定</h3>
</div>

<script src="../vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.2/dayjs.min.js"></script>

<script>
  // 全局定义过滤器
  Vue.filter('myFilter', function (value) {
    return value.slice(4, value.length).toLowerCase()
  })
    
  const vm = new Vue({
    el: '#app',
    data: function () {
      return {
        time: 1652496467882,
        title: 'headTitle'
      }
    },
    // 通过computed实现过滤器
    computed: {
      getComputedTime() {
        return dayjs(this.time).format('YYYY年MM月DD日')
      }
    },
    // 通过methods实现过滤器
    methods: {
      getMethodsTime() {
        return dayjs(this.time).format('YYYY年MM月DD日')
      }
    },
    // 通过过滤器定义
    filters: {
      getTime(value) {
        // 过滤器的值作为过滤器函数的参数
        return dayjs(value).format('YYYY年MM月DD日')
      }
    }
  })
</script>

3-2 过滤器使用方法

  • 过滤器函数接收表达式的值作为第一个参数。getTime 过滤器函数将会收到 time 的值作为第一个参数
  • 过滤器函数可以接受多个参数,第一个参数为表达式的值,后面的参数则是过滤器函数自定义参数。getTime 被定义为接收二个参数的过滤器函数。其中 time 的值作为第一个参数,普通字符串 format 作为第二个参数
  • 过滤器可以串联使用。getHead 被定义为接收单个参数的过滤器函数,表达式 title 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 getMain,将 getHead 的结果传递到 getMain
<div id="app">
  <h3>{{time | getTime('YYYY年MM月DD日')}}</h3>
  <!-- 过滤器串联使用 -->
  <!-- 将getHead氯气函数得到的值作为getMain过滤器函数的参数 -->
  <h3>{{title | getHead | getMain}}</h3>
</div>

<script src="../vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.2/dayjs.min.js"></script>

<script>
  const vm = new Vue({
    el: '#app',
    data: function() {
      return {
        time: 1652574881705,
        title: '我是过滤器 嘻嘻'
      }
    },
    filters: {
      getTime(value, format) {
        // 表达式的值为第一个参数
        console.log(value)
        // 第二个参数为过滤器函的自定义参数
        return dayjs(value).format(format)
      },
      getHead(value) {
        return value.slice(0, 5)
      },
      getMain(value) {
        return value.slice(2, value.length)
      }
    }
  })
</script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值