Vue3 组合API 的学习之路(一)

147 阅读4分钟

Vue3 组合API 的学习之路(一)

  • 前言:本文的所有演示示例代码的写法都是以Vue3的setup语法糖来编写的
  • Vue3中文官网

1. 生命周期

生命周期钩子函数

1.1 vue3 和 vue2 的生命周期函数对比情况

Vue2的生命周期函数Vue3生命周期函数描述
beforeCreatesetup创建实例前
createdsetup创建实例
beforeMountonBeforeMount挂载DOM前
mountedonMounted挂载DOM后
beforeUpdateonBeforeUpdate更新组件前
updatedonUpdated更新组件后
beforeDestroyonBeforeUnmount卸载销毁前
destroyedonUnmounted卸载销毁后

1.2 演示示例

<script setup>
import {
  onMounted,
  onBeforeMount,
  onBeforeUpdate,
  onUpdated,
  onActivated,
  onDeactivated,
  onBeforeUnmount,
  onUnmounted
} from 'vue'

    console.log('--- setup')
    
    onBeforeMount(() => {
      console.log('--- onBeforeMount')
    })

    onMounted(() => {
      console.log('--- onMounted')
    })

    onBeforeUpdate(() => {
      console.log('--- onBeforeUpdate')
    })

    onUpdated(() => {
      console.log('--- onUpdated')
    })

    onActivated(() => {
      console.log('--- onActivated')
    })

    onDeactivated(() => {
      console.log('--- onDeactivated')
    })

    onBeforeUnmount(() => {
      console.log('--- onBeforeUnmount')
    })

    onUnmounted(() => {
      console.log('--- onUnmounted')
    })    
</script>

2. setup 函数

作用:

  1. 组件实例创建前执行
  2. 函数中不能使用 this --> undefined
  3. 模版中需要使用的数据和函数,需要在setup中返回

2.1 setup 的参数

  • props: 接受父组件传入的通过props声明的属性

  • comtext : 上下文

    • attrs : 接受父组件传入的没有通过props声明过的属性
    • slots :接收父组件传入的插槽内容的对象
    • emit : 用来分发自定义事件的函数

2.2 演示示例

<script setup>
import { defineProps, defineEmits } from 'vue'

const props = defineProps({
  icon: {
    type: String,
    default: ''
  }
})

const emit = defineEmits(['click'])
const click = () => {
  emit('click', params)
}
</script>

3. reactive 函数

作用:定义响应式函数,通常定义复杂的数据类型,通常用来定义对象

3.1 演示示例

<template>
   <div @click="updateName()">{{ obj.name }}</div>
</template>

<script setup>
import { reactive } from 'vue'

const obj = reactive({ name: '定义响应式对象', age: 20 })
const updateName = () => {
  obj.name = '更新响应式数据'
}
</script>

4. toRef 函数

作用:转换响应式对象中的某个属性为单独的响应式数据,并且值是关联的
注意:通过 toref 转换响应式数据包装成了对象,value 存放值的位置

4.1 演示示例

<template>
   <div @click="updateName()">{{ name }}</div>
</template>

<script setup>
import { reactive, toRef } from 'vue'

const obj = reactive({ name: '定义响应式对象', age: 20 })
const name = toRef(obj, 'name')
const updateName = () => {
  name.value = 'zs'
}
</script>

4.2 使用场景

  • 有一个响应式对象数据,但在模块中只需要使用其中一项的数据

5. toRefs 函数

作用:转换响应式中的所有属性为响应式数据,通常用于解构|展开reactive定义对象
注意:通过 toref 转换响应式数据包装成了对象,value 存放值的位置

5.1 演示示例

<template>
    <div @click="updateName()">{{ name }} - {{ age }} </div>
</template>

<script setup>
import { reactive, toRefs } from 'vue'

const obj = reactive({ name: '定义响应式对象', age: 20 })
const { name, age } = toRefs(obj)
const updateName = () => {
  name.value = 'zs'
}
</script>

5.2 使用场景

  • 剥离响应式对象,想使用响应式对象中的多个或者所有属性作为响应式数据

6. ref 函数

作用:定义响应式函数

  1. 通常定义简单类型数据
  2. 其实也可以定义复杂类型的数据,在你得到数据类型是未知的情况下:可以这么定义: const data = ref(null) 注意:
  3. 在修改值或获取值的时候都需要.value
  4. 在模版中使用 ref 申明的响应式数据,可以省略.value

6.1 演示示例

<template>
    <div @click="updateName()">{{ name }} - {{ age }} </div>
</template>

<script setup>
import { ref } from 'vue'

const name = ref('ls')  
const age = ref(18)
const data = ref(null)  // [] | {} | 0 | 'string' | true

const updateName = () => {
  name.value = 'zs'
}
</script>

6.2 使用场景

  • 当你明确知道需要的是一个响应式数据对象,那么就使用reactive
  • 其他情况使用ref

7. computed 函数

作用:定义计算属性

7.1 简单用法:演示示例

<template>
    <div>{{ age }} - {{ newAge }} </div>
</template>

<script setup>
import { ref, computed } from 'vue'
const age = ref(18)
const newAge = computed(() => age.value + 2)
</script>

7.2 高级用法:演示示例

<template>
    <div>{{ age }} - {{ newAge }} </div>
    <input v-model="newAge" />
</template>

<script setup>
import { ref, computed } from 'vue'
const age = ref(18)
const newAge = computed({
  get() {
    return age.value + 2
  },
  // 当你给计算属性设置值的时候触发,实现计算属性也支持v-model
  set(value) {
    age.value = value - 2
  }
})
</script>

8. watch 函数、watchEffect 函数

作用: 定义监听器

  1. watch(): 指定监听数据
    • 监听指定的一个或多个响应式数据,一旦数据变化,就自动执行监听回调
      • 如果是监听reactive对象中的属性,必须通过函数来指定
      • 监听多个数据,使用数组来指定
    • 默认初始时不执行回调,但可以通过配置immebdiate:true,来指定初始时立即执行第一次
    • 通过配置deep:true,来指定深度监视
  2. watchEffect(): 不指定监听数据
    • 不用直接指定要监视的数据,回调函数中使用的那些响应式数据就监视那些响应式数据
    • 默认初始时就会执行一次

8.1 watch用法:演示示例

  • 监听一个数据
<template>
    <div>{{ count }} </div>
    <button @click="add">修改值</button>
</template>

<script setup>
import { ref, watch } from 'vue'
  
const count = ref(0)
const add = () => { count.value++ }

watch(count, (newVal, oldVal) => { 
  console.log(newVal, oldVal)
})
  
</script>
  • 监听多个数据
<template>
    <div>{{ count }} </div>
    <button @click="add">修改值</button>
    <hr />
    <div>{{ obj.name }} </div>
    <div>{{ obj.age }} </div>
    <button @click="updateName">修改值</button>
</template>

<script setup>
import { ref, reactive, watch } from 'vue'
const count = ref(0)
const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })

const add = () => { count.value++ }
const updateName = () => { obj.name = 'zs' }

watch([count, obj], () => { 
  console.log('监听多个数据变了')
})

</script>
  • 监听对象中某一个属性的变化
<template>
    <div>{{ obj.name }} </div>
    <div>{{ obj.age }} </div>
    <button @click="updateName">修改值</button>
    <button @click="updateBrandName">修改值</button>
</template>

<script setup>
import { reactive, watch } from 'vue'

const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })
const updateName = () => { obj.name = 'zs' }
  
// 监听对象中某一个属性的变化,例如obj.name
watch(() => obj.name, () => {
  console.log('监听obj.name数据变了')
})

</script>
  • 深度监听
<template>
    <div>{{ obj.name }} </div>
    <div>{{ obj.age }} </div>
    <button @click="updateName">修改值</button>
    <button @click="updateBrandName">修改值</button>
</template>

<script setup>
import { reactive, watch } from 'vue'
  
const obj = reactive({ name: 'ls', age: 18, brand: { id: 1, name: '宝马'} })
const updateName = () => { obj.name = 'zs' }
const updateBrandName = () => { obj.brand.name = '奔驰' }
  
watch(obj, () => {
  console.log('监听数据变了')
})
 
watch(() => obj.brand, () => {
  console.log('监听brand数据变了')
}, {
  immebdiate: true, // 立即监听
  deep: true // 深度监听
})

</script>

8.2 watchEffect用法: 演示示例

<template>
    <input v-mode="name">
    <input v-mode="age">
    <div>{{ full }}</div>
</template>

<script setup>
import { ref, watchEffect } from 'vue'
  
const name = ref('ls')
const age = ref(18)
const full = ref(null)

watchEffect(() => { full.value = name.value + '--', age.value })
  
</script>

9. ref 属性

作用:通过 ref 绑定 DOM 元素

9.1 演示示例

  • 单个 DOM 元素的绑定
<template>
    <div ref="dom">DOM</div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
const dom = ref(null)
onMounted(() => {
  console.log('ref-dom', dom.value)
})
</script>
  • 被 v-for 便利的 DOM 元素(多个)
<template>
  <ul>
    <li v-for="item in 4" :ref="setDom">第{{ item }}个Li</li>
  </ul>
</template>

<script setup>
const domList = []
const setDom = (el) => {
  domList.push(el)
}
console.log('domList', domList)
</script>

10. 父子通讯

10.1 父传子

  • 父组件 app.vue
<template>
    <h1>父组件</h1>
    <Som :msg="msg" />
</template>

<script setup>
import { ref } from 'vue'
import Som from './som.vue'

const msg = ref('props')
</script>
  • 子组件 som.vue
<template>
    <h1>子组件</h1>
    <p>{{ props.msg }}</p>
</template>

<script setup>
import { defineProps } from 'vue'
const props = defineProps({
  msg: {
    type: String,
    default: 'hello'
  }
})
</script>

10.2 子传父

  • 父组件 app.vue
<template>
    <h1>父组件</h1>
    <p>{{ msg }}</p>
    <Som :msg="msg" @change="changeMsg" />
</template>

<script setup>
import { ref } from 'vue'
import Som from './som.vue'

const msg = ref('props')
const changeMsg = (newMsg) => { msg.value = newMsg }
</script>
  • 子组件 som.vue
<template>
    <h1>子组件</h1>
    <p>{{ props.msg }}</p>
    <button @click="change">改变了msg</button>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
  msg: {
    type: String,
    default: 'hello'
  }
})

const emit = defineEmits(['change'])
const change = () => { emit('change', '更新msg') }
</script>

10.3 扩展(对以上代码中绑定的事件和参数的简写版本)

  • 父组件 app.vue
<template>
  <h1>父组件</h1>
  <p>{{ msg }}</p>
  <Som v-model:msg="msg" />
</template>

<script setup>
import { ref } from 'vue'
import Som from './som.vue'

const msg = ref('props')
const changeMsg = (newMsg) => { msg.value = newMsg }
</script>
  • 子组件 som.vue
<template>
    <h1>子组件</h1>
    <p>{{ props.msg }}</p>
    <button @click="change">改变了msg</button>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
  msg: {
    type: String,
    default: 'hello'
  }
})

const emit = defineEmits(['change'])
// const change = () => { emit('change', '更新msg') }
const change = () => { emit('update:msg', '更新msg') }
</script>

11. 后代组件数据通讯 provide 函数和 inject 函数

作用:

  1. provide: 提供数据和函数给后代组件使用
  2. inject:给当前组件注入provide提供的数据和函数 注意:后代组件不能修改父组件的数据,遵循单向数据流的原则;数据是谁定义的,谁来修改

11.1 演示示例

  • 父组件 app.vue
<template>
  <h1>父组件</h1>
  <p>{{ money }}</p>
  <button @click="money = 2000">发钱</button>
  <hr />
  <Som />
</template>

<script setup>
import { ref, provide } from 'vue'
import Som from './som.vue'

const money = ref(100)
const changeMoney = (saleMoney) => {
  money.value = money.value - saleMoney
}
// 将数据提供后代组件
provide('money', money)
// 将函数提供给后代组件
provide('changeMoney', changeMoney)

</script>
  • 子组件 som.vue
<template>
  <h2>子组件</h2>
  <p>{{ money }}</p>
  <hr />
  <GrandSom />
</template>

<script setup>
import { inject } from 'vue'
import GrandSom from './GrandSom.vue'
// 接受父组件提供的数据
const money = inject('money')
</script>
  • 孙组件 GrandSom.vue
<template>
  <h3>孙组件</h3>
  <p>{{ money }}</p>
  <button @click="changeMoney(20)">消费20</button>
</template>

<script setup>
import { inject } from 'vue'
import GrandSom from './GrandSom.vue'
// 接受父组件提供的数据
const money = inject('money')
// 后代组件不能修改父组件的数据,遵循单向数据流的原则;数据是谁定义的,谁来修改
const changeMoney = inject('changeMoney')
</script>

11.2 使用场景

  • 有一个父组件,里头有子组件,孙组件等很多后代组件;共享父组件的数据

12. v-model 语法糖

vue3 封装组件支持 v-model 的时候,父传子:modelValue,子传父@update:modelValue

12.1 演示示例

  • 父组件 app.vue
<template>
  <h2>父组件</h2>
  <p>{{ money }}</p>
  <hr />
  <Som v-model="money" />
</template>

<script setup>
import { ref } from 'vue'
import Som from './som.vue'
const money = ref(100)
</script>
  • 子组件 som.vue
<template>
  <h2>子组件</h2>
  <p>{{ props.modelValue }}</p>
  <button @click="change">改变数据</button>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
  modelValue: {
    type: Number,
    default: 0
  }
})

const emit = defineEmits(['change'])
// const change = () => { emit('change', '更新msg') }
const change = () => { emit('update:modelValue', 200) }
</script>

结语

希望文章中的内容能够帮助到大家!如果文章写的不好,请大家见谅,如果觉得还不错的话,请给我点点赞,您的赞是我最大的动力!

谢谢