vue2.0中如何利用vue-router构建一个列表页

发布时间:2022-05-05 18:18:34 作者:zzz
来源:亿速云 阅读:244
# Vue2.0中如何利用vue-router构建一个列表页

## 前言

在现代前端开发中,单页应用(SPA)已经成为主流开发模式。Vue.js作为当前最流行的前端框架之一,配合vue-router可以轻松实现SPA的路由管理。本文将详细介绍如何在Vue2.0项目中利用vue-router构建一个功能完善的列表页,涵盖从基础配置到高级功能的完整实现过程。

## 一、项目初始化与路由基础配置

### 1.1 创建Vue项目

首先我们需要创建一个基于Vue2.0的项目:

```bash
vue create list-page-demo

选择Vue2模板进行安装:

Vue CLI v4.5.15
? Please pick a preset: 
  Default ([Vue 2] babel, eslint) 
  Default (Vue 3) ([Vue 3] babel, eslint)
> Manually select features

1.2 安装vue-router

在项目目录下安装vue-router:

npm install vue-router@3.5.2 --save

注意:Vue2项目需要使用vue-router 3.x版本,Vue3对应的是vue-router 4.x

1.3 基本路由配置

src目录下创建router文件夹,并新建index.js文件:

import Vue from 'vue'
import VueRouter from 'vue-router'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: Home
  },
  {
    path: '/list',
    name: 'List',
    component: () => import('../views/List.vue')
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes
})

export default router

1.4 挂载路由到Vue实例

main.js中引入并挂载路由:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

二、列表页基础结构搭建

2.1 创建列表组件

views目录下创建List.vue文件:

<template>
  <div class="list-container">
    <h2>商品列表</h2>
    <ul class="list-items">
      <li v-for="item in items" :key="item.id" class="list-item">
        {{ item.name }}
      </li>
    </ul>
  </div>
</template>

<script>
export default {
  name: 'ListPage',
  data() {
    return {
      items: [
        { id: 1, name: '商品1' },
        { id: 2, name: '商品2' },
        { id: 3, name: '商品3' }
      ]
    }
  }
}
</script>

<style scoped>
.list-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}
.list-items {
  list-style: none;
  padding: 0;
}
.list-item {
  padding: 10px;
  border-bottom: 1px solid #eee;
}
</style>

2.2 添加导航链接

App.vue中添加导航:

<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link> |
      <router-link to="/list">列表页</router-link>
    </nav>
    <router-view/>
  </div>
</template>

三、动态路由与参数传递

3.1 配置动态路由

修改router/index.js,添加详情页路由:

{
  path: '/detail/:id',
  name: 'Detail',
  component: () => import('../views/Detail.vue'),
  props: true
}

3.2 列表项添加跳转链接

修改List.vue中的列表项:

<li v-for="item in items" :key="item.id" class="list-item">
  <router-link :to="'/detail/' + item.id">
    {{ item.name }}
  </router-link>
</li>

3.3 创建详情页组件

新建views/Detail.vue

<template>
  <div class="detail-container">
    <h2>商品详情</h2>
    <div v-if="item">
      <h3>{{ item.name }}</h3>
      <p>ID: {{ item.id }}</p>
      <button @click="goBack">返回列表</button>
    </div>
    <div v-else>
      商品不存在
    </div>
  </div>
</template>

<script>
export default {
  name: 'DetailPage',
  props: ['id'],
  data() {
    return {
      items: [
        { id: 1, name: '商品1' },
        { id: 2, name: '商品2' },
        { id: 3, name: '商品3' }
      ]
    }
  },
  computed: {
    item() {
      return this.items.find(item => item.id === Number(this.id))
    }
  },
  methods: {
    goBack() {
      this.$router.go(-1)
    }
  }
}
</script>

四、列表页高级功能实现

4.1 分页功能

4.1.1 添加分页组件

components目录下创建Pagination.vue

<template>
  <div class="pagination">
    <button 
      :disabled="currentPage === 1" 
      @click="changePage(currentPage - 1)"
    >
      上一页
    </button>
    <span>第 {{ currentPage }} 页 / 共 {{ totalPages }} 页</span>
    <button 
      :disabled="currentPage === totalPages" 
      @click="changePage(currentPage + 1)"
    >
      下一页
    </button>
  </div>
</template>

<script>
export default {
  name: 'Pagination',
  props: {
    currentPage: {
      type: Number,
      required: true
    },
    totalPages: {
      type: Number,
      required: true
    }
  },
  methods: {
    changePage(page) {
      this.$emit('page-change', page)
    }
  }
}
</script>

4.1.2 在列表页中使用分页

修改List.vue

<template>
  <div class="list-container">
    <h2>商品列表</h2>
    <ul class="list-items">
      <li v-for="item in paginatedItems" :key="item.id" class="list-item">
        <router-link :to="'/detail/' + item.id">
          {{ item.name }}
        </router-link>
      </li>
    </ul>
    <Pagination
      :current-page="currentPage"
      :total-pages="totalPages"
      @page-change="handlePageChange"
    />
  </div>
</template>

<script>
import Pagination from '@/components/Pagination.vue'

export default {
  components: {
    Pagination
  },
  data() {
    return {
      items: [
        // 模拟大量数据
        ...Array.from({ length: 50 }, (_, i) => ({
          id: i + 1,
          name: `商品${i + 1}`
        }))
      ],
      pageSize: 10,
      currentPage: 1
    }
  },
  computed: {
    totalPages() {
      return Math.ceil(this.items.length / this.pageSize)
    },
    paginatedItems() {
      const start = (this.currentPage - 1) * this.pageSize
      const end = start + this.pageSize
      return this.items.slice(start, end)
    }
  },
  methods: {
    handlePageChange(page) {
      this.currentPage = page
      // 更新URL但不重新加载组件
      this.$router.push({ query: { page } }).catch(() => {})
    }
  },
  created() {
    // 从URL查询参数中获取当前页码
    this.currentPage = Number(this.$route.query.page) || 1
  }
}
</script>

4.2 路由守卫控制

添加路由守卫,在进入列表页前检查权限:

// router/index.js
router.beforeEach((to, from, next) => {
  if (to.name === 'List' && !localStorage.getItem('token')) {
    next({ name: 'Home' })
  } else {
    next()
  }
})

4.3 滚动行为控制

在路由配置中添加滚动行为控制:

const router = new VueRouter({
  // ...其他配置
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else if (to.hash) {
      return {
        selector: to.hash
      }
    } else {
      return { x: 0, y: 0 }
    }
  }
})

五、性能优化

5.1 路由懒加载

我们已经使用了动态导入语法实现路由懒加载:

component: () => import('../views/List.vue')

5.2 列表项懒加载

对于长列表,可以使用vue-lazyload插件:

npm install vue-lazyload --save

main.js中配置:

import VueLazyload from 'vue-lazyload'

Vue.use(VueLazyload, {
  preLoad: 1.3,
  error: 'error.png',
  loading: 'loading.gif',
  attempt: 1
})

在列表中使用:

<li v-for="item in items" :key="item.id">
  <img v-lazy="item.imageUrl" alt="">
</li>

5.3 路由组件缓存

使用keep-alive缓存列表页:

<template>
  <div id="app">
    <!-- ... -->
    <keep-alive include="ListPage">
      <router-view/>
    </keep-alive>
  </div>
</template>

List.vue中添加name属性:

export default {
  name: 'ListPage',
  // ...
}

六、常见问题与解决方案

6.1 路由重复导航错误

在分页跳转时可能会遇到:

NavigationDuplicated: Avoided redundant navigation to current location

解决方案:

this.$router.push({ query: { page } }).catch(() => {})

6.2 动态路由参数变化组件不更新

当仅路由参数变化时,组件不会重新创建,解决方案:

watch: {
  '$route'(to, from) {
    if (to.params.id !== from.params.id) {
      this.fetchData()
    }
  }
}

6.3 路由模式选择

七、完整示例代码

7.1 最终路由配置

import Vue from 'vue'
import VueRouter from 'vue-router'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
    meta: { title: '首页' }
  },
  {
    path: '/list',
    name: 'List',
    component: () => import('../views/List.vue'),
    meta: { 
      title: '商品列表',
      requiresAuth: true
    }
  },
  {
    path: '/detail/:id',
    name: 'Detail',
    component: () => import('../views/Detail.vue'),
    props: true,
    meta: { title: '商品详情' }
  },
  {
    path: '*',
    redirect: '/'
  }
]

const router = new VueRouter({
  mode: 'history',
  base: process.env.BASE_URL,
  routes,
  scrollBehavior(to, from, savedPosition) {
    if (savedPosition) {
      return savedPosition
    } else {
      return { x: 0, y: 0 }
    }
  }
})

router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || '默认标题'
  
  // 验证需要登录的路由
  if (to.matched.some(record => record.meta.requiresAuth)) {
    if (!localStorage.getItem('token')) {
      next({ name: 'Home' })
      return
    }
  }
  
  next()
})

export default router

7.2 列表页完整实现

<template>
  <div class="list-container">
    <h2>商品列表</h2>
    
    <div class="filter-controls">
      <input 
        v-model="searchQuery" 
        placeholder="搜索商品..."
        @input="handleSearch"
      >
      <select v-model="sortBy" @change="handleSortChange">
        <option value="name">按名称排序</option>
        <option value="id">按ID排序</option>
      </select>
    </div>
    
    <div v-if="loading" class="loading">加载中...</div>
    
    <template v-else>
      <ul class="list-items">
        <li 
          v-for="item in paginatedItems" 
          :key="item.id" 
          class="list-item"
        >
          <router-link :to="{ name: 'Detail', params: { id: item.id }}">
            {{ item.name }}
          </router-link>
          <span class="item-id">#{{ item.id }}</span>
        </li>
      </ul>
      
      <Pagination
        :current-page="currentPage"
        :total-pages="totalPages"
        @page-change="handlePageChange"
      />
    </template>
  </div>
</template>

<script>
import Pagination from '@/components/Pagination.vue'
import { fetchItems } from '@/api/items'

export default {
  name: 'ListPage',
  components: {
    Pagination
  },
  data() {
    return {
      items: [],
      filteredItems: [],
      loading: true,
      pageSize: 10,
      currentPage: 1,
      searchQuery: '',
      sortBy: 'id'
    }
  },
  computed: {
    totalPages() {
      return Math.ceil(this.filteredItems.length / this.pageSize)
    },
    paginatedItems() {
      const start = (this.currentPage - 1) * this.pageSize
      const end = start + this.pageSize
      return this.filteredItems.slice(start, end)
    }
  },
  methods: {
    async fetchData() {
      try {
        this.loading = true
        const response = await fetchItems()
        this.items = response.data
        this.filteredItems = [...this.items]
        this.sortItems()
      } catch (error) {
        console.error('获取数据失败:', error)
      } finally {
        this.loading = false
      }
    },
    handleSearch() {
      this.filteredItems = this.items.filter(item => 
        item.name.toLowerCase().includes(this.searchQuery.toLowerCase())
      )
      this.currentPage = 1
    },
    handleSortChange() {
      this.sortItems()
    },
    sortItems() {
      this.filteredItems.sort((a, b) => {
        if (this.sortBy === 'name') {
          return a.name.localeCompare(b.name)
        } else {
          return a.id - b.id
        }
      })
    },
    handlePageChange(page) {
      this.currentPage = page
      this.$router.push({ 
        name: 'List',
        query: { 
          page,
          search: this.searchQuery,
          sort: this.sortBy
        }
      }).catch(() => {})
    }
  },
  created() {
    const { page = 1, search = '', sort = 'id' } = this.$route.query
    this.currentPage = Number(page)
    this.searchQuery = search
    this.sortBy = sort
    
    this.fetchData()
  },
  watch: {
    '$route.query'(newQuery) {
      const { page = 1, search = '', sort = 'id' } = newQuery
      this.currentPage = Number(page)
      this.searchQuery = search
      this.sortBy = sort
    }
  }
}
</script>

<style scoped>
.list-container {
  max-width: 800px;
  margin: 0 auto;
  padding: 20px;
}

.filter-controls {
  margin: 20px 0;
  display: flex;
  gap: 10px;
}

.list-items {
  list-style: none;
  padding: 0;
}

.list-item {
  padding: 12px;
  border-bottom: 1px solid #eee;
  display: flex;
  justify-content: space-between;
}

.item-id {
  color: #999;
  font-size: 0.9em;
}

.loading {
  text-align: center;
  padding: 20px;
  color: #666;
}
</style>

结语

通过本文的详细介绍,我们学习了如何在Vue2.0项目中利用vue-router构建一个功能完善的列表页。从基础的路由配置到高级的分页、搜索、排序功能实现,再到性能优化和常见问题解决,涵盖了实际开发中的主要场景。希望本文能帮助你在实际项目中更好地使用vue-router构建列表页面。

在实际开发中,还可以根据需求进一步扩展功能,如: - 无限滚动加载 - 多条件筛选 - 表格视图与卡片视图切换 - 服务端分页与过滤等

Vue-router作为Vue生态的核心组件之一,熟练掌握它将大大提高你的开发效率和项目质量。 “`

推荐阅读:
  1. 利用Dockerfile构建一个nginx容器
  2. 利用Vue2.0怎么实现一个分页跳转效果

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

vue vue-router

上一篇:vue如何设置导航栏、侧边栏为公共页面

下一篇:vue中如何实现鼠标悬停事件

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》