【Vue进阶学习笔记】组合式API(Composition API)
目录
- setup 选项
- reactive和ref函数
- reactive()
- ref()
- 示例-计数器按钮
- computed 计算属性函数
- 示例-过滤数组
- watch 函数
- 基础使用 - 侦听单个数据
- 基础使用 - 侦听多个数据
- deep 深度监听机制
- 默认浅层监听的问题
- 解决方案:开启 deep 选项
- 生命周期函数
- Vue3的生命周期API(选项式VS组合式)
- 生命周期函数基本使用
- 父子通信
- 父传子
- 子传父
- 模板引用
- 如何使用(以获取dom为例 组件同理)
- defineExpose()
- provide 和 inject
- 作用和场景
- 跨层传递响应式数据
- 基本用法
setup 选项
Vue3 引入了全新的 setup
选项,用于定义组件的初始状态和核心逻辑。
setup
函数采用 Composition API 的方式组织代码,显著提升了代码的可读性和灵活性。该函数接收 props
和 context
两个参数,并返回一个包含响应式状态和方法的对象,这些内容可直接在模板中使用。
这种设计不仅使组件结构更加清晰明了,还为逻辑复用提供了更好的支持。
原始复杂写法
<script>
export default {setup() {//数据const message = 'Hello Vue 3!'//函数const sayHello = () => {console.log(message)}//返回数据和函数return {message,sayHello}}
}
</script>
语法糖写法
<script setup>const message = 'Hello Vue 3!'const sayHello = () => {console.log(message)}
</script>
reactive和ref函数
reactive()
作用:接受对象类型数据的参数传入并返回一个响应式的对象
核心步骤
<script setup>
import { reactive } from 'vue'
const state = reactive(对象类型数据)
</script>
- 从 vue 包中导入 reactive 函数
- 在
ref()
作用:接受任意类型数据的参数传入并返回一个响应式且可变的ref对象,通过.value属性访问和修改内部值。适用于基本类型数据和对象引用。更推荐使用ref(),因为在功能上覆盖了reactive()
核心步骤:
<script setup>
import { ref } from 'vue'
const count = ref(简单类型或者复杂类型数据) // 基本类型
</script>
示例-计数器按钮
<template><button @click="buttonClick">{{ count }}</button>
</template><script setup>import { ref } from 'vue'const count = ref(0)function buttonClick(){count.value++}
</script>
computed 计算属性函数
计算属性基本思想和Vue2的完全一致,组合式API下的计算属性只是修改了写法
<script setup>
import { computed } from 'vue'
const computedState = computed(() => {return 基于响应式数据做计算之后的值
})
</script>
- 导入computed函数
- 执行函数,在回调参数中return基于响应式数据做计算的值,用变量接收
示例-过滤数组
<template><div>原始响应式数组 - {{ list }}</div><div>过滤后响应式数组 - {{ computedList }}</div>
</template><script setup>
import { ref, computed } from 'vue';
const list = ref([1, 2, 3, 4, 5, 6, 7, 8]);
const computedList = computed(() => {return list.value.filter(item => item > 2);
});
</script>
watch 函数
作用:侦听一个或者多个数据的变化,数据变化时执行回调函数
两个额外参数:1.immediate(立即执行)2.deep(深度侦听)
基础使用 - 侦听单个数据
- 导入watch函数
- 执行watch函数传入要侦听的响应式数据(ref对象)和回调函数
<script setup>
// 1. 导入watch
import { ref, watch } from 'vue'
const count = ref(0)// 2. 调用watch 侦听变化
watch(count, (newValue, oldValue) => {console.log(`count发生了变化,老值为${oldValue},新值为${newValue}`)
})
</script>
基础使用 - 侦听多个数据
说明:同时侦听多个响应式数据的变化,不管哪个数据变化都需要执行回调
<script setup>
import { ref, watch } from 'vue'
const count = ref(0)
const name = ref('cp')
// 侦听多个数据源
watch([count, name],([newCount, newName], [oldCount, oldName]) => {console.log('count或者name变化了', [newCount, newName], [oldCount, oldName])}
)
</script>
deep 深度监听机制
在 Vue 的响应式系统中,通过 watch
监听的 ref 对象默认采用浅层侦听(shallow watch)机制。这意味着当直接修改嵌套的对象属性时,默认不会触发 watch 回调函数执行。
默认浅层监听的问题
const state = ref({ count: 0 })// 默认浅层监听
watch(state, () => {console.log('数据变化了') // 不会触发
})const changeStateByCount = () => {// 直接修改嵌套属性 - 不会触发回调state.value.count++
}
在这个例子中,当我们修改 state.value.count
时,watch 回调不会执行,因为:
- ref 对象本身(state)的引用没有改变
- 默认的浅层监听不会追踪嵌套属性的变化
解决方案:开启 deep 选项
要实现深度监听,需要显式设置 deep: true
选项:
watch(state,() => {console.log('深度监听:数据变化了')},{ deep: true } // 启用深度监听
)
开启深度监听后,watch 会:
- 递归追踪所有嵌套属性的变化
- 无论修改哪一层级的属性都会触发回调
- 注意性能开销,因为要追踪的对象可能很大
生命周期函数
Vue3的生命周期API(选项式VS组合式)
选项式 API | 组合式 API |
---|---|
beforeCreate/created | setup |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeUnmount | onBeforeUnmount |
unmounted | onUnmounted |
生命周期函数基本使用
- 导入生命周期函数
- 执行生命周期函数 传入回调
import { onMounted } from 'vue'
onMounted(() =? {//自定义逻辑
})
父子通信
父传子
同样是通过props
实现,在vue3中有更简洁的写法:通过defineProps()
往里面传入数组或对象来接受父级传来的数据
<script setup>
const props = defineProps({message:String,count:Number
})
</script>
以下是一个示例
父组件
<template><div class="box"><h1>props:我是父组件曹操</h1><hr /><Child info="我是曹操" :money="money"></Child></div>
</template><script setup lang="ts">
import Child from "./Child.vue";
import { ref } from "vue";
let money = ref(10000);
</script><style scoped>
.box {width: 100vw;height: 400px;background: yellowgreen;
}
</style>
子组件
<template><div class="son"><h1>我是子组件:曹植</h1><p>{{info}}</p><p>{{money}}</p><button @click="updateProps">修改props数据</button></div>
</template><script setup lang="ts">
//需要使用到defineProps方法去接受父组件传递过来的数据
//defineProps是Vue3提供方法,不需要引入直接使用
let props = defineProps(['info','money']); //数组|对象写法都可以
//按钮点击的回调
const updateProps = ()=>{// props.money+=10; props:只读的console.log(props.info)
}
</script><style scoped>
.son{width: 400px;height: 200px;background: hotpink;
}
</style>
子传父
基本思想
- 父组件中给子组件标签通过@绑定事件
- 子组件内部通过
$emit
方法触发事件
父组件
<script setup>
// 引入子组件
import sonComVue from './son-com.vue'
const getMessage = (msg) => {console.log(msg)
}
</script><template><!-- 1. 绑定自定义事件 --><sonComVue @get-message="getMessage" />
</template>
子组件
<script setup>
// 2. 通过 defineEmits 编译器宏生成 emit 方法,以数组传入要生成的事件名称
const emit = defineEmits(['get-message'])
const sendMsg = () => {// 3. 触发自定义事件 并传递参数emit('get-message', 'this is son msg')
}
</script><template><button @click="sendMsg">sendMsg</button>
</template>
模板引用
通过ref标识获取真实的dom对象或者组件实例对象
如何使用(以获取dom为例 组件同理)
<template><!-- 2. 通过ref标识绑定ref对象 --><h1 ref="h1Ref">我是dom标签h1</h1>
</template>
<script setup>
import { ref } from 'vue'
// 1. 调用ref函数得到ref对象
const h1Ref = ref(null)
</script>
defineExpose()
默认情况下在
<script setup>
import { ref } from 'vue'
const testMessage = ref('this is test msg')
defineExpose({testMessage
})
</script>
provide 和 inject
作用和场景
provide
和 inject
是 Vue.js 中用于实现跨层级组件通信的一对 API,主要用于解决组件多层嵌套时的数据传递问题。它们的作用是允许祖先组件向其所有后代组件(无论嵌套多深)传递数据和方法,而不需要逐层通过 props 传递。
典型应用场景包括:
- 主题切换功能(深层次子组件需要访问主题变量)
- 国际化实现(所有组件都需要语言包)
- 用户权限管理(深层组件需要判断权限)
- 全局状态共享(替代 Vuex 的轻量级方案)
跨层传递响应式数据
在 Vue 3 中,为了确保传递的数据保持响应式,需要在调用 provide 函数时将第二个参数设置为 ref 对象或 reactive 对象。这样当数据变化时,所有注入该数据的组件都能自动更新。
基本用法
顶层组件(提供数据)
import { ref, provide } from 'vue'export default {setup() {// 创建响应式数据const count = ref(0)const userInfo = reactive({name: '张三',age: 25})// 提供数据给后代组件provide('count-key', count)provide('user-info-key', userInfo)// 也可以提供方法const increment = () => {count.value++}provide('increment-method', increment)return { count }}
}
底层组件(注入数据)
import { inject } from 'vue'export default {setup() {// 注入数据const count = inject('count-key')const userInfo = inject('user-info-key')const increment = inject('increment-method')// 可以设置默认值const theme = inject('theme', 'light')// 如果确定提供者会提供数据,可以使用非空断言const requiredData = inject('required-key')!return { count, userInfo, increment, theme }}
}