您好,登录后才能下订单哦!
# 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
在项目目录下安装vue-router:
npm install vue-router@3.5.2 --save
注意:Vue2项目需要使用vue-router 3.x版本,Vue3对应的是vue-router 4.x
在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
在main.js
中引入并挂载路由:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
new Vue({
router,
render: h => h(App)
}).$mount('#app')
在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>
在App.vue
中添加导航:
<template>
<div id="app">
<nav>
<router-link to="/">首页</router-link> |
<router-link to="/list">列表页</router-link>
</nav>
<router-view/>
</div>
</template>
修改router/index.js
,添加详情页路由:
{
path: '/detail/:id',
name: 'Detail',
component: () => import('../views/Detail.vue'),
props: true
}
修改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>
新建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>
在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>
修改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>
添加路由守卫,在进入列表页前检查权限:
// router/index.js
router.beforeEach((to, from, next) => {
if (to.name === 'List' && !localStorage.getItem('token')) {
next({ name: 'Home' })
} else {
next()
}
})
在路由配置中添加滚动行为控制:
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 }
}
}
})
我们已经使用了动态导入语法实现路由懒加载:
component: () => import('../views/List.vue')
对于长列表,可以使用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>
使用keep-alive
缓存列表页:
<template>
<div id="app">
<!-- ... -->
<keep-alive include="ListPage">
<router-view/>
</keep-alive>
</div>
</template>
在List.vue
中添加name属性:
export default {
name: 'ListPage',
// ...
}
在分页跳转时可能会遇到:
NavigationDuplicated: Avoided redundant navigation to current location
解决方案:
this.$router.push({ query: { page } }).catch(() => {})
当仅路由参数变化时,组件不会重新创建,解决方案:
watch: {
'$route'(to, from) {
if (to.params.id !== from.params.id) {
this.fetchData()
}
}
}
hash
模式:兼容性好,URL中有#
history
模式:URL美观,需要服务器支持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
<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生态的核心组件之一,熟练掌握它将大大提高你的开发效率和项目质量。 “`
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。