Vue Composition API 实际项目中的典型应用场景
Vue Composition API 实际项目中的典型应用场景
状态管理与逻辑复用
在传统的 Vue 开发中,当组件逻辑变得复杂,特别是涉及到多个组件间共享逻辑时,代码维护和复用会变得棘手。Vue Composition API 通过 setup
函数和 reactive
、ref
等方法,为状态管理和逻辑复用提供了更高效的方式。
组件内状态管理
在一个简单的计数器组件中,使用 Vue Composition API 可以这样管理状态:
<template>
<div>
<p>Count: {{ count.value }}</p>
<button @click="increment">Increment</button>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const count = ref(0);
const increment = () => {
count.value++;
};
return {
count,
increment
};
}
};
</script>
在上述代码中,通过 ref
创建了一个响应式的 count
变量。ref
会自动将传入的值包装成一个响应式对象,访问和修改时需要通过 .value
。setup
函数返回一个对象,对象中的属性和方法可以在模板中直接使用。
逻辑复用 - 自定义 Hook
假设在多个组件中都需要处理页面滚动时的某些逻辑,比如判断页面是否滚动到了底部加载更多数据。可以通过自定义 Hook 来复用这一逻辑。
// useScroll.js
import { ref, onMounted, onUnmounted } from 'vue';
export const useScroll = () => {
const isBottom = ref(false);
const handleScroll = () => {
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop;
const clientHeight = document.documentElement.clientHeight;
const scrollHeight = document.documentElement.scrollHeight;
isBottom.value = scrollTop + clientHeight >= scrollHeight - 100;
};
onMounted(() => {
window.addEventListener('scroll', handleScroll);
});
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
return {
isBottom
};
};
在组件中使用这个 Hook:
<template>
<div>
<p v-if="isBottom">You are near the bottom of the page.</p>
</div>
</template>
<script>
import { useScroll } from './useScroll';
export default {
setup() {
const { isBottom } = useScroll();
return {
isBottom
};
}
};
</script>
通过自定义 Hook,将页面滚动相关的逻辑封装起来,多个组件可以轻松复用,提高了代码的可维护性和复用性。
生命周期管理
Vue Composition API 为生命周期管理带来了更加直观和灵活的方式。在 setup
函数中,可以通过引入不同的生命周期钩子函数来处理组件不同阶段的逻辑。
常用生命周期钩子的使用
onMounted
:组件挂载后执行,常用于初始化一些需要 DOM 元素的操作。
<template>
<div id="myDiv">This is a div</div>
</template>
<script>
import { onMounted } from 'vue';
export default {
setup() {
onMounted(() => {
const div = document.getElementById('myDiv');
if (div) {
div.style.color = 'red';
}
});
}
};
</script>
onUpdated
:组件更新后执行,当组件的数据发生变化重新渲染后,会触发这个钩子。比如在数据更新后可能需要重新计算一些 DOM 元素的位置或样式。
<template>
<div>
<input v-model="text" />
<p ref="textParagraph">{{ text }}</p>
</div>
</template>
<script>
import { ref, onUpdated } from 'vue';
export default {
setup() {
const text = ref('');
const textParagraph = ref(null);
onUpdated(() => {
if (textParagraph.value) {
const width = textParagraph.value.offsetWidth;
console.log(`The width of the paragraph is ${width}`);
}
});
return {
text,
textParagraph
};
}
};
</script>
onUnmounted
:组件卸载时执行,用于清理一些在组件挂载期间创建的副作用,比如移除事件监听器。
<template>
<div></div>
</template>
<script>
import { onMounted, onUnmounted } from 'vue';
export default {
setup() {
const handleClick = () => {
console.log('Clicked outside the component');
};
onMounted(() => {
document.addEventListener('click', handleClick);
});
onUnmounted(() => {
document.removeEventListener('click', handleClick);
});
}
};
</script>
响应式数据处理与计算属性
Vue Composition API 提供了强大的响应式数据处理能力,结合计算属性可以实现复杂的数据处理和展示逻辑。
响应式数据的深度监听
在处理复杂对象或数组时,reactive
可以创建深度响应式的数据。
<template>
<div>
<input v-model="user.name" />
<input v-model="user.age" />
<p>{{ user.name }} is {{ user.age }} years old.</p>
</div>
</template>
<script>
import { reactive } from 'vue';
export default {
setup() {
const user = reactive({
name: 'John',
age: 30
});
return {
user
};
}
};
</script>
上述代码中,user
对象是深度响应式的,当 user.name
或 user.age
发生变化时,模板会自动更新。
计算属性
计算属性在 Vue Composition API 中通过 computed
方法实现。比如在一个购物车组件中,计算商品总价。
<template>
<div>
<input type="number" v-model="quantity" />
<input type="number" v-model="price" />
<p>Total: {{ totalPrice.value }}</p>
</div>
</template>
<script>
import { ref, computed } from 'vue';
export default {
setup() {
const quantity = ref(1);
const price = ref(10);
const totalPrice = computed(() => {
return quantity.value * price.value;
});
return {
quantity,
price,
totalPrice
};
}
};
</script>
computed
会缓存计算结果,只有当依赖的响应式数据(这里是 quantity
和 price
)发生变化时,才会重新计算。
依赖注入
依赖注入在 Vue 中是一种在组件树中共享数据的方式。Vue Composition API 对依赖注入的支持使得这一过程更加简洁和高效。
简单的依赖注入示例
假设在一个应用中有一个全局的配置对象,多个组件可能需要使用这个配置。
// main.js
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
const config = {
theme: 'light',
apiUrl: 'https://example.com/api'
};
app.provide('config', config);
app.mount('#app');
在子组件中使用注入的数据:
<template>
<div>
<p>The current theme is {{ config.theme }}</p>
<p>The API URL is {{ config.apiUrl }}</p>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const config = inject('config');
return {
config
};
}
};
</script>
通过 provide
在父级组件中提供数据,通过 inject
在子组件中获取数据,实现了数据在组件树中的共享。
复杂依赖注入场景 - 多层组件传递
当组件嵌套层次较深时,依赖注入的优势更加明显。例如,在一个多层菜单组件中,顶层组件提供一些菜单配置,底层菜单项组件可以直接注入使用。
<!-- ParentComponent.vue -->
<template>
<div>
<ChildComponent />
</div>
</template>
<script>
import { provide } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: {
ChildComponent
},
setup() {
const menuConfig = {
menuItems: ['Home', 'About', 'Contact'],
menuStyle: 'horizontal'
};
provide('menuConfig', menuConfig);
}
};
</script>
<!-- ChildComponent.vue -->
<template>
<div>
<GrandChildComponent />
</div>
</template>
<script>
import GrandChildComponent from './GrandChildComponent.vue';
export default {
components: {
GrandChildComponent
}
};
</script>
<!-- GrandChildComponent.vue -->
<template>
<div>
<ul :style="{ display: menuConfig.menuStyle === 'horizontal'? 'flex' : 'block' }">
<li v-for="item in menuConfig.menuItems" :key="item">{{ item }}</li>
</ul>
</div>
</template>
<script>
import { inject } from 'vue';
export default {
setup() {
const menuConfig = inject('menuConfig');
return {
menuConfig
};
}
};
</script>
这样即使经过多层组件传递,底层组件依然可以方便地获取到顶层提供的数据,避免了通过层层 props 传递数据的繁琐。
表单处理
在实际项目中,表单处理是常见的需求。Vue Composition API 可以让表单处理的逻辑更加清晰和易于维护。
基本表单绑定
对于一个简单的文本输入表单,可以这样处理:
<template>
<div>
<input v-model="inputText" />
<p>You entered: {{ inputText }}</p>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const inputText = ref('');
return {
inputText
};
}
};
</script>
这里通过 ref
创建了一个响应式的 inputText
,实现了表单输入和数据的双向绑定。
复杂表单验证
在一个包含多个字段且需要验证的表单中,例如注册表单,需要验证用户名、邮箱和密码等。
<template>
<div>
<form @submit.prevent="submitForm">
<label>Username:</label>
<input v-model="formData.username" />
<p v-if="errors.username" class="error">{{ errors.username }}</p>
<label>Email:</label>
<input v-model="formData.email" />
<p v-if="errors.email" class="error">{{ errors.email }}</p>
<label>Password:</label>
<input type="password" v-model="formData.password" />
<p v-if="errors.password" class="error">{{ errors.password }}</p>
<button type="submit">Submit</button>
</form>
</div>
</template>
<script>
import { ref } from 'vue';
export default {
setup() {
const formData = ref({
username: '',
email: '',
password: ''
});
const errors = ref({
username: '',
email: '',
password: ''
});
const validateUsername = () => {
if (formData.value.username.length < 3) {
errors.value.username = 'Username must be at least 3 characters long';
} else {
errors.value.username = '';
}
};
const validateEmail = () => {
const re = /\S+@\S+\.\S+/;
if (!re.test(formData.value.email)) {
errors.value.email = 'Please enter a valid email address';
} else {
errors.value.email = '';
}
};
const validatePassword = () => {
if (formData.value.password.length < 6) {
errors.value.password = 'Password must be at least 6 characters long';
} else {
errors.value.password = '';
}
};
const submitForm = () => {
validateUsername();
validateEmail();
validatePassword();
if (!errors.value.username &&!errors.value.email &&!errors.value.password) {
console.log('Form submitted successfully:', formData.value);
}
};
return {
formData,
errors,
submitForm
};
}
};
</script>
在上述代码中,通过 ref
创建了 formData
和 errors
对象。分别编写了各个字段的验证函数,并在 submitForm
函数中调用这些验证函数。根据验证结果在模板中显示错误信息,实现了复杂表单的验证功能。
路由相关处理
在单页应用(SPA)开发中,路由是重要的组成部分。Vue Composition API 与 Vue Router 结合,可以更灵活地处理路由相关的逻辑。
获取当前路由信息
在组件中获取当前路由的参数、路径等信息。
<template>
<div>
<p>Current route path: {{ currentRoute.path }}</p>
<p>Current route parameter: {{ currentRoute.params.id }}</p>
</div>
</template>
<script>
import { useRoute } from 'vue-router';
export default {
setup() {
const currentRoute = useRoute();
return {
currentRoute
};
}
};
</script>
通过 useRoute
可以方便地获取到当前路由的详细信息,例如,当路由为 /user/:id
时,可以获取到 id
参数。
路由导航
在组件内实现路由导航,比如点击按钮跳转到另一个页面。
<template>
<div>
<button @click="goToAbout">Go to About Page</button>
</div>
</template>
<script>
import { useRouter } from 'vue-router';
export default {
setup() {
const router = useRouter();
const goToAbout = () => {
router.push('/about');
};
return {
goToAbout
};
}
};
</script>
通过 useRouter
获取到路由实例,然后调用 push
方法实现页面跳转。
路由守卫
在路由切换前进行一些验证或操作,例如检查用户是否登录。
// router.js
import { createRouter, createWebHistory } from 'vue-router';
import Home from './views/Home.vue';
import About from './views/About.vue';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About,
beforeEnter: (to, from, next) => {
const isLoggedIn = true; // 这里可以根据实际情况判断用户是否登录
if (isLoggedIn) {
next();
} else {
next('/login');
}
}
}
]
});
export default router;
在上述代码中,通过 beforeEnter
守卫,在进入 /about
路由前检查用户是否登录,如果未登录则跳转到 /login
页面。结合 Vue Composition API,在组件内也可以通过 router.beforeEach
等方法实现更复杂的全局路由守卫逻辑。
与第三方库的集成
在实际项目中,常常需要与各种第三方库集成。Vue Composition API 提供了一种清晰的方式来管理与第三方库相关的逻辑。
集成 Chart.js 绘制图表
以集成 Chart.js 绘制柱状图为例:
<template>
<div>
<canvas ref="chartCanvas"></canvas>
</div>
</template>
<script>
import { ref, onMounted, onUnmounted } from 'vue';
import Chart from 'chart.js/auto';
export default {
setup() {
const chartCanvas = ref(null);
let chartInstance = null;
onMounted(() => {
if (chartCanvas.value) {
const ctx = chartCanvas.value.getContext('2d');
chartInstance = new Chart(ctx, {
type: 'bar',
data: {
labels: ['Red', 'Blue', 'Yellow', 'Green', 'Purple', 'Orange'],
datasets: [
{
label: '# of Votes',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(255, 99, 132, 0.2)',
'rgba(54, 162, 235, 0.2)',
'rgba(255, 206, 86, 0.2)',
'rgba(75, 192, 192, 0.2)',
'rgba(153, 102, 255, 0.2)',
'rgba(255, 159, 64, 0.2)'
],
borderColor: [
'rgba(255, 99, 132, 1)',
'rgba(54, 162, 235, 1)',
'rgba(255, 206, 86, 1)',
'rgba(75, 192, 192, 1)',
'rgba(153, 102, 255, 1)',
'rgba(255, 159, 64, 1)'
],
borderWidth: 1
}
]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
}
});
onUnmounted(() => {
if (chartInstance) {
chartInstance.destroy();
}
});
return {
chartCanvas
};
}
};
</script>
在上述代码中,通过 ref
创建了 chartCanvas
引用,在 onMounted
钩子中初始化 Chart.js 图表,在 onUnmounted
钩子中销毁图表实例,实现了 Vue 组件与 Chart.js 的集成。
集成第三方 UI 库
以 Element Plus 为例,在 Vue 项目中使用其组件:
<template>
<div>
<el-button @click="showMessage">Click me</el-button>
</div>
</template>
<script>
import { defineComponent } from 'vue';
import { ElButton, ElMessage } from 'element-plus';
export default defineComponent({
components: {
ElButton
},
setup() {
const showMessage = () => {
ElMessage({
message: 'This is a message from Element Plus',
type:'success'
});
};
return {
showMessage
};
}
});
</script>
首先在组件中注册 ElButton
组件,然后在 setup
函数中使用 ElMessage
方法实现点击按钮弹出消息提示,展示了 Vue Composition API 与第三方 UI 库的集成方式。
通过以上各个典型应用场景的介绍,我们可以看到 Vue Composition API 在实际项目中为前端开发带来了更高的灵活性、可维护性和复用性,使得代码结构更加清晰,逻辑更加易于管理。无论是小型项目还是大型复杂项目,合理运用 Vue Composition API 都能提升开发效率和代码质量。