一、为什么你的页面总是“卡顿”不堪?
还记得当年用jQuery操作DOM的日子吗?每次更新页面数据,都要手动找到对应元素,像在垃圾堆里翻找钥匙。而Vue的组件化开发,就像把家具分门别类放进了带标签的收纳盒——需要什么,直接取用整个盒子。
举个栗子:电影购票APP中,每个电影卡片都要显示海报、评分、片名。传统做法是写N遍相似代码,而Vue组件只需这样:
<!-- MovieCard.vue -->
<template>
<div class="movie-card" @click="handleClick">
<img :src="movie.poster" :alt="movie.title">
<h3>{{ movie.title }}</h3>
<span class="rating">{{ movie.rating }}</span>
<button>选座购票</button>
</div>
</template>
<script>
export default {
props: ['movie'],
methods: {
handleClick() {
this.$emit('select-movie', this.movie)
}
}
}
</script>
看,一个电影卡片组件就诞生了!要用的时候,像玩积木一样拼接即可。
二、路由:给你的APP装上“GPS导航系统”
单页面应用(SPA)就像没有隔断的大开间,所有功能挤在一起。而Vue Router就是帮你划分房间的室内设计师,让每个功能都有独立空间。
路由配置基础版:
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import MovieList from '@/views/MovieList.vue'
import MovieDetail from '@/views/MovieDetail.vue'
const routes = [
{
path: '/',
redirect: '/movies' // 默认跳转到电影列表
},
{
path: '/movies',
name: 'MovieList',
component: MovieList
},
{
path: '/movie/:id', // 动态路由参数
name: 'MovieDetail',
component: MovieDetail,
props: true // 神奇小开关,让路由参数自动变成props
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
三、电影页面组件:细节决定用户体验
3.1 电影列表页:不只是简单的数据展示
电影列表页可不是简单循环输出就完事了,得考虑这些:
搜索过滤功能:
<!-- MovieList.vue -->
<template>
<div class="movie-list">
<div class="search-box">
<input
v-model="searchText"
placeholder="找你想看的..."
@input="filterMovies"
>
</div>
<div class="movies-container">
<MovieCard
v-for="movie in filteredMovies"
:key="movie.id"
:movie="movie"
@select-movie="goToDetail"
/>
</div>
<!-- 加载更多 -->
<button
v-if="hasMore"
@click="loadMore"
class="load-more"
>
{{ loading ? '加载中...' : '查看更多' }}
</button>
</div>
</template>
<script>
import { ref, computed, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import MovieCard from '@/components/MovieCard.vue'
export default {
components: { MovieCard },
setup() {
const router = useRouter()
const allMovies = ref([])
const filteredMovies = ref([])
const searchText = ref('')
const loading = ref(false)
const page = ref(1)
const hasMore = ref(true)
// 过滤电影
const filterMovies = computed(() => {
if (!searchText.value) return allMovies.value
return allMovies.value.filter(movie =>
movie.title.includes(searchText.value)
)
})
// 跳转详情页
const goToDetail = (movie) => {
router.push(`/movie/${movie.id}`)
}
// 加载更多数据
const loadMore = async () => {
loading.value = true
// 模拟API调用
const newMovies = await fetchMovies(page.value + 1)
allMovies.value.push(...newMovies)
page.value++
hasMore.value = newMovies.length > 0
loading.value = false
}
onMounted(async () => {
allMovies.value = await fetchMovies(1)
})
return {
filteredMovies,
searchText,
loading,
hasMore,
goToDetail,
loadMore
}
}
}
</script>
3.2 电影详情页:让用户一秒入戏
详情页是转化关键,设计要像电影预告片一样抓人眼球:
<!-- MovieDetail.vue -->
<template>
<div v-if="movie" class="movie-detail">
<!-- 背景大图营造氛围 -->
<div class="hero-section" :style="{ backgroundImage: `url(${movie.backdrop})` }">
<div class="movie-info">
<img :src="movie.poster" class="poster">
<div class="detail-text">
<h1>{{ movie.title }}</h1>
<div class="meta">
<span class="rating">⭐ {{ movie.rating }}</span>
<span>{{ movie.duration }}分钟</span>
<span>{{ movie.genre.join(' / ') }}</span>
</div>
<p class="plot">{{ movie.plot }}</p>
<button @click="showSeatSelection" class="book-btn">立即购票</button>
</div>
</div>
</div>
<!-- 演职员表横向滚动 -->
<section class="cast-section">
<h2>主演</h2>
<div class="cast-scroll">
<div v-for="actor in movie.cast" :key="actor.id" class="actor-card">
<img :src="actor.avatar">
<p>{{ actor.name }}</p>
</div>
</div>
</section>
</div>
<div v-else class="loading">加载中...</div>
</template>
<script>
import { ref, onMounted } from 'vue'
import { useRoute } from 'vue-router'
export default {
props: ['id'], // 接收路由参数
setup(props) {
const route = useRoute()
const movie = ref(null)
const fetchMovieDetail = async (id) => {
// 模拟API调用
const response = await fetch(`/api/movies/${id}`)
movie.value = await response.json()
}
const showSeatSelection = () => {
// 跳转到选座页面
}
onMounted(() => {
// 优先使用props,没有则从路由获取
const movieId = props.id || route.params.id
fetchMovieDetail(movieId)
})
return {
movie,
showSeatSelection
}
}
}
</script>
四、高级技巧:让你的APP更智能
4.1 路由守卫:给页面加个“门卫”
有些页面需要登录才能访问,比如选座页面:
// router/index.js 中添加
router.beforeEach((to, from, next) => {
const requiresAuth = to.matched.some(record => record.meta.requiresAuth)
const isLoggedIn = checkLoginStatus() // 检查登录状态
if (requiresAuth && !isLoggedIn) {
next('/login?redirect=' + to.path)
} else {
next()
}
})
// 路由配置中添加meta
{
path: '/select-seats/:sessionId',
component: SelectSeats,
meta: { requiresAuth: true }
}
4.2 懒加载:让首屏飞快起来
电影详情页这种不一定每次都会访问的页面,可以用懒加载:
{
path: '/movie/:id',
component: () => import('@/views/MovieDetail.vue') // 魔法发生在这里
}
五、避坑指南:前人踩过的雷,你别再踩
- 路由参数变化组件不更新?
watch: {
'$route.params.id': {
handler(newId) {
this.fetchData(newId)
},
immediate: true
}
}
-
- 问题:从
/movie/1跳转到/movie/2,组件没重新渲染 - 解决:在组件内监听路由参数变化
- 问题:从
- 组件样式污染?
<style scoped>
.movie-card {
/* 这里的样式只在本组件生效 */
}
</style>
-
- 解决:使用scoped样式
- 图片加载慢?
<img
:src="movie.poster"
:alt="movie.title"
loading="lazy"
@error="handleImageError"
>
-
- 解决:使用懒加载和占位图
六、完整项目结构参考
movie-app/
├── public/
├── src/
│ ├── components/
│ │ ├── MovieCard.vue
│ │ └── AppHeader.vue
│ ├── views/
│ │ ├── MovieList.vue
│ │ ├── MovieDetail.vue
│ │ └── SelectSeats.vue
│ ├── router/
│ │ └── index.js
│ └── App.vue
在App.vue中记得加上路由出口:
<template>
<div id="app">
<AppHeader />
<router-view /> <!-- 这里显示当前路由对应的组件 -->
</div>
</template>
结语:从“会用”到“精通”的关键一步
组件化开发不是炫技,而是实实在在的提升开发效率和维护性。当你把页面拆分成一个个独立的组件,就像拥有了一个乐高工具箱——下次要做类似功能时,直接拿出积木块重新组合。
现在就去重构你的项目吧!当看到代码变得清晰可维护时,你会感谢今天认真学习组件化的自己。
彩蛋:遇到问题不要慌,记住这个万能口诀——“数据驱动视图,路由管理跳转,组件负责展示”。掌握了这三条核心原则,Vue开发路上基本可以横着走了!
818

被折叠的 条评论
为什么被折叠?



