新闻客户端案例的实现,使用axios获取数据并渲染页面,路由传参(查询参数,动态路由),使用keep-alive实现组件缓存

文章目录

0.页面要求

一共两个页面:首页和详情页(一级路由),

其中,首页由新闻列表组成,点其中一个可以进入详情页,详情页有返回首页的按钮

底部导航能切换:列表,收藏,喜欢,我的(嵌套二级路由)

1.功能要求
  • 配置路由
  • 渲染首页
  • 路由传参:首页带参数跳转详情页
  • 渲染详情页
  • 优化性能:组件缓存
2.开始路由配置

views目录下创建:

  • 一级路由:首页Home.vue,详情页Detail.vue
  • 嵌套在首页中的二级路由:列表List.vue,收藏Collect.vue,喜欢Like.vue,我的User.vue
2.1.嵌套二级路由如何配置?
  • step1:在首页的配置项中添加children属性
javascript 复制代码
routes:[
	{
		path:"/home",
		component:Home,
		children:[
			{path:"/list",component:List},
			...
			{path:"/user",component:User}
		]
	}
]
  • step2:准备二级路由出口
    一级路由出口写在App.vue中,同理嵌套在首页的二级路由的出口可以写在首页Home.vue中
html 复制代码
//Home.vue
<router-view><router-view>
2.2.路由重定向,NotFound页面,去除"#"号
javascript 复制代码
routes:[
	...(接上面代码)...
	{path:"/",redirect:"/list"},//路由重定向
	{path:"*",component:NotFound};//再新建一个NotFound.vue
],
mode:"history",//去除井号
3.实现底部导航栏的高亮效果
  • step1:用router-link标签替换a标签(一开始直接不用a标签)
html 复制代码
  <div class="h5-wrapper">
    <!-- 内容区域 -->
    <div class="content">
          <router-view></router-view>
    </div>

    <!-- 底部导航栏 -->
    <nav class="tabbar">
      <router-link to="/list">列表</router-link>
      <router-link to="/collect">收藏</router-link>
      <router-link to="/like">喜欢</router-link>
      <router-link to="/user">我的</router-link>
    </nav>
  </div>
  • step2:利用高亮类名实现高亮效果
css 复制代码
/* 基础布局 */
.h5-wrapper {
  height: 100vh;
  display: flex;
  flex-direction: column;
}

.content {
  flex: 1;
  overflow-y: auto;
}

/* 底部导航栏样式 */
.tabbar {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  height: 50px;
  background: #fff;
  display: flex;
  border-top: 1px solid #eee;
  box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
}

.tabbar a {
  flex: 1;
  display: flex;
  justify-content: center;
  align-items: center;
  color: #666;
  text-decoration: none;
  font-size: 14px;
  transition: all 0.3s;
}

/* 激活状态样式 */
.tabbar a.router-link-active {
  color: orange;
  font-weight: bold;
}

/* 点击反馈效果 */
.tabbar a:active {
  background: rgba(0,0,0,0.05);
}

效果:

4.渲染首页:使用axios请求数据

*由于路由重定向,此处首页的渲染请求,实际上就是对嵌套二级路由List.vue的渲染

  • 安装axiosnpm install axios -g
  • 查看接口文档,确认请求方式、请求地址、请求参数
  • 在created钩子发送请求,获取数据后在data中存起来
javascript 复制代码
//List.vue
import axios from "axios"

export default from {
async created(){
    const res=await axios.get("https://mock.boxuegu.com/mock/3083/articles")
	//console.log(res);
	this.newsList=res.data.result.rows//根据res的结构逐渐获取想要的数据
}
data(){
    return{
        newsList:[]
   	  }
	}
}
  • 用获取到的数据渲染首页
html 复制代码
    <div 
    v-for="item in newsList" 
    :key="item.id"
    @click="$router.push(`/detail/${item.id}`)">//传参,后面讲
        <img :src="item.img" alt="">
        <h3 class="title">{{item.title}}</h3>
        <div class="body">{{item.source}}</div>
        <div class="meta-info">
          文章点击量:{{item.cmtcount}}&nbsp;&nbsp;|&nbsp;&nbsp;编辑于{{item.time}}
        </div>
    </div>
5.路由传参
5.1.回顾:查询参数传参或者动态路由传参
javascript 复制代码
    查询参数:
   		 父组件传参:@click="this.$router.push('/detail? 名1=值1&名2=值2')"
   		 子组件接收:this.$route.query.名1
    动态路由:
   		 需先改造路由:{path:"/detail/:参数名",...}
   		 父组件传参:@click='this.$router.push("/detail/参数值")'
    	 子组件接收::this.$route.params.参数名    
5.2.具体代码
javascript 复制代码
//父组件(首页):List.vue
<template>
  <div class="wrapper div-list-page">
    <div 
    v-for="item in newsList" 
    :key="item.id"
    @click="$router.push(`/detail/${item.id}`)">
        <img :src="item.img" alt="">
        <h3 class="title">{{item.title}}</h3>
        <div class="body">{{item.source}}</div>
        <div class="meta-info">
          文章点击量:{{item.cmtcount}}&nbsp;&nbsp;|&nbsp;&nbsp;编辑于{{item.time}}
        </div>
    </div>
  </div>
</template>

<script>
export default {
  data() {
    return {
      newLists:null
    };
  },
  async created() {
      const res = await this.$axios.get(`http://hmajax.itheima.net/api/news`)
      this.newsList = res.data.data
      //console.log("新闻列表:",this.newsList);
  },
};
</script>
<style scoped>
/* 外层容器 */
.wrapper {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

/* 单个新闻项 */
.list-page {
  background: #fff;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.1);
  margin-top: 24px;
  padding: 20px;
  transition: box-shadow 0.3s ease;
}

/* 悬停效果 */
.list-page:hover {
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}

/* 新闻图片 */
.list-page img {
  width: 100%;
  max-width: 600px;
  height: auto;
  border-radius: 4px;
  margin-bottom: 16px;
}

/* 标题样式 */
.title {
  font-size: 1.5rem;
  color: #333;
  margin-bottom: 12px;
  font-weight: 600;
  line-height: 1.3;
}

/* 正文内容 */
.body {
  color: #666;
  line-height: 1.6;
  margin-bottom: 16px;
}

/* 页脚元信息 */
.meta-info {
  color: #999;
  font-size: 0.9rem;
  border-top: 1px solid #eee;
  padding-top: 12px;
}
</style>

*********************************************************************
//子组件(详情页):Detail.vue
<nav class="nav" >
	 //点击返回首页,用到了实例对象$router的back方法
      <span class="back" @click="$router.back()">首页</span>
      <p>我是详情页</p>
      我获取到id了----{{$route.params.id}}
</nav>

效果:

6.渲染详情页

步骤和首页一样,略

7.解决请求过程中出现空白页的bug
出现原因

详情页组件在获取参数后需要异步加载数据,在数据返回前页面没有内容,除此之外,也受限于

  • 在哪个生命周期阶段发起数据请求
  • 网络延迟导致接口请求慢
  • 路由切换时的默认过渡效果可能导致视觉上的空白
解决方法
  • 方法一:越早发送请求越好(created)
  • 方法二:给详情页包裹一层v-if=list.id,如果没有数据则不渲染,没有数据时使用骨架屏内容
html 复制代码
<div>
	<div v-if="loading" class="skeloton">
	<!-- 骨架屏内容 -->
	</div>
	<div v-else>
		<! -- 获取数据,渲染内容 -->
	</div>
</div>
  • 其他:懒加载/预加载/keep-alive
7.组件缓存
问题描述

从首页进度条下拉的新闻列表中点击其中一条新闻,进入详情页然后返回首页,发现自动回到了首页的进度条顶部位置

原因分析

点进详情页又返回,首页数据重新加载了.即:路由跳转后,组件被销毁了,返回来后组件又重建了,导致数据被重新加载

解决方法

使用keep-alive将组件缓存下来

html 复制代码
//App.vue
<keep-alive :include="['Home.vue']">
 	 <router-view></router-view>
</keep-alive>
拓展:keep-alive介绍
1.keep-alive 是什么?

keep-alive是Vue的内置组件,用来包裹动态组件,使其缓存不活动的组件实例,而非销毁组件

keep-alive是一个抽象组件,它自身不会渲染成一个DOM元素,也不会出现在父组件中

2.keep-alive的优点是什么?

在组件切换的过程中,把切换出去的组件保留在内存中,避免重复渲染DOM,

减少加载时间和性能消耗,提高用户体验

3.keep-alive的三个属性

在上例中,首页被缓存时我们所需要的,详情页却不是,此时需要用到keep-alive的属性对这两个一级路由选择性缓存

  • include:组件名数组[*],匹配的组件会缓存
  • exclude:组件名数组,匹配的组件不会缓存
  • max:最多可以缓存多少个组件实例

*注意组件名数组和文件名(Detail.vue)的不同,有些组件文件会配置name属性:name:DetailPage

4.keep-alive的两个生命周期

首先来验证一下keep-alive的缓存效果:

javascript 复制代码
//Home.vue
created(){consol.log("组件被加载了...")},
mounted(){console.log("DOM渲染完了...")},
destroyed(){console.log("组件被销毁了...")}

结果:除第一次进入组件时触发前两个钩子外,第二次返回页面时不会再触发

接下来,写入actived和deactived看效果:

javascript 复制代码
activeed(){console.log("组件激活了...")},
deactived(){console.log("组件失活了...")}

结果,组件切换的过程中会触发

相关推荐
涵信2 小时前
第十八节:开放性问题-Vue生态未来趋势
前端·vue.js·devops
Monly213 小时前
Vue:el-table-tree懒加载数据
前端·javascript·vue.js
致微4 小时前
【Vue bug】:deep()失效
前端·vue.js·bug
Jiaberrr4 小时前
uniapp 实现低功耗蓝牙连接并读写数据实战指南
java·前端·javascript·vue.js·struts·uni-app
工业互联网专业5 小时前
基于web的可追溯果蔬生产过程的管理系统
java·vue.js·spring boot·毕业设计·源码·课程设计·可追溯果蔬生产过程的管理系统
HBR666_8 小时前
vue3定义全局防抖指令
前端·javascript·vue.js
前端老实人灬9 小时前
vue使用docx 生成 导出word文件。(包含:页眉、页脚、生成目录、页码、图片、表格、合并表格)
前端·vue.js·word
光影少年9 小时前
vue中$set原理
前端·javascript·vue.js
codecodegirl9 小时前
实现在h5中添加日历提醒:safari唤起系统日历,其它浏览器跳转google日历
前端·javascript·vue.js·html5
F2E_Zhangmo12 小时前
webpack5启动项目报错:process is not defined
前端·vue.js·webpack·webpack5