vue

✍️ Tangxt ⏳ 2020-07-15 🏷️ computed、watch

04-computed 和 watch

★计算属性(computed

1)为什么需要计算属性?

我们在模板内可以插入表达式,如🟡🟡message🟡🟡🟡🟡User.name🟡🟡……

假如我们在模板中插入这样复杂的表达式:

<div id="example">
  🟡🟡 message.split('').reverse().join('') 🟡🟡
</div>

可以看到,此时我们并不能一眼就能明白 message.split('').reverse().join('') 在表示什么

也就是说,这个表达式是很「命令式」的,并不像messageUser.name这样这么「声明式」!

所以,针对这种有「复杂逻辑」的表达式,我们都应该使用计算属性!反之,如果是「简单运算」,那就不需要用计算属性,直接用 data 里边旗下的属性就好了!

2)使用计算属性

有以下模板:

模板

测试:

计算属性

计算属性的缓存 vs 方法:

缓存

小结:

3)计算属性也有 `setter`

计算属性默认只有 getter,不过在需要时你也可以提供一个 setter

计算属性的 setter

★侦听属性(watch

1)是什么?

一种更通用的方式来观察和响应 Vue 实例上的数据变动

简单来说,就是当你有一些数据需要随着其它数据变动而变动时,就可以用该侦听属性 watch

不过该属性过于「命令式」,相较于「计算属性」来说,这很不方便……

2)使用

watch

可看到,watch里边的代码是很重复的,很命令的,相较于计算属性的版本来说,是比较 low 的:

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

★侦听器(Watchers)

还咩有确定一个值到底是多少的时候!

1)为什么需要自定义侦听器?

既然尤大大提供了一个 watch 选项,那么肯定会有它使用的场景的:

2)测试

测试 1:

模板:

模板

测试 1

测试 2:

测试 2

所以,watch的监听方式是:简单数据类型看值,复杂数据类型(对象)看地址

3)命令式的 `vm.$watch`

你可以不用 watch选项 -> 可以使用 vm.$watch 来自定义侦听器

文档:vm-watch

Tip:watch也可以监听模拟的「计算属性」

★疑问

1)`watch` 选项 先执行 ,还是 `created` 钩子先执行?

watch

2)只要计算属性有依赖其它属性就会响应式更新?

我之前一直以为只有写在return旁边的其它属性,如「return xxx」,xxx变化了,计算属性才会跟着变化

没想到,并不需要这样,总之,只要函数体中存在依赖的其它属性就行了!

3)对于计算属性的缓存你怎么看?

关于缓存,如果依赖的属性没有变化,就不会重新计算,getter/setter默认不会做缓存,Vue 做了特殊处理。

4)什么是计算属性,什么是侦听属性?

区别:

computed看上去是方法,但是实际上是计算属性,它会根据你所依赖的数据动态显示新的计算结果。

计算结果会被缓存,computed 的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取 computed 的值时才会**重新调用对应的 getter **来计算。

watcher 更像是一个 data数据监听回调,当依赖的 data 的数据变化,执行回调,在方法中会传入 newValoldVal。可以提供输入值无效,提供中间值 特场景。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。如果你需要在某个数据变化时做一些事情,使用watch

5)`watch`的`deep`属性?

默认值是 false,代表是否深度监听

使用场景:

data是这样的:

{
  obj: {
    a: 123
  }
}

我们watch这个对象:

{
  watch: {
    obj: {
      handler(newName, oldName) {
         console.log('obj.a changed');
      },
      immediate: true,
      deep: true
    }
  } 
}

效果:

deep:true的存在,让obj.a变化了,也会触发handler的执行,不然,默认的deep:false,只有obj的引用地址发生了变化,handler才会去执行!

所以:

deep的意思就是深入观察,监听器会一层层的往下遍历,给对象的所有属性都加上这个监听器,但是这样性能开销就会非常大了,任何修改obj里面任何一个属性都会触发这个监听器里的 handler

因此,我们可以这样优化,使用字符串形式监听:

watch: {
  'obj.a': {
    handler(newName, oldName) {
      console.log('obj.a changed');
    },
    immediate: true,
    // deep: true
  }
} 

这样 vue 才会一层一层解析下去,直到遇到属性a,然后才会给a设置监听函数,而不是obj旗下所有的属性!

6)为什么要注销 `watch`?

因为我们的组件是经常要被销毁的,比如我们跳一个路由,从一个页面跳到另外一个页面,那么原来的页面的 watch 其实就没用了,这时候我们应该注销掉原来页面的 watch 的,不然的话可能会导致内存溢出

当然,我们平时 watch 都是写在组件的选项中的,它会随着组件的销毁而销毁。

然而,如果我们使用下面这样的方式写 watch,那么就要手动注销了,当然,这种注销其实也是很简单:

const unWatch = app.$watch('text', (newVal, oldVal) => {
  console.log(`${newVal} : ${oldVal}`);
})

unWatch(); // 手动注销 watch

app.$watch调用后会返回一个值,就是unWatch方法,你要注销 watch 只要调用unWatch方法就可以了。

7)对配置的认识?

配置

我们知道观察的数据发生了变化,就会执行callback,可如immediatedeep这样的选项,它们都具备某种特殊的功能,而这些功能在某种场景下会有奇效!

8)我们观察`xxx`的状态,那么`watch`会有几种写法?

watch属性可以是字符串、函数、对象、数组

{
  watch:{
    n(){} // 可以是函数
    n: 'logN' // 可以是 methods 里面的函数
    n: { handler(){} , deep:true }
    n: { handler(){} , immediate:true }
    n: [f1(){},f2(){}] //都会执行里面的函数
    'obj.a'(){} // 监听 obj 里面 a 的值是否变化
  }
}

9)不建议使用`watch`模拟`computed`?

let vm = new Vue({
      data: {
        user: {
          email: "fangfang@qq.com",
          nickname: "方方",
          phone: "13812312312"
        },
        displayName: ""
      },
      watch: {
        "user.email": {
          handler() {
            const { user: { email, nickname, phone } } = this;//从this中解析出user,从user中解析出三个变量
            this.displayName = nickname || email || phone;
          },
          immediate: true // 第一次渲染也触发 watch
        },
        "user.nickname": {
          handler() {
            const { user: { email, nickname, phone } } = this;
            this.displayName = nickname || email || phone;
          },
          immediate: true
        },
        "user.phone": {
          handler() {
            const { user: { email, nickname, phone } } = this;
            this.displayName = nickname || email || phone;
          },
          immediate: true
        }
      },
      template: `
    <div>
       🟡🟡displayName🟡🟡
       <button @click="user.nickname=undefined">remove nickname</button>
    </div>
  `,
      methods: {
        changed() {
          console.log(arguments);
          const user = this.user;
          this.displayName = user.nickname || user.email || user.phone;
        }
      }
    }).$mount("#app");

效果:

模拟计算属性

★了解更多

➹:计算属性和侦听器 — Vue.js

➹:vue computed正确使用方式 - 知乎

➹:41.computed和watch - 掘金

➹:Vue.js中 watch 的高级用法 - 掘金

➹:浅析Vue进阶属性“computed、watch” - 知乎

➹:認識 Vue.js watch 監聽器. 認識 watch 如何使用、有哪些可選設定

➹:Vue.js: Watch

➹:Vue 的 computed 和 watch 的区别 - 知乎

➹:Vue 里的 computed 和 watch 的区别 - 知乎

➹:computed和watch的区别 - 知乎

★总结