vue3组合式API,看这篇就够了
创建vue3项目
组合式API
setup
setup在beforecreate前执行
左边是原始写法,右边是语法糖写法。
区别在于,左边需要定义变量或者函数,然后手动一个个返回。
语法糖写法,加上setup,就能在你定义后以对象方式自动return
需要注意的是,在setup里不用this访问,直接访问即可
reactive和ref函数
<script setup>
import {reactive} from "vue";
const state = reactive({
count:0
})
const add = ()=>{
state.count++;
}
</script>
<template>
<div>
<button @click="add">{{ state.count}}</button>
</div>
</template>
<script setup>
import {reactive, ref} from "vue";
const count = ref(0)
const add = ()=>{
count.value++;
//用value是因为,count实际上是个对象
}
</script>
<template>
<div>
<button @click="add">{{ count}}</button>
//这里不是用count.value,
//应该直接使用 count,因为 Vue 会自动解包 ref 的值。
</div>
</template>
选择使用
- 使用
reactive
当你需要处理一个复杂对象或数组时。- 使用
ref
当你处理的是单个基本类型值,或希望明确表示某个值是响应式的。
computed计算属性函数
<script setup>
import { computed, ref } from "vue";
const list = ref([1, 2, 3, 4, 5, 6, 7, 8, 9]);
const filterList = computed(() => {
return list.value.filter(i => i > 3);
});
</script>
<template>
<div>
{{ filterList }}
</div>
</template>
需要注意的是,计算属性中只能用来计算,不要用来修改。这样能使代码更规范
watch函数
用法
watch
函数用于在 Vue 中观察响应式数据的变化。当被观察的数据变化时,watch
会执行一个回调函数。这在处理副作用或需要响应数据变化时特别有用。
import { watch, ref } from 'vue';
const count = ref(0);
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
参数
- 第一个参数:要观察的响应式数据或计算属性。
- 第二个参数:回调函数,接受两个参数:
newValue
:新值oldValue
:旧值
特点
- 可以观察多个响应式数据,使用数组作为第一个参数。
- 可以选择在值变化时立即执行回调,通过第三个参数
{ immediate: true }
。 - 支持深度观察,使用
{ deep: true }
选项。
示例
immediate是立即执行一次
watch(
count,
(newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
},
{ immediate: true } // 立即执行
);
<script setup>
import { computed, ref, watch } from "vue";
const count = ref(0)
const name = ref('abc')
const change = ()=>{
count.value = 10
name.value = 'qwetyu'
}
watch(
[count,name],
([oldCount,newCount],[oldName,newName])=>{
console.log([oldCount,newCount],[oldName,newName])
},
{
immediate:true
}
)
</script>
<template>
<div>
{{count+" "+name}}
<button @click="change">改变</button>
</div>
</template>
deep
深度监听(deep watch)在 Vue 中用于观察对象内部的变化。当你希望监测一个嵌套对象的属性变化时,普通的监听可能无法捕捉到这些变化。这时就可以使用深度监听。
深度监听用法
在 watch
中,可以通过选项 { deep: true }
来启用深度监听。
深度监听示例
import { ref, watch } from 'vue';
const user = ref({
name: 'Alice',
address: {
city: 'New York',
zip: '10001'
}
});
watch(user, (newValue, oldValue) => {
console.log('User changed:', newValue);
}, { deep: true });
// 修改嵌套属性
user.value.address.city = 'Los Angeles';
深度监听关键点
- 性能:深度监听会遍历对象的每个层级,可能会影响性能,尤其在对象很大时。
- 适用场景:适用于需要细致跟踪嵌套状态的场合,例如表单数据、配置对象等。
深度监听能帮助你在复杂的数据结构中捕捉变化,提供更精细的控制!
监听对象注意点
如果你只想监听对象中的某一个特定属性,可以直接指定该属性,而不是对整个对象进行深度监听。这样可以提高性能并减少不必要的计算。
示例
假设你有一个对象 user
,你只想监听 user.name
的变化:
import { ref, watch } from 'vue';
const user = ref({
name: 'Alice',
age: 30
});
// 只监听 user.name 的变化
watch(() => user.value.name, (newValue, oldValue) => {
console.log(`Name changed from ${oldValue} to ${newValue}`);
});
// 修改 user.name
user.value.name = 'Bob'; // 控制台会输出: Name changed from Alice to Bob
关键点
- 使用箭头函数
() => user.value.name
来明确指定要观察的属性。 - 只在该属性变化时执行回调,从而避免对整个对象的监听。
这种方法使得监听更高效,也更容易理解!
生命周期
左边对应右边
onMounted可以执行多次
父子通信
父传子
父组件
<script setup>
import { computed, ref, watch } from "vue";
import Son from './components/Son.vue';
const count = ref(1)
const add = ()=>{
count.value++;
}
</script>
<template>
<div>
<son msg="from father" :count="count"></son>
<button @click="add">增加</button>
</div>
</template>
子组件
<script setup>
const props = defineProps({
msg:String,
count:Number
})
</script>
<template>
<div>
父组件传来--{{msg}}和{{count}}
</div>
</template>
<style scoped>
</style>
子传父
1. 子组件(Child.vue)
在子组件中,你可以使用 defineEmits
来定义要触发的事件,并通过 emit
方法来发送数据。
<template>
<div>
<button @click="sendData">Send Data to Parent</button>
</div>
</template>
<script setup>
import { defineEmits } from 'vue';
const emit = defineEmits();
const sendData = () => {
const data = 'Hello from Child!';
emit('child-to-parent', data); // 触发事件并传递数据
};
</script>
2. 父组件(Parent.vue)
在父组件中,你可以监听子组件发出的事件,并处理接收到的数据。
<template>
<div>
<h1>Parent Component</h1>
<child @child-to-parent="handleChildData" />
</div>
</template>
<script setup>
import Child from './Child.vue';
const handleChildData = (data) => {
console.log('Received from child:', data); // 处理从子组件接收到的数据
};
</script>
3. 总结
- 子组件使用
defineEmits
定义事件:你可以在子组件中定义事件名称(如child-to-parent
),并使用emit
来发送数据。 - 父组件使用
@
监听事件:在父组件中,通过@event-name
监听子组件的事件,并定义处理函数来处理接收到的数据。
上面可以看到有两种写法,关于defineEmits带不带参数解释如下
第一段代码
在第一个 <script setup>
中,你使用了 defineEmits(['update-list'])
,这是定义了一个具体的事件名 update-list
,表示该组件将发出这个事件。这样做的好处是:
- 类型安全: 如果你使用 TypeScript,定义事件名可以提供类型检查。
- 文档化: 让其他开发者看到组件支持的事件。
const emit = defineEmits(['update-list']);
第二段代码
在第二段代码中,使用了 defineEmits()
而没有传入任何参数:
const emit = defineEmits();
这种用法的含义是这个组件可以发出任意事件,但没有事先定义事件名。这样做的影响包括:
- 灵活性: 你可以在组件中发出任何事件,但失去了类型安全和明确的文档。
- 不利于维护: 其他开发者可能不知道这个组件可以发出哪些事件。
-
参数定义:
- 第一段代码明确了事件名,提供了更好的类型安全和文档化。
- 第二段代码则没有事件名,提供了灵活性但缺乏明确性。
-
使用场景:
- 如果你知道组件会发出特定事件,建议使用带参数的
defineEmits()
。 - 如果组件的事件名不确定,或者会动态变化,可以使用不带参数的
defineEmits()
。
- 如果你知道组件会发出特定事件,建议使用带参数的
建议在大多数情况下使用带参数的方式,以提高代码的可读性和可维护性。
模板引用
1. 模板引用的概念
模板引用是通过在模板中使用 ref
属性来标识一个 DOM 元素或子组件。使用 ref
可以在 Vue 的 setup
函数中获取这些元素或组件的引用。
总之!是用来方便获取dom元素的!
2. 使用 ref
获取 DOM 元素
在模板中,可以给任何 DOM 元素添加 ref
属性:
<template>
<div>
<h1 ref="myHeading">Hello, Vue!</h1>
<button @click="changeHeading">Change Heading</button>
</div>
</template>
在这个示例中,<h1>
标签被标识为 myHeading
。
在 setup
函数中访问
在 setup
函数中,可以通过 ref
函数获取对该 DOM 元素的引用:
<script setup>
import { ref } from 'vue';
const myHeading = ref(null); // 初始化为 null
const changeHeading = () => {
if (myHeading.value) {
myHeading.value.textContent = 'Heading Changed!'; // 修改 DOM 内容
}
};
</script>
myHeading.value
:这是对 DOM 元素的引用,可以通过.value
访问实际的 DOM 对象。
3. 使用 ref
获取组件实例
同样地,可以使用 ref
来引用子组件的实例:
子组件(Child.vue)
<template>
<div>Child Component</div>
</template>
<script setup>
// 子组件逻辑
</script>
父组件(Parent.vue)
<template>
<div>
<Child ref="myChild" />
<button @click="callChildMethod">Call Child Method</button>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const myChild = ref(null); // 引用子组件
const callChildMethod = () => {
if (myChild.value) {
myChild.value.someMethod(); // 调用子组件的方法
}
};
</script>
4. 注意事项
- 生命周期:在组件挂载后,引用才会有效。因此在
mounted
钩子中访问这些引用是安全的。 - 响应式:使用
ref
时,访问 DOM 元素或组件实例需要使用.value
。
总结
通过 ref
,开发者可以轻松地访问和操作真实的 DOM 元素或子组件实例。这为动态更新内容、调用组件方法等提供了极大的便利,使得 Vue 的开发更加灵活和强大。
ref
在 Vue 中的作用确实类似于在原生 JavaScript 中使用id
或class
属性,然后通过querySelector
获取 DOM 元素。相似点
获取元素:使用
ref
,你可以直接在 Vue 中引用 DOM 元素或组件实例,就像用document.querySelector
获取元素一样。简便性:
ref
提供了一种更简洁的方式来访问和操作 DOM,因为你不需要查找元素,而是直接引用。动态更新:无论是
ref
还是querySelector
,你都可以在事件处理函数中对元素进行动态更新。不同点
响应式:
ref
是 Vue 的响应式系统的一部分,可以在 Vue 的生命周期内自动管理,而querySelector
则是直接访问 DOM,不具备响应式特性。与 Vue 生态集成:使用
ref
时,你可以更方便地访问子组件的方法和属性,这在使用原生 DOM 方法时并不容易实现。总之,
ref
使得 Vue 开发中对 DOM 的操作更加高效和集成,尤其是在处理组件时。
defineExpose
defineExpose
是 Vue 3 中一个很有用的功能,主要用于在 <script setup>
中显式地暴露组件的 API(即方法、属性等),以便外部使用。这使得子组件可以向父组件提供接口,增强了组件之间的通信和封装性
基本用法
在子组件中,你可以使用 defineExpose
来指定希望暴露给父组件的属性或方法。
子组件示例
<template>
<div>
<h1>Child Component</h1>
</div>
</template>
<script setup>
import { ref, defineExpose } from 'vue';
const count = ref(0);
const increment = () => {
count.value++;
};
defineExpose({
count,
increment,
});
</script>
在这个示例中,count
和 increment
被暴露给外部。
父组件使用示例
<template>
<div>
<Child ref="childRef" />
<button @click="incrementChildCount">Increment Child Count</button>
<p>Child Count: {{ childCount }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue';
import Child from './Child.vue';
const childRef = ref(null);
const childCount = ref(0);
const incrementChildCount = () => {
if (childRef.value) {
childRef.value.increment(); // 调用子组件的方法
childCount.value = childRef.value.count; // 获取子组件的 count 值
}
};
</script>
关键点
-
暴露的属性和方法:
defineExpose
允许你选择性地暴露组件内部的状态和方法,增强了封装性。 -
在父组件中访问:通过
ref
引用子组件,可以直接访问暴露的属性和方法。 -
与响应式结合:暴露的属性仍然是响应式的,可以在父组件中直接使用。
使用场景
- 组件间通信:当子组件需要向父组件提供某些功能时,
defineExpose
是一个理想的选择。 - 封装逻辑:可以将复杂的逻辑封装在子组件中,只向外暴露必要的 API,增强组件的可重用性和维护性。
总的来说,defineExpose
是一个非常强大的工具,使得 Vue 3 组件之间的交互更加灵活和简洁。
provide和inject
provide
和inject
是 Vue 3 中用于组件间依赖注入的两个 API,主要用于实现跨层级的状态共享和数据传递。这种机制尤其适合在较深层级的组件树中传递数据,而不需要通过每一层的 props 逐级传递。
使用场景
-
跨层级通信:当你有多个层级的组件时,可以使用
provide
和inject
来避免通过 props 逐层传递数据。 -
共享状态:对于一些全局状态,比如主题、用户信息等,可以在根组件中使用
provide
进行提供,而在任何子组件中使用inject
进行访问。 -
插件和库:许多 Vue 插件和库使用
provide
和inject
来在组件间传递配置或状态。
注意事项
-
响应式:当你通过
provide
提供的是响应式数据(如ref
或reactive
),子组件在使用inject
时会保持响应性。 -
依赖关系:
inject
只会注入其父组件提供的值,如果没有找到对应的provide
,会返回undefined
。 -
多重提供:如果一个组件在多层级中被提供了同样的键,子组件会优先使用最近的提供者。
跨层传递普通数据
父组件
<template>
<Child />
</template>
<script setup>
import { provide, ref } from 'vue';
const sharedData = ref('Hello from Parent');
provide('myData', sharedData);
</script>
子组件
<template>
<div>{{ injectedData }}</div>
</template>
<script setup>
import { inject } from 'vue';
const injectedData = inject('myData');
if (!injectedData) {
console.warn('myData is not provided');
}
</script>
跨层传递响应式数据
父组件
<script setup>
import {computed, provide, ref, watch} from "vue";
import Son from './components/Son.vue';
const count = ref(1000)
provide('count',count)
const add = ()=>{
count.value++;
}
</script>
<template>
<div>
<Son></Son>
<button @click="add">增加</button>
</div>
</template>
子组件
<script setup>
import {inject, ref} from "vue";
const res = inject('count')
</script>
<template>
<div>
{{res}}
</div>
</template>
<style scoped>
</style>
跨层传递方法
父组件
<script setup>
import {computed, provide, ref, watch} from "vue";
import Son from './components/Son.vue';
const count = ref(1000)
const add = ()=>{
count.value++;
}
provide('count',count)
provide('add',add)
</script>
<template>
<div>
<Son></Son>
</div>
</template>
子组件
<script setup>
import {inject, ref} from "vue";
const res = inject('count')
const add = inject('add')
</script>
<template>
<div>
{{res}}
<button @click="add">增加</button>
</div>
</template>
<style scoped>
</style>
这个总结很重要。
案例
原文地址:https://blog.csdn.net/clmm_/article/details/142436633
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!