Evan's blog Evan's blog
首页
关于
  • 分类
  • 标签
  • 归档
  • H5&CSS3
  • JS
  • TS
  • Node
  • Webpack
  • Vue2
  • Vue3
  • 微信小程序
  • Andorid
  • Flutter
推荐
GitHub (opens new window)

conanan

真相只有一个
首页
关于
  • 分类
  • 标签
  • 归档
  • H5&CSS3
  • JS
  • TS
  • Node
  • Webpack
  • Vue2
  • Vue3
  • 微信小程序
  • Andorid
  • Flutter
推荐
GitHub (opens new window)
  • 基础

    • 入门
    • 模版语法&指令
    • 条件渲染&列表渲染
    • 计算属性&侦听器
      • 计算属性 computed 🔥
        • 介绍
        • 计算属性的setter和getter
        • 源码 🔥
      • 侦听器 Watch 🔥
        • 介绍
        • Watch Option 用法 🔥
        • $watch 实例方法(掌握) 🔥
        • 参数
        • 返回
        • 用法
        • 示例
        • 选项:deep 🔥
        • 选项:immediate
        • 选项:flush
    • 表单的双向绑定
    • 组件化开发
  • 组件

  • 动画

  • Composition Api

  • 高级语法

  • Vue源码

  • VueCLI&Vite

  • VueRouter

  • Vuex

  • 项目

  • Vue3.x
  • 基础
xugaoyi
2022-01-15
目录

计算属性 & 侦听器

# 计算属性 & 侦听器

# 计算属性 computed 🔥

# 介绍

  • 对于任何包含响应式数据的复杂逻辑,你都应该使用计算属性
  • 计算属性将被混入到组件实例中。所有 getter 和 setter 的 this 上下文自动地绑定为组件实例
  • 用法:
    • **选项:**computed
    • 类型:{ [key: string]: Function | { get: Function, set: Function } }

# 计算属性的setter和getter

  • 计算属性在大多数情况下,只需要一个getter方法即可,所以我们会将计算属性直接写成一个函数
  • 但是,如果我们确实想设置计算属性的值呢?这个时候我们也可以给计算属性设置一个setter的方法
computed: {
    // fullName 的 getter方法
    fullName() {
        return this.firstName + ' ' + this.lastName
    },
    // fullName的getter和setter方法
    fullName: {
        get: function () {
            return this.firstName + ' ' + this.lastName
        },
        set: function (newValue) {
            console.log(newValue)
            const names = newValue.split(' ')
            this.firstName = names[0]
            this.lastName = names[1]
        },
    },
},
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 源码 🔥

Vue内部是如何对我们传入的是一个getter,还是说是一个包含setter和getter的对象进行处理的呢?

vue-next/packages/runtime-core/src/componentOptions.ts中的

image-20220116134736618

# 侦听器 Watch 🔥

# 介绍

在data返回的对象中定义了数据,这个数据通过插值语法等方式绑定到template中

  • 当数据变化时,template会自动进行更新来显示最新的数据
  • 希望在代码逻辑中监听某个数据的变化,这个时候就需要用侦听器watch来完成了

# Watch Option 用法 🔥

  • 选项:watch
  • 类型:{ [key: string]: string | Function | Object | Array}

由类型可知:

  • key 是 string 类型,那个数组代表可以监听多个
  • value可以是对应的字符串方法名、回调函数、或者包含额外选项的对象
const app = createApp({
  data() {
    return {
      a: 1,
      b: 2,
      c: {
        d: 4
      },
      e: 5,
      f: 6
    }
  },
  watch: {
    // 侦听顶级 property
    a(val, oldVal) {
      console.log(`new: ${val}, old: ${oldVal}`)
    },
    // 字符串方法名
    b: 'someMethod',
    // 该回调会在任何被侦听的对象的 property 改变时被调用,不论其被嵌套多深
    c: {
      handler(val, oldVal) {
        console.log('c changed')
      },
      deep: true
    },
    // 侦听单个嵌套 property
    'c.d': function (val, oldVal) {
      // do something
    },
    // 该回调将会在侦听开始之后被立即调用
    e: {
      handler(val, oldVal) {
        console.log('e changed')
      },
      immediate: true
    },
    // 你可以传入回调数组,它们会被逐一调用
    f: [
      'handle1',
      function handle2(val, oldVal) {
        console.log('handle2 triggered')
      },
      {
        handler: function handle3(val, oldVal) {
          console.log('handle3 triggered')
        }
        /* ... */
      }
    ]
  },
  methods: {
    someMethod() {
      console.log('b changed')
    },
    handle1() {
      console.log('handle 1 triggered')
    }
  }
})

const vm = app.mount('#app')

vm.a = 3 // => new: 3, old: 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64

注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向组件实例,this.updateAutocomplete 将是 undefined

# $watch 实例方法(掌握) 🔥

# 参数

  • {string | Function} source
  • {Function | Object} callback
  • {Object} [options]
    • {boolean} deep
    • {boolean} immediate
    • {string} flush

# 返回

{Function} unwatch

# 用法

侦听组件实例上的响应式 property 或函数计算结果的变化。回调函数得到的参数为新值和旧值。我们只能将顶层的 data、props 或 computed property 名作为字符串传递。对于更复杂的表达式,用一个函数取代。

# 示例

const app = createApp({
  data() {
    return {
      a: 1,
      b: 2,
      c: {
        d: 3,
        e: 4
      }
    }
  },
  created() {
    // 顶层property 名
    this.$watch('a', (newVal, oldVal) => {
      // 做点什么
    })

    // 用于监视单个嵌套property 的函数
    this.$watch(
      () => this.c.d,
      (newVal, oldVal) => {
        // 做点什么
      }
    )

    // 用于监视复杂表达式的函数
    this.$watch(
      // 表达式 `this.a + this.b` 每次得出一个不同的结果时
      // 处理函数都会被调用。
      // 这就像监听一个未被定义的计算属性
      () => this.a + this.b,
      (newVal, oldVal) => {
        // 做点什么
      }
    )
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

当侦听的值是一个对象或者数组时,对其属性或元素的任何更改都不会触发侦听器,因为它们**引用(不变)**相同的对象/数组:

const app = createApp({
  data() {
    return {
      article: {
        text: 'Vue is awesome!'
      },
      comments: ['Indeed!', 'I agree']
    }
  },
  created() {
    this.$watch('article', () => {
      console.log('Article changed!')
    })

    this.$watch('comments', () => {
      console.log('Comments changed!')
    })
  },
  methods: {
    // 这些方法不会触发侦听器,因为我们只更改了Object/Array的一个property,
    // 不是对象/数组本身
    changeArticleText() {
      this.article.text = 'Vue 3 is awesome'
    },
    addComment() {
      this.comments.push('New comment')
    },

    // 这些方法将触发侦听器,因为我们完全替换了对象/数组
    changeWholeArticle() {
      this.article = { text: 'Vue 3 is awesome' }
    },
    clearComments() {
      this.comments = []
    }
  }
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

$watch 返回一个取消侦听函数,用来停止触发回调:

const app = createApp({
  data() {
    return {
      a: 1
    }
  }
})

const vm = app.mount('#app')

const unwatch = vm.$watch('a', cb)
// later, teardown the watcher
unwatch()
1
2
3
4
5
6
7
8
9
10
11
12
13

# 选项:deep 🔥

为了发现对象内部值的变化,可以在选项参数中指定 deep: true。这个选项同样适用于监听数组变更。

注意:当变更(不是替换)对象或数组并使用 deep 选项时,旧值将与新值相同,因为它们的引用指向同一个对象/数组。Vue 不会保留变更之前值的副本。可以参考Vue3中watch的最佳实践 (opens new window)来完成!!!

vm.$watch('someObject', callback, {
  deep: true
})
vm.someObject.nestedValue = 123
// callback is fired
1
2
3
4
5

# 选项:immediate

在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调:

vm.$watch('a', callback, {
  immediate: true
})
// 立即以 `a` 的当前值触发 `callback`
1
2
3
4

注意,在带有 immediate 选项时,你不能在第一次回调时取消侦听给定的 property。

// 这会导致报错
const unwatch = vm.$watch(
  'value',
  function() {
    doSomething()
    unwatch()
  },
  { immediate: true }
)
1
2
3
4
5
6
7
8
9

如果你仍然希望在回调内部调用一个取消侦听的函数,你应该先检查其函数的可用性:

let unwatch = null

unwatch = vm.$watch(
  'value',
  function() {
    doSomething()
    if (unwatch) {
      unwatch()
    }
  },
  { immediate: true }
)
1
2
3
4
5
6
7
8
9
10
11
12

# 选项:flush

flush 选项可以更好地控制回调的时间。它可以设置为 'pre'、'post' 或 'sync'。

默认值是 'pre',指定的回调应该在渲染前被调用。它允许回调在模板运行前更新了其他值。

'post' 值是可以用来将回调推迟到渲染之后的。如果回调需要通过 $refs 访问更新的 DOM 或子组件,那么则使用该值。

如果 flush 被设置为 'sync',一旦值发生了变化,回调将被同步调用。

对于 'pre' 和 'post',回调使用队列进行缓冲。回调只被添加到队列中一次,即使观察值变化了多次。值的中间变化将被跳过,不会传递给回调。

缓冲回调不仅可以提高性能,还有助于保证数据的一致性。在执行数据更新的代码完成之前,侦听器不会被触发。

'sync' 侦听器应少用,因为它们没有这些好处。

更多关于 flush 的信息,请参阅副作用刷新时机 (opens new window)。

编辑 (opens new window)
上次更新: 2022/03/23, 17:55:39
条件渲染&列表渲染
表单的双向绑定

← 条件渲染&列表渲染 表单的双向绑定→

最近更新
01
重点
04-12
02
搭建项目
04-04
03
TS补充
03-30
更多文章>
Theme by Vdoing | Copyright © 2019-2022 conanan | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式