Vue3组合式API

目录

composition API vs options API

体验 composition API

setup 函数

reactive 函数

ref 函数

script setup 语法

计算属性 computed 函数

监听器 watch 函数

生命周期

模板中 ref 的使用

组件通讯 - 父传子

组件通讯 - 子传父

依赖注入 - provide 和 inject

保持响应式 - toRefs 函数


​​​​​​​composition API vs options API

  1. vue2 采用的就是 options API

    (1) 优点: 易于学习和使用, 每个代码有着明确的位置 (例如: 数据放 data 中, 方法放 methods 中)

    (2) 缺点: 完成相同功能的代码不能放在一起, 在大项目中尤为明显。可维护性不好

  2. vue3 新增的就是 composition API

    (1) compositionAPI 是基于 逻辑功能 组织代码的, 一个功能相关的代码可以放到一起

    (2) 即使项目大了, 功能多了,也能快速定位相关功能的代码,大大的提升了代码的可维护性

  3. vue3 推荐使用 composition API, 也保留了options API

    即就算不用 composition API, 用 vue2 的写法也完全兼容!!

体验 composition API

需求:

  1. 显示隐藏图片

  2. 计数器功能

 options API 版本

<template>
  <button @click="toggle">显示隐藏图片</button>
  <img v-show="show" alt="Vue logo" src="./assets/vue.svg" />
  <hr />
  计数器:{{ count }} <button @click="add">+1</button>
</template>
<script>
export default {
  data() {
    return {
      show: true,
      count: 0
    }
  },
  methods: {
    toggle() {
      this.show = !this.show
    },
    add() {
      this.count++
    }
  }
}
</script>

composition API 版本

<template>
  <button @click="toggle">显示隐藏图片</button>
  <img v-show="show" alt="Vue logo" src="./assets/vue.svg" />
  <hr />
  计数器:{{ count }} <button @click="add">+1</button>
</template>
<script>
// ref 就是一个组合式API,用于数据响应式 
import { ref } from 'vue';
export default {
  setup () {
    // 显示隐藏图片
    const show = ref(true)
    const toggle = () => {
      show.value = !show.value
    }
    
    // 计数器
    const count = ref(0)
    const add = () => {
      count.value++
    }
    return { show, toggle, count, add }
  }
}
</script>

小结:

optionsAPI

  • 优点: 易于学习和使用, 每个代码有着明确的位置

  • 缺点: 完成相同功能的代码不能放在一起, 在大项目中尤为明显。可维护性不好

compositionAPI

  • 基于 逻辑功能 组织代码

  • 可复用性和可维护性都更好!

setup 函数

composition api 的使用, 需要配置一个 setup 函数

  1. setup 函数是一个新的组件选项, 作为组件中 composition API 的起点

  2. setup 比 beforeCreate 执行时机还要早,此时 vue 的实例还没有创建,所以setup 中不能使用 this, this 指向 undefined

  3. 在模版中需要使用的数据和函数,需要在 setup 返回

<template>
  <h1 @click="sayHi">{{msg}}</h1>
</template>

<script>
export default {
  setup () {
    console.log('setup')
    console.log(this)
    
    // 定义数据和函数
    const msg = 'hi vue3'
    const sayHi = () => {
      console.log(msg)
    }

    return { msg, say }
  },
  beforeCreate() {
    console.log('beforeCreate')
    console.log(this)
  }
}
</script>

reactive 函数

setup 中定义的数据,默认情况不是响应式的,需要用 reactive 函数,将数据变成响应式的

作用:可以将一个复杂类型的数据,转换成响应式数据

<template>
  <div>{{ user.name }}</div>
  <div>{{ user.age }}</div>
  <button @click="changeAge">改年龄</button>
</template>

<script>
import { reactive } from 'vue'

export default {
  setup () {
    const user = reactive({
      name: 'zs',
      age: 18
    })
    
    const changeAge = () => {
      user.age++
      console.log(user.age)
    }

    return {
      user,
      changeAge
    }
  }
}
</script>

ref 函数

reactive 处理的数据,必须是复杂类型,如果是简单类型无法处理成响应式,所以有 ref 函数!

作用:可以将一个简单类型或复杂类型的数据,转换成响应式数据。

<template>
  <div>{{ count }}</div>
  <button @click="add">+1</button>
</template>

<script>
import { ref } from 'vue'

export default {
  setup() {
    const count = ref(0)
    console.log(count)
		console.log(count.value)
    
    const add = () => {
      count.value++
    }
    return {
      count,
      add
    }
  }
}
</script>

ref 和 reactive 的最佳使用方式:

  • 明确的对象,明确的属性,用 reactive,其他用 ref

  • 从vue3.2之后,更推荐使用 ref(ref底层性能做了提升 => 260%)

// 1. 明确的对象和属性
const form = reactive({
  username: '',
  password: ''
})

// 2. 不明确的对象和属性
const list = ref([])
const res = await axios.get('/list')
list.value = res.data

script setup 语法

script setup 是在单文件组件 (SFC) 中,使用组合式 API 的编译时语法糖。相比于普通的 script 语法更加简洁

要使用这个语法,需要将 setup attribute 添加到 <script> 代码块上:

<script setup></script>

所有定义的变量,函数和 import 导入的内容都可以直接在模板中直接使用

<template>
  <div>{{ count }}</div>
  <button @click="add">+1</button>
</template>

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

计算属性 computed 函数

computed 函数调用时,要接收一个处理函数,处理函数中,需要返回计算属性的值。

<template>
  <div>今年的年龄 <input type="number" v-model="age" /></div>
  <div>明年的年龄 {{ nextAge }}</div>
</template>

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

const age = ref(18)
const nextAge = computed(() => {
  return age.value + 1
})
</script>

监听器 watch 函数

watch 监听, 接收三个参数
1. 参数1: 监视的数据
2. 参数2: 回调函数
3. 参数3: 额外的配置

<template>
  <div>{{ count }} <button @click="count++">+1</button></div>
  <div>
    {{ user }} 
    <button @click="changeUser">修改用户信息</button>
    <button @click="changeName">修改用户的年龄</button>
    <button @click="changeAge">修改用户的姓名</button>
  </div>
</template>

<script setup>
import { ref, watch } from 'vue'
  
// 1. 监听数据基础用法
const count = ref(0)
watch(count, (newValue, oldValue) => {
  console.log(newValue, oldValue)
})

// 2. 监听复杂类型数据
const user = ref({
  name: 'zs',
  age: 18
})
const changeUser = () => {
  user.value = {
    name: 'ls',
    age: 19
  }
}
const changeName = () => {
	user.value.name = 'ls'
}
watch(user, (newValue) => {
  console.log('user变化了', newValue)
}, {
  // 深度监听:当监听复杂数据类型的某个属性的变化,需要深度监听
  deep: true,
  // 让监听器立即执行一次
  immediate: true
})

// 3. 监听对象的某个属性的变化
const changeAge = () => {
  user.value.age = 19
}
watch(
  () => user.value.age,
  (newValue) => {
    console.log(newValue)
  }
)
</script>

生命周期

生命周期函数 vue3 中的生命周期函数, 需要在 setup 中调用。

vue2 和 vue3 的生命周期对比:

  1. beforeCreate 和 created 在 setup 中不需要,原来在这两个生命周期中做的事,直接写到setup函数中。

  2. 在原来的名字前加一个 on

  3. beforeDestroyed 变为 onBeforeUnmount,destroyed 变为 onUnmounted

选项式API下的生命周期函数使用组合式API下的生命周期函数使用
beforeCreate不需要(直接写到setup函数中)
created不需要(直接写到setup函数中)
beforeMountonBeforeMount
mountedonMounted
beforeUpdateonBeforeUpdate
updatedonUpdated
beforeDestroyedonBeforeUnmount
destroyedonUnmounted
activatedonActivated
deactivatedonDeactivated

 

<script setup>
import { onMounted } from "vue"

// 生命周期函数:组件渲染完毕
onMounted(()=>{
  console.log('onMounted触发了')
})

onMounted(()=>{
  console.log('onMounted也触发了')
})
</script>

<template>
  <div>生命周期函数</div>
</template>

模板中 ref 的使用

联想之前的 ref 和 $refs, 获取 DOM 元素 或 组件

获取 DOM

  1. 创建 ref => const h1Ref = ref(null)

  2. 模板中建立关联 => <h1 ref="h1Ref">钩子函数-----123</h1>

  3. 使用 => hRef.value 来获取 DOM

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

const h1Ref = ref(null) 

onMounted(() => {
  console.log(h1Ref.value)
})
</script>

<template>
	<h1 ref="h1Ref">我是标题</h1>
</template>

获取组件实例

<script setup>
import { ref } from 'vue'
import MyForm from './components/MyForm.vue'

const myFormRef = ref(null)

const fn = () => {
  console.log(myFormRef.value.count)
  myFormRef.value.validate()
}
</script>

<template>
  <MyForm ref="myFormRef"></MyForm>
	<button @click="fn">获取子	组件属性和方法</button>
</template>

需要使用 defineExpose 暴露属性或方法

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

const count = ref(0)
const validate = () => {
  console.log('表单校验方法')
}
// 暴露属性或方法给外部组件使用
defineExpose({
  count,
  validate
})
</script>

<template>
  <h3>我是 Form 组件</h3>
</template>

组件通讯 - 父传子

目标:能够实现组件通讯中的父传子组件通讯

步骤:

  1. 父组件提供数据

  2. 父组件将数据传递给子组件

  3. 子组件通过 defineProps 进行接收

核心代码:

父组件

<script setup>
import { ref } from 'vue'
// 在 setup 语法中,组件导入之后就能够直接使用,不需要使用 components 进行局部注册
import Child from './components/Child.vue'

const car = ref('宝马')
</script>

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h1>我是父组件</h1>
    <Child :car="car"></Child>
  </div>
</template>

子组件

<script setup>
// defineProps: 接收父组件传递的数据
const props = defineProps({
  car: {
    type: String,
    required: true
    // default: '奔驰'
  }
})
// 如果使用 defineProps 接收数据,这个数据只能在模板中渲染,如果想要在 script 中使用 props 属性,应该接收返回值。
console.log(props.car)
</script>

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h3>我是子组件</h3>
    <div>{{ car }}</div>
  </div>
</template>

组件通讯 - 子传父

目标:能够实现组件通讯中的子传父

步骤:

  1. 父组件通过自定义事件的方式在子组件上注册事件

  2. 父组件提供自定义事件的处理函数,并接收子组件传递的数据

  3. 子组件通过 defineEmits 获取 emit 对象(因为没有 this)

  4. 子组件通过 emit 触发事件,并且传递数据

核心代码

父组件

<script setup>
import { ref } from 'vue'
import Child from './components/Child.vue'

const car = ref('宝马')

const changeCar = (newCar) => {
  car.value = newCar
}
</script>

<Child :car="car" @changeCar="changeCar"></Child>

子组件

<script setup>
defineProps({
  car: {
    type: String,
    required: true
    // default: '奔驰'
  }
})

const emit = defineEmits(['changeCar'])

const change = () => {
  emit('changeCar', '玛莎拉蒂')
}
</script>

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h3>我是子组件</h3>
    <div>{{ car }}</div>
    <button @click="change">换车</button>
  </div>
</template>

依赖注入 - provide 和 inject

依赖注入:可以非常方便的实现跨级的组件通信

父组件利用 provide 提供数据

<script setup>
import { provide, ref } from 'vue'
import Child from './components/Child.vue'

const car = ref('宝马')
provide('car', car)
</script>

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h1>我是父组件</h1>
    <Child></Child>
  </div>
</template>

子孙后代组件,都可以拿到这个数据)

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

const car = inject('car')
</script>	

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h3>我是子组件 -- {{ car }}</h3>
  </div>
</template>

如果希望子传父, 可以 provide 传递一个方法

父组件

<script setup>
import { provide, ref } from 'vue'
import Child from './components/Child.vue'

const car = ref('宝马')
const changeCar = (newCar) => {
  car.value = newCar
}
provide('car', car)
provide('changeCar', changeCar)
</script>

子组件

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

const car = inject('car')
const changeCar = inject('changeCar')
</script>

<template>
  <div style="border: 3px solid #ccc;margin: 10px;">
    <h3>我是子组件 -- {{ car }}</h3>
    <button @click="changeCar('兰博基尼')">换车</button>
  </div>
</template>

保持响应式 - toRefs 函数

如果对一个响应式数据, 进行解构, 会丢失它的响应式特性!

原因: vue3 底层是对 对象 进行监听劫持,reactive/ref 的响应式功能是赋值给对象的, 如果给对象解构, 会让数据丢失响应式的能力

toRefs 作用: 对一个 响应式对象 的所有内部属性, 都做响应式处理, 保证解构出的数据也是响应式的

<script setup>
import { ref, toRefs } from 'vue'
const user = ref({
  name: 'zs',
  age: 18
})
// 直接解构,会丢失响应式
const { name, age } = user.value
// 解构之前使用 toRefs 处理要解构的数据
const { name, age } = toRefs(user.value)
</script>

<template>
  <div>{{ name }}</div>
  <div>{{ age }}</div>
  <button @click="name = 'ls'">改名</button>
	<button @click="age++">改年龄</button>
</template>

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值