认识CompositionAPI
Vue Composition API
是Vue 3
引入的一种新的 API,用于组织和复用组件逻辑。它通过setup()
函数作为入口,结合响应式数据、计算属性、侦听器和生命周期钩子,使代码更加模块化和可维护。
核心概念:
setup()
函数:作为 Composition API 的入口,接收props
和context
,并返回的对象中的属性可以直接在模板中使用。- 响应式数据:通过
ref
和reactive
创建响应式状态。 计算属性:通过computed
定义计算属性。 - 侦听器:通过
watch
和watchEffect
监听状态变化。 - 生命周期钩子:以
on
开头(如onMounted
、onUnmounted
),直接在setup()
中使用。
应用场景:
Vue Composition API
主要用于Vue.js
开发中,帮助开发者更好地组织组件逻辑,提高代码复用性和可维护性。
认识setup
setup的基本概念
setup
是一个函数,它在组件的初始化阶段被调用,用于定义组件的响应式状态、计算属性、方法和生命周期钩子。它的作用类似于Vue 2
中的data
、methods
和生命周期钩子的集合。
setup的参数
setup函数接收两个参数:
props
:组件的属性,是响应式的。context
:一个对象,包含以下属性:attrs
:非响应式的组件属性($attrs
)。slots
:组件的插槽内容。emit
:用于触发事件的方法。
setup的返回值
setup
函数的返回值是一个对象,对象中的属性和方法可以直接在模板中使用。这意味着你可以通过setup
返回的对象,将数据、方法和计算属性暴露给模板。
setup的使用示例
示例1:基本用法
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
// 定义响应式数据
const count = ref(0);
// 定义方法
function increment() {
count.value++; // 直接操作响应式数据
}
// 返回对象,暴露给模板
return {
count,
increment
};
}
};
</script>
示例 2:使用生命周期钩子
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const count = ref(0);
function increment() {
count.value++;
}
// 使用生命周期钩子
onMounted(() => {
console.log('Component is mounted!');
});
return {
count,
increment
};
}
};
</script>
setup的优势
- 模块化:
setup
允许开发者将逻辑分解为可复用的函数,而不是将所有逻辑集中在一个组件对象中。 - 可维护性:通过将逻辑组织为函数,代码更清晰,易于维护和扩展。
- 灵活性:可以更灵活地组织和复用逻辑,支持逻辑的组合和重用。
setup的限制
- 不能直接访问
this
:在setup
中,this
是未定义的。所有逻辑必须通过setup
的参数或返回值来实现。 - 不能直接访问组件实例:
setup
中无法直接访问组件实例的生命周期钩子(如mounted
、updated
等),必须通过 Composition API 提供的生命周期函数(如onMounted
、onUpdated
)来使用。 - 必须返回对象:
setup
的返回值必须是一个对象,否则模板中无法访问到定义的逻辑。 setup 的高级用法 使用computed
:定义计算属性。
import { computed } from 'vue';
const fullName = computed(() => `${firstName.value} ${lastName.value}`);
使用watch
和watchEffect
:监听响应式数据的变化。
import { watch, watchEffect } from 'vue';
watch(count, (newValue, oldValue) => {
console.log(`Count changed from ${oldValue} to ${newValue}`);
});
watchEffect(() => {
console.log(`Count is now ${count.value}`);
});
组合逻辑:将逻辑分解为可复用的函数,然后在setup
中组合。
import { useCounter } from './composables/useCounter';
export default {
setup() {
const { count, increment } = useCounter();
return {
count,
increment
};
}
};
认识ref和reactive
- 在
Vue 3
中,ref
和reactive
是Composition API
提供的两种主要方式,用于创建响应式数据。它们是Vue 3
响应式系统的核心,允许开发者将普通数据转换为响应式状态,从而实现数据的自动更新和视图的动态渲染。虽然它们都可以创建响应式数据,但在使用场景和内部实现上有一些区别。
ref的理解:
ref
是一个函数,用于将一个值(无论是基本数据类型还是复杂对象)包装成一个响应式引用(Ref)。ref
返回的对象包含一个value
属性,通过访问或修改value
属性来操作原始数据。
特点:
- 包装值:
ref
可以包装任何值(如数字、字符串、对象等),使其成为响应式数据。 - 访问和修改:通过
.value
属性访问或修改包装的值。 - 自动解包:在模板中使用时,
Vue
会自动解包ref
的值,因此可以直接使用变量名,而无需显式访问.value
。
使用场景ref
通常用于以下场景:
- 包装基本数据类型(如数字、字符串、布尔值)。
- 包装单个对象或数组,但更推荐使用
reactive
。
示例
<template>
<div>
<p>{{ count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0); // 创建一个响应式引用
function increment() {
count.value++; // 通过 .value 访问和修改
}
return {
count,
increment
};
}
};
</script>
reactive的理解
reactive
是一个函数,用于将一个对象(包括数组和普通对象)转换为响应式对象。与ref
不同,reactive
直接返回一个响应式对象,而不是一个包装对象。
特点
- 对象响应式:
reactive
只能用于对象(包括数组)。它会递归地将对象的每个属性转换为响应式属性。 - 直接操作:不需要通过
.value
属性,直接操作返回的对象即可。 - 性能优化:由于
reactive
是基于Proxy
实现的,它在处理复杂对象时比ref
更高效。
使用场景
- 包装复杂对象或数组。
- 需要频繁操作对象的多个属性。
示例
<template>
<div>
<p>{{ state.count }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const state = reactive({
count: 0
});
function increment() {
state.count++; // 直接操作对象
}
return {
state,
increment
};
}
};
</script>
ref和reactive的区别
特点 | ref | reactive |
---|---|---|
适用数据类型 | 基本数据类型或对象 | 仅对象(包括数组) |
访问方式 | 通过 .value 访问 | 直接访问对象属性 |
模板中使用 | 自动解包,无需 .value | 直接使用对象属性 |
内部实现 | 基于 Object.defineProperty | 基于 Proxy |
性能 | 对于基本数据类型更轻量 | 对于复杂对象更高效 |
ref和reactive的内部实现
ref的实现:
ref
是基于Object.defineProperty
实现的,它会将值包装成一个对象,并通过value
属性暴露原始值。- 当访问或修改
.value
时,Vue
的响应式系统会自动触发依赖更新。
reactive的实现:
reactive
是基于ES6
的Proxy
实现的,它会递归地将对象的每个属性转换为响应式属性。Proxy
提供了更强大的拦截能力,可以处理对象的增删属性、数组的索引操作等复杂场景。
toRef和toRefs的认识与理解
- 在
Vue 3
的Composition API
中,toRef
和toRefs
是两个用于处理响应式对象的工具函数,它们可以帮助开发者更好地管理响应式数据,尤其是在解构或传递响应式对象时。
toRef的理解
作用
toRefs
是一个工具函数,用于将一个响应式对象的所有属性转换为一个普通对象,其中每个属性都是一个
ref
对象。这在解构响应式对象时非常有用,因为直接解构会丢失响应式。
使用场景
- 当你需要解构响应式对象,但又希望保持每个属性的响应式时。
- 在组合式函数中返回响应式对象的属性时。
特点
- 响应式连接:
toRef
创建的ref
对象与原始属性保持同步。 - 非响应式属性的处理:即使原始属性不存在,
toRef
仍然会返回一个ref
对象,其值为undefined
。如果后续原始对象添加了该属性,ref
对象会自动更新。 - 与ref的区别:
ref
创建一个独立的响应式引用,而toRef
创建一个与原始属性绑定的响应式引用。
示例代码
import { reactive, toRef } from 'vue';
const state = reactive({
foo: 1,
bar: 2
});
const fooRef = toRef(state, 'foo'); // 将 state.foo 转换为 ref
console.log(fooRef.value); // 输出 1
fooRef.value = 10; // 修改 ref 的值
console.log(state.foo); // 输出 10,原始属性也被更新
state.foo = 20; // 修改原始属性
console.log(fooRef.value); // 输出 20,ref 的值也被更新
toRefs的理解
作用
toRefs
是一个工具函数,用于将一个响应式对象的所有属性转换为一个普通对象,其中每个属性都是一个ref
对象。这在解构响应式对象时非常有用,因为直接解构会丢失响应式。
使用场景
- 当你需要解构响应式对象,但又希望保持每个属性的响应式时。
- 在组合式函数中返回响应式对象的属性时。
特点
- 保持响应式:
toRefs
将响应式对象的每个属性转换为ref
,从而保持响应式。 - 解构安全:使用
toRefs
解构后,属性仍然是响应式的,不会丢失响应性。
示例代码
import { reactive, toRefs } from 'vue';
const state = reactive({
count: 0,
name: 'Vue'
});
const { count, name } = toRefs(state); // 解构并保持响应式
console.log(count.value); // 输出 0
console.log(name.value); // 输出 'Vue'
state.count = 10; // 修改原始对象的属性
console.log(count.value); // 输出 10,响应式连接保持
name.value = 'Vue 3'; // 修改 ref 的值
console.log(state.name); // 输出 'Vue 3',原始属性也被更新
toRef 和 toRefs 的区别
特性 | toRef | toRefs |
---|---|---|
作用对象 | 单个属性 | 整个对象 |
返回值 | 单个 ref 对象 | 包含所有属性的 ref 对象的普通对象 |
使用场景 | 提取单个属性并保持响应式连接 | 解构整个对象并保持响应式连接 |
注意事项
.value访问:
toRef
和toRefs
返回的都是ref
对象,因此需要通过.value
访问其值。
原始属性的存在性:
- 如果原始属性不存在,
toRef
仍然会返回一个ref
对象,但其值为undefined
。
性能优化:
- 使用
toRef
和toRefs
可以减少不必要的响应式转换,从而提高性能。