Vue基础教程(219)电影购票APP开发实战之设计项目页面组件及路由配置中的我的页面组件及路由:别再说学不会Vue了!手把手教你撸个电影APP,我的页面这样设计就对了

嗨,各位奋战在前端一线的小伙伴们!是不是经常面对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')
    })
  }
}

五、避坑指南:我踩过的坑你们就别踩了

  1. 路由刷新404:如果用history模式,服务器需要配置支持
  2. 组件样式污染:记得加scoped,但深度选择器要用::v-deep
  3. 路由守卫死循环:确保守卫逻辑有出口,别自己跳自己
  4. 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学习就像打游戏,一开始觉得难,但只要过了新手村,后面就越打越顺手。最重要的是——多写!多踩坑!多总结!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值