Vue基础教程(217)电影购票APP开发实战之设计项目页面组件及路由配置中的电影页面组件及路由:别再用静态页面了!手把手教你用Vue造个会“变装”的电影票APP

一、为什么你的页面总是“卡顿”不堪?

还记得当年用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') // 魔法发生在这里
}

五、避坑指南:前人踩过的雷,你别再踩

  1. 路由参数变化组件不更新
watch: {
  '$route.params.id': {
    handler(newId) {
      this.fetchData(newId)
    },
    immediate: true
  }
}
    • 问题:从 /movie/1 跳转到 /movie/2,组件没重新渲染
    • 解决:在组件内监听路由参数变化
  1. 组件样式污染
<style scoped>
.movie-card {
  /* 这里的样式只在本组件生效 */
}
</style>
    • 解决:使用scoped样式
  1. 图片加载慢
<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开发路上基本可以横着走了!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

值引力

持续创作,多谢支持!

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

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

打赏作者

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

抵扣说明:

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

余额充值