八、Vue3 Composition API (二)

2022/8/27 Vuecomputed函数组件生命周期函数Provide和inject使用watchwatchEffect自定义Hookscript setup语法糖写法definePropsdefineEmitsdefineExpose

# 1、computed

  • 在前面我们讲解过计算属性computed:当我们的某些属性是依赖其他状态时,我们可以使用计算属性来处理
    • 在前面的Options API中,我们是使用computed选项来完成的;
    • 在Composition API中,我们可以在 setup 函数中使用 computed 方法来编写一个计算属性;
  • 如何使用computed呢?
    • 方式一:接收一个getter函数,并为 getter 函数返回的值,返回一个不变的 ref 对象;
    • 方式二:接收一个具有 get 和 set 的对象,返回一个可变的(可读写)ref 对象;

120ce3d0a1bb45ace.png

# 2、setup中使用ref(获取元素及组件)

  • 在setup中如何使用ref获取元素或者组件?
    • 其实非常简单,我们只需要定义一个ref对象,绑定到元素或者组件的ref属性上即可
    • 这里我们用vue2,vue3的方式分别演示;如下图所示

2b5c89f956ad0ec9f.png

# 3、生命周期钩子

  • 我们前面说过 setup 可以用来替代 data 、 methods 、 computed 等等这些选项,也可以替代 生命周期钩子。
  • 那么setup中如何使用生命周期函数呢?
    • 可以使用直接导入的 onX 函数注册生命周期钩子

3b6a0211a7f6b8da3.png

# 4、Provide函数和Inject函数

# 4.1.Provide函数

  • 事实上我们之前还学习过Provide和Inject,Composition API也可以替代之前的 Provide 和 Inject 的选项。
    • 我们可以通过 provide来提供数据:
    • 可以通过 provide 方法来定义每个 Property;
  • provide可以传入两个参数:
    • name:提供的属性名称;
    • value:提供的属性值;
<script>
  import { provide } from 'vue'
  export default {
    setup() {
      let counter = 100
      let info = {
        name:'kobe',
        age:45
      }
      
      provide("counter", counter)]
      provide("info", info)
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 4.2.Inject函数

  • 在 后代组件 中可以通过 inject 来注入需要的属性和对应的值:
    • 可以通过 inject 来注入需要的内容;
  • inject可以传入两个参数:
    • 要 inject 的 property 的 name;
    • 默认值;
<script>
  import { inject } from 'vue'
  export default {
    setup() {
      const conuter = inject("counter")
      const info = inject("info")
    }
  }
</script>
1
2
3
4
5
6
7
8
9

# 4.3.数据的响应式

  • 为了增加 provide 值和 inject 值之间的响应性,我们可以在 provide 值时使用 ref 和 reactive。
<script>
  import { ref, reactive, provide } from 'vue'
  export default {
    setup() {
      let counter = ref(100)
      let info = reactive({
        name:'kobe',
        age:45
      })
      
      provide("counter", counter)]
      provide("info", info)
    }
  }
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 5、侦听器Watch

# 5.1.侦听数据的变化

  • 在前面的Options API中,我们可以通过watch选项来侦听data或者props的数据变化,当数据变化时执行某一些操作。
  • 在Composition API中,我们可以使用watchEffect和watch来完成响应式数据的侦听;
    • watchEffect:用于自动收集响应式数据的依赖;
    • watch:需要手动指定侦听的数据源;

# 5.2.Watch的使用

  • watch的API完全等同于组件watch选项的Property:
    • watch需要侦听特定的数据源,并且执行其回调函数;
    • 默认情况下它是惰性的,只有当被侦听的源发生变化时才会执行回调;
<script>
  import { ref, watch } from 'vue'
  export default {
    setup() {
      const name = ref('kobe')
      
      watch(name,(newValue,oldValue) => {
        console.log(newValue,oldValue)
      })
      
      return {
        name
      }
    }
  }
</script>
<template>
  <div class='watch'>
    <input type="text" v-model="name">
  </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

# 5.3.侦听多个数据源

  • 侦听器还可以使用数组同时侦听多个源:
<script>
  import { ref, watch } from 'vue'
  export default {
    setup() {
      const name = ref('kobe')
      const age = ref(45)
      
      watch([name,age],(newValue,oldValue) => {
        console.log(newValue,oldValue)
      })
      
      return {
        name,
        age
      }
    }
  }
</script>
<template>
  <div class='watch'>
    <input type="text" v-model="name">
    <input type="text" v-model="age">
  </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

# 5.4.watch的选项

  • 如果我们希望侦听一个深层的侦听,那么依然需要设置 deep 为true:
    • 也可以传入 immediate 立即执行;即刚开始数据没发生改变就侦听一次;
  • 注意:
    • 通过reactive处理的对象,默认是深层侦听的;
  • 补充:如果你侦听的是一个原始对象:即没有通过ref或reactive处理;会报如下警告
    • A watch source can only be a getter/effect function, a ref, a reactive object, or an array of these types.(监视源只能是getter/effect函数、ref、reactive对象或这些类型的数组。)
<script>
  import { reactive, watch } from 'vue'
  export default {
    setup() {
      //深层侦听,和立即执行
      const info = reactive({
        name:'kobe',
        age:45,
        friends:[
          {id:112,name:'james',age:38}
        ]
      })
      
      watch(() => ({...info}),(newValue,oldValue) => {//这里想要深层侦听,必须设置deep:true
        console.log(newValue.friends[0].name,oldValue?.friends[0].name)
        console.log(newValue,oldValue)
      },{
        deep:true
      })

      watch(info,(newValue,oldValue) => {//通过reactive处理的对象,默认是深层侦听的
        console.log(newValue.friends[0].name,oldValue?.friends[0].name)
        console.log(newValue,oldValue)
      },{
        immediate:true
      })
      
      return {
        info
      }
    }
  }
</script>
<template>
  <div class='watch'>
    <input type="text" v-model="info.friends[0].name">
  </div>
</template>
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

# 5.5.watchEffect

  • 当侦听到某些响应式数据变化时,我们希望执行某些操作,这个时候可以使用 watchEffect。
  • 我们来看一个案例:
    • 首先,watchEffect传入的函数会被立即执行一次,并且在执行的过程中会收集依赖;
    • 其次,只有收集的依赖发生变化时,watchEffect传入的函数才会再次执行;
<script>
  import { ref, watchEffect } from 'vue'
  export default {
    setup() {
      const age = ref(8)
      const name = ref('kobe')
      watchEffect(() => {
        console.log("watchEffect正在执行->", name.value, age.value)
      })
      function changeAge() {
        age.value++
      }
      
      return {
        age,
        name,
        changeAge
      }
    }
  }
</script>
<template>
  <h2>age:{{age}}</h2>
  <button @click="changeAge">age+1</button>
</template>
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

# 5.6.watchEffect的停止侦听

  • 如果在发生某些情况下,我们希望停止侦听,这个时候我们可以获取watchEffect的返回值函数调用该函数即可
  • 比如在上面的案例中,我们age达到20的时候就停止侦听:
<script>
  import { ref, watchEffect } from 'vue'
  export default {
    setup() {
      const age = ref(8)
      const name = ref('kobe')
      const stopWatch = watchEffect(() => {
        console.log("watchEffect正在执行->", name.value, age.value)
        //if(age.value >= 20) {
        //  stopWatch()
        //}
      })
      function changeAge() {
        age.value++
        if(age.value > 20) {
          console.log('停止侦听')
          stopWatch()//调用watchEffect的返回值,停止侦听
        }
      }
      
      return {
        age,
        name,
        changeAge
      }
    }
  }
</script>
<template>
  <h2>age:{{age}}</h2>
  <button @click="changeAge">age+1</button>
</template>
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

# 6、自定义hook练习

  1. useCounter
    • 我们先来对之前的counter逻辑进行抽取:
  2. useTitle
    • 我们编写一个修改title(网站标题)的Hook:
  3. useScrollPosition
    • 我们来完成一个监听界面滚动位置的Hook:

41c684d7d0e7c8054.png

# 7、script setup语法

# 7.1.setup函数语法糖

  • <script setup> 是在单文件组件 (SFC) 中使用组合式 API 的编译时语法糖,当同时使用 SFC 与组合式 API 时则推荐该语法。
    • 更少的样板内容,更简洁的代码;
    • 能够使用纯 Typescript 声明 prop 和抛出事件;
    • 更好的运行时性能 ;
    • 更好的 IDE 类型推断性能 ;
  • 使用这个语法,需要将 setup attribute 添加到 <script>代码块上:
<script setup>
  console.log("Hello setup语法糖写法")
</script>
1
2
3
  • 里面的代码会被编译成组件 setup() 函数的内容:
    • 这意味着与普通的<script>只在组件被首次引入的时候执行一次不同;
    • <script setup>中的代码会在每次组件实例被创建的时候执行。

# 7.2.顶层的绑定会被暴露给模板

  • 当使用 <script setup>的时候,任何在 <script setup> 声明的顶层的绑定 (包括变量,函数声明,以及 import 引入的内容)都能在模板中直接使用
<script setup>
  import Home from './Home.vue'
  
  const message = '顶层的绑定直接暴露给模板template,可直接使用'
  function btnClick() {
    console.log('btnClick')
  }
</script>

<template>
  <div class="app">
    <home></home>
    <h2>{{message}}</h2>
    <button @click="btnClick">按钮</button>
  </div>
</template>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  • 响应式数据需要通过ref、reactive来创建。

# 7.3.导入的组件直接使用

  • <script setup>范围里的值也能被直接作为自定义组件的标签名使用:
<script setup>
  import Home from './Home.vue'
</script>

<template>
  <div class="app">
    <home></home>
  </div>
</template>
1
2
3
4
5
6
7
8
9

# 7.4.defineProps() 和 defineEmits()

  • 父子组件通信在setup函数语法糖写法中的用法

  • 为了在声明 props 和 emits 选项时获得完整的类型推断支持,我们可以使用 defineProps 和 defineEmits API,

  • 它们将自动地在<script setup> 中可用,不需要另外import引入

5.png

# 7.5.defineExpose()

  • defineExpose也是自动地在<script setup> 中可用,不需要另外import引入

  • 使用 <script setup> 的组件是默认关闭的:

    • 通过模板 ref 或者 $parent 链获取到的组件的公开实例,不会暴露任何在 <script setup>中声明的绑定;
  • 通过 defineExpose 编译器宏来显式指定在 <script setup> 组件中要暴露出去的 property:

6.png

最后更新时间: 2022/09/21, 01:34:25
彩虹
周杰伦