2.组合式函数
选项式 API
在 Vue 中引入组合式 API 之前,开发人员用选项式 API 来组织组件逻辑,其中包括响应式数据、生命周期方法、计算属性等。选项式 API 允许在特定选项中定义这些方面,如下所示:
<!-- Template -->
<script>
export default {
name: "MyComponent",
props: {
// props
},
data() {
// data
},
computed: {
// computed properties
},
watch: {
// properties to watch
},
methods: {
// methods
},
created() {
// lifecyle methods like created
},
// ...
};
</script>
<!-- Styles -->
虽然这种方法达到了其目的并且仍适用于 Vue 3,但随着组件变得越来越大、越来越复杂,管理和维护可能会变得困难。在特定选项中定义组件逻辑可能会增加阅读和理解代码的难度,尤其是在处理大量组件时。在这个场景中,提取和重用组件之间的通用逻辑也可能很困难。
让我们看一个 App
组件的简单示例,它包含两个独立的子组件 —— Count
和 Width
。
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
export default {
name: "App",
data() {
return {
count: 0,
width: 0,
};
},
mounted() {
this.handleResize();
window.addEventListener("resize", this.handleResize);
},
beforeUnmount() {
window.removeEventListener("resize", this.handleResize);
},
methods: {
increment() {
this.count++;
},
decrement() {
this.count--;
},
handleResize() {
this.width = window.innerWidth;
},
},
components: {
Count,
Width,
},
};
</script>
上面的代码片段表示一个名为 App
的 Vue 单文件组件 (SFC: single-file component)。
<template>
部分定义组件的模板标签。在本例中,它包含一个带有“App”类的 <div>
元素,该元素包裹了两个子组件: <Count>
和 <Width>
。这些子组件使用 Vue 的属性绑定语法( :count
、 :increment
、 :decrement
和 :width
)传递某些属性值。
<script>
部分包含组件的 JavaScript 代码。首先从各自的文件中导入 Count
和 Width
组件。 export default
语句用于导出组件定义。在本组件的定义中,我们可以看到有如下部分:
data
方法返回一个包含组件初始数据属性的对象,其中count
和width
初始化为 0。mounted()
生命周期钩子用于在组件挂载到 DOM 中后执行代码。在本例中,它调用了handleResize()
方法,并监听窗口大小变动事件。beforeUnmount()
生命周期钩子用于在组件卸载和销毁之前执行代码。在这里,它删除了窗口大小变动事件的监听。methods
对象包含组件的方法。它定义了increment()
、decrement()
和handleResize()
方法,这些方法根据某些事件或行为操作计数和宽度。
当应用运行时,实时显示当前计数和窗口内部宽度。用户可以通过使用 <Count>
组件中的按钮增加和减少计数来与组件交互。
同样,每当调整窗口大小时,宽度数据就会自动更新。
App.vue
单文件组件的构造方式可以如下所示:
尽管这个组件体积很小,但它内部的逻辑已经交织在一起。有些部分专用于计数的功能,而其他部分则涉及窗口大小逻辑。随着组件规模的增长,在组件内组织和定位相关逻辑将变得更具挑战性。
为了应对这些挑战,Vue 团队在 Vue 3 中引入了组合式 API。
组合式 API
组合式 API 可以看作是提供代表 Vue 核心功能的独立函数的 API。这些函数主要在 setup()
选项中使用,此选项作为使用组合式 API 的入口。
<!-- Template -->
<script>
export default {
name: "MyComponent",
setup() {
// the setup function
},
};
</script>
<!-- Styles -->
setup()
函数将在创建组件之前以及组件的属性可用时执行。
通过组合式 API,我们可以导入独立功能来让我们在组件中使用 Vue 的核心功能。让我们使用组合式 API 语法重写前述的计数器和宽度示例。
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import { ref, onMounted, onBeforeUnmount } from "vue";
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
export default {
name: "App",
setup() {
const count = ref(0);
const width = ref(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
const handleResize = () => {
width.value = window.innerWidth;
};
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
count,
width,
increment,
decrement,
};
},
components: {
Count,
Width,
},
};
</script>
组件的 <template>
保持不变,但在组件的 <script>
中,我们使用带有 setup()
函数的组合式 API。
在 setup()
函数中,我们:
- 使用
ref()
函数定义count
和width
响应性变量 - 该函数接受单个原始类型值(例如字符串、数字等)并返回响应性/可变的对象。 - 我们还定义了自定义函数
increment()
、decrement()
和handleResize()
。这些函数与我们在之前的选项式 API 示例中定义的方法类似。 - 我们使用
onMounted()
生命周期函数来调用自定义的handleResize()
函数,并在组件挂载时为 resize 事件添加事件监听。同样,我们在组件卸载之前使用onBeforeUnmount()
生命周期函数删除 resize 事件的事件监听。 - 然后返回
setup()
函数中定义的响应性变量和函数,以便在组件模板中访问它们。
组合式函数
通过我们之前的代码示例,人们可能仍然想知道 setup()
函数如何为开发提供任何优势,因为它似乎只需要我们在单个函数中声明组件选项。
采用组合式 API 的巨大好处之一是能够提取和重用组件之间的共享逻辑。这是因为我们可以简单地声明我们自己的函数,使用 Vue 的全局可用组合函数,并且让我们的函数可以轻松地在多个组件中使用以实现相同的结果。
让我们进一步研究之前的计数器和宽度示例——通过创建并封装可跨组件、重用的共享逻辑的组合式函数。
首先,我们创建一个名为 useCounter
的组合式函数,它封装了计数器功能并返回 count
的当前值、一个 increment()
方法和一个 decrement()
方法。
按照惯例,组合式函数名称以 “use” 关键字开头。
import { ref } from "vue";
export function useCounter(initialCount = 0) {
const count = ref(initialCount);
function increment() {
count.value++;
}
function decrement() {
count.value--;
}
return {
count,
increment,
decrement,
};
}
同样,我们可以创建一个名为 useWidth()
的组合式函数,它封装了应用的宽度功能。
import { ref, onMounted, onBeforeUnmount } from "vue";
export function useWidth() {
const width = ref(0);
function handleResize() {
width.value = window.innerWidth;
}
onMounted(() => {
handleResize();
window.addEventListener("resize", handleResize);
});
onBeforeUnmount(() => {
window.removeEventListener("resize", handleResize);
});
return {
width,
};
}
在我们的 App
组件中,我们现在可以使用可组合函数来实现相同的结果:
<template>
<div class="App">
<Count :count="count" :increment="increment" :decrement="decrement" />
<div id="divider" />
<Width :width="width" />
</div>
</template>
<script>
import Count from "./components/Count.vue";
import Width from "./components/Width.vue";
import { useCounter } from "./composables/useCounter";
import { useWidth } from "./composables/useWidth";
export default {
name: "App",
components: {
Count,
Width,
},
setup() {
const { count, increment, decrement } = useCounter(0);
const { width } = useWidth();
return {
count,
increment,
decrement,
width,
};
},
};
</script>
通过这些更改,我们的应用程序的功能将与以前相同,但更具有组合式的风味。
通过在组合式 API 中使用组合式函数,我们能够将应用的上下文分解为更小的、可重用的部分,从而分隔逻辑。
让我们将刚刚所做的更改进行可视化处理,与最初的选项式 API 示例组件进行比较。
在 Vue 中使用组合式函数可以更轻松地将组件的逻辑分成几个更小的部分。现在,重用相同的状态逻辑变得很容易,因为我们不再局限于在选项式 API 中的特定选项内组织代码。
借助组合式函数,我们可以灵活地提取和重用跨组件的共享逻辑。这种关注点分离使我们能够专注于每个组合式函数中的特定功能,从而使我们的代码更加模块化和可维护。
通过将逻辑分解为更小的、可重用的部分,我们可以使用这些组合式函数来构建组件,将必要的功能组合在一起,而无需重复代码。这种方法提高了代码的可重用性,并降低了代码重复和不一致导致的风险。
此外,使用组合式 API 可以提高组件逻辑的可读性和可理解性。每个组合式函数都封装了组件行为的特定方面,使其更容易推理和测试。随着代码变得更加结构化和组织化,它还允许团队成员之间更轻松地协作。
最后,使用组合式 API 构建 Vue 应用程序可以实现更好的类型推断。由于组合式 API 帮助我们使用变量和标准 JavaScript 函数处理组件逻辑,因此使用 TypeScript 等静态类型系统构建大型 Vue 应用程序变得更加容易!
实用资源
转载自:https://juejin.cn/post/7360586351815360549