嗨,各位奋战在前端一线的小伙伴们!是不是经常面对Vue官方文档一脸懵逼,感觉每个字都认识,连起来就不知道在说啥?别慌,今天咱们就抛开那些枯燥的理论,直接上手撸一个电影购票APP,重点攻克那个看似简单却暗藏玄机的——“我的页面”。
相信我,看完这篇,你会忍不住感叹:原来Vue可以这么玩!
一、需求来袭:产品经理的“简单”需求
先来看看场景:产品经理扶了扶眼镜,轻描淡写地说:“这个我的页面很简单,就显示用户头像、昵称、几个功能入口,再做个未登录状态就行。”
(内心OS:简单?光未登录判断就能写出八种bug好吗!)
但作为优秀的开发者,我们要微笑着接过需求,然后…在代码里尽情发挥。先来拆解一下“我的页面”需要哪些要素:
- 用户信息区:头像、昵称(未登录时显示默认头像和“点击登录”)
- 功能入口:比如“我的订单”、“我的收藏”、“观影券”、“设置”等
- 登出按钮:登录后才显示
- 状态管理:需要判断用户是否登录,展示不同内容
二、组件设计:先把架子搭起来
在设计Vue组件时,咱们要有个核心思想——像搭积木一样思考。把页面拆成一个个小积木,最后组合起来。
1. 我的页面组件结构设计
对于“我的页面”,我们可以这样设计组件结构:
<template>
<div class="profile-page">
<!-- 用户信息卡片 -->
<div class="user-card">
<div class="avatar-section">
<img :src="userInfo.avatar || defaultAvatar" alt="头像" class="avatar">
<div class="user-info">
<h3 v-if="isLogin">{{ userInfo.nickname }}</h3>
<h3 v-else class="login-tip" @click="handleLogin">点击登录</h3>
<p v-if="isLogin" class="vip-level">VIP等级: {{ userInfo.vipLevel }}</p>
</div>
</div>
</div>
<!-- 功能列表 -->
<div class="function-list">
<div class="function-item" @click="navigateTo('/orders')">
<span class="icon">🎫</span>
<span>我的订单</span>
<span class="arrow">></span>
</div>
<div class="function-item" @click="navigateTo('/favorites')">
<span class="icon">❤️</span>
<span>我的收藏</span>
<span class="arrow">></span>
</div>
<div class="function-item" @click="navigateTo('/coupons')">
<span class="icon">🎁</span>
<span>观影券</span>
<span class="arrow">></span>
</div>
<div class="function-item" @click="navigateTo('/settings')">
<span class="icon">⚙️</span>
<span>设置</span>
<span class="arrow">></span>
</div>
</div>
<!-- 退出登录按钮 -->
<button v-if="isLogin" class="logout-btn" @click="handleLogout">
退出登录
</button>
</div>
</template>
2. 逻辑处理部分
光有架子还不够,得让组件活起来:
<script>
export default {
name: 'ProfilePage',
data() {
return {
defaultAvatar: '/default-avatar.png',
userInfo: {
avatar: '',
nickname: '',
vipLevel: 0
}
}
},
computed: {
isLogin() {
return !!this.userInfo.nickname
}
},
methods: {
handleLogin() {
// 这里实际开发中会跳转到登录页
console.log('跳转到登录页面')
// 模拟登录成功
this.userInfo = {
avatar: '/user-avatar.jpg',
nickname: 'Vue大神',
vipLevel: 3
}
},
handleLogout() {
this.userInfo = {
avatar: '',
nickname: '',
vipLevel: 0
}
alert('已退出登录')
},
navigateTo(path) {
if (!this.isLogin && path !== '/settings') {
alert('请先登录')
return
}
this.$router.push(path)
}
},
mounted() {
// 组件加载时检查登录状态
this.checkLoginStatus()
}
}
</script>
3. 样式美化
颜值即正义!来点CSS让页面好看点:
<style scoped>
.profile-page {
min-height: 100vh;
background-color: #f5f5f5;
padding: 20px;
}
.user-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 15px;
padding: 30px 20px;
color: white;
margin-bottom: 20px;
}
.avatar-section {
display: flex;
align-items: center;
}
.avatar {
width: 60px;
height: 60px;
border-radius: 50%;
border: 3px solid rgba(255, 255, 255, 0.3);
margin-right: 15px;
}
.user-info h3 {
margin: 0 0 8px 0;
font-size: 18px;
}
.login-tip {
cursor: pointer;
transition: all 0.3s;
}
.login-tip:hover {
transform: scale(1.05);
}
.vip-level {
margin: 0;
font-size: 14px;
opacity: 0.8;
}
.function-list {
background: white;
border-radius: 12px;
overflow: hidden;
}
.function-item {
display: flex;
align-items: center;
padding: 16px 20px;
border-bottom: 1px solid #f0f0f0;
cursor: pointer;
transition: background-color 0.2s;
}
.function-item:hover {
background-color: #f8f8f8;
}
.function-item:last-child {
border-bottom: none;
}
.icon {
margin-right: 12px;
font-size: 18px;
}
.arrow {
margin-left: auto;
color: #999;
}
.logout-btn {
width: 100%;
padding: 12px;
background: #ff4757;
color: white;
border: none;
border-radius: 8px;
margin-top: 30px;
font-size: 16px;
cursor: pointer;
transition: background-color 0.3s;
}
.logout-btn:hover {
background: #ff3742;
}
</style>
三、路由配置:给页面找个家
组件设计好了,得告诉Vue什么时候显示它,这就是路由的活了。
1. 路由配置文件
在Vue项目中,我们通常有个专门的路由配置文件:
// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import ProfilePage from '@/views/ProfilePage.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
routes: [
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name: 'Home',
component: () => import('@/views/HomePage.vue')
},
{
path: '/profile',
name: 'Profile',
component: ProfilePage,
meta: {
requiresAuth: true // 需要登录才能访问
}
},
{
path: '/orders',
name: 'Orders',
component: () => import('@/views/OrdersPage.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/favorites',
name: 'Favorites',
component: () => import('@/views/FavoritesPage.vue'),
meta: {
requiresAuth: true
}
},
{
path: '/login',
name: 'Login',
component: () => import('@/views/LoginPage.vue')
}
// ...其他路由
]
})
2. 路由守卫:给页面加个保安
有些页面不是谁都能看的(比如我的页面需要登录),这时候就需要路由守卫出场了:
// 在router/index.js中添加路由守卫
router.beforeEach((to, from, next) => {
// 检查目标路由是否需要登录
if (to.matched.some(record => record.meta.requiresAuth)) {
// 这里应该检查用户实际登录状态,简单演示用localStorage
const isAuthenticated = localStorage.getItem('userToken') || false
if (!isAuthenticated) {
// 没登录?滚去登录页!
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
})
3. 在导航中使用路由
最后,别忘了在主导航中添加通往“我的页面”的入口:
<!-- 在App.vue或导航组件中 -->
<template>
<div id="app">
<nav class="main-nav">
<router-link to="/home" class="nav-item">首页</router-link>
<router-link to="/movies" class="nav-item">电影</router-link>
<router-link to="/profile" class="nav-item">我的</router-link>
</nav>
<router-view/>
</div>
</template>
四、实战技巧:那些官方文档不会告诉你的
1. 组件通信的骚操作
在电影APP中,用户登录状态可能在多个组件中使用,这时候用Vuex就很有必要了:
// store/index.js
export default new Vuex.Store({
state: {
user: null,
isLogin: false
},
mutations: {
setUser(state, userInfo) {
state.user = userInfo
state.isLogin = true
},
clearUser(state) {
state.user = null
state.isLogin = false
}
},
actions: {
login({ commit }, userInfo) {
commit('setUser', userInfo)
localStorage.setItem('userToken', 'mock-token')
},
logout({ commit }) {
commit('clearUser')
localStorage.removeItem('userToken')
}
}
})
然后在组件中使用:
<script>
import { mapState, mapActions } from 'vuex'
export default {
computed: {
...mapState(['user', 'isLogin'])
},
methods: {
...mapActions(['login', 'logout']),
handleLogin() {
// 调用Vuex action
this.login({
avatar: '/user-avatar.jpg',
nickname: 'Vue大神',
vipLevel: 3
})
},
handleLogout() {
this.logout()
this.$router.push('/home')
}
}
}
</script>
2. 路由懒加载:让应用飞起来
注意到上面路由配置中的() => import('@/views/xxx.vue')了吗?这就是路由懒加载,能显著提升首屏加载速度。
3. 动态路由:更灵活的权限控制
对于更复杂的权限系统,还可以使用动态路由:
// 根据用户权限动态添加路由
function addDynamicRoutes() {
const userRole = getUserRole() // 获取用户角色
if (userRole === 'vip') {
router.addRoute({
path: '/vip-center',
component: () => import('@/views/VipCenter.vue')
})
}
}
五、避坑指南:我踩过的坑你们就别踩了
- 路由刷新404:如果用history模式,服务器需要配置支持
- 组件样式污染:记得加
scoped,但深度选择器要用::v-deep - 路由守卫死循环:确保守卫逻辑有出口,别自己跳自己
- Vuex数据丢失:页面刷新时Vuex会重置,需要配合localStorage
六、完整示例:抄作业时间到!
由于篇幅限制,这里给出最关键的部分代码,完整项目可以到GitHub上查看:
<!-- src/views/ProfilePage.vue -->
<template>
<div class="profile-page">
<!-- 用户信息区 -->
<UserCard :user="user" @login="handleLogin" />
<!-- 功能列表 -->
<FunctionList :items="functionItems" @item-click="handleFunctionClick" />
<!-- 退出按钮 -->
<LogoutButton v-if="isLogin" @logout="handleLogout" />
</div>
</template>
<script>
import { mapState, mapActions } from 'vuex'
import UserCard from '@/components/UserCard.vue'
import FunctionList from '@/components/FunctionList.vue'
import LogoutButton from '@/components/LogoutButton.vue'
export default {
name: 'ProfilePage',
components: {
UserCard,
FunctionList,
LogoutButton
},
data() {
return {
functionItems: [
{ id: 1, name: '我的订单', icon: '🎫', path: '/orders' },
{ id: 2, name: '我的收藏', icon: '❤️', path: '/favorites' },
{ id: 3, name: '观影券', icon: '🎁', path: '/coupons' },
{ id: 4, name: '设置', icon: '⚙️', path: '/settings' }
]
}
},
computed: {
...mapState(['user', 'isLogin'])
},
methods: {
...mapActions(['login', 'logout']),
handleLogin() {
// 实际开发中跳转到登录页
this.login({
avatar: '/user-avatar.jpg',
nickname: 'Vue追剧达人',
vipLevel: 2
})
},
handleLogout() {
this.logout()
this.$message.success('已退出登录')
},
handleFunctionClick(item) {
if (!this.isLogin && item.path !== '/settings') {
this.$message.warning('请先登录')
return
}
this.$router.push(item.path)
}
}
}
</script>
总结
好了,到这里你应该已经掌握了Vue组件设计和路由配置的精髓。记住,好的组件设计是项目成功的一半,而合理的路由配置则是用户体验的保障。
其实Vue学习就像打游戏,一开始觉得难,但只要过了新手村,后面就越打越顺手。最重要的是——多写!多踩坑!多总结!
840

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



