新闻客户端案例的实现,使用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("组件失活了...")}

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

相关推荐
_codeOH3 小时前
Vue 3 vs React 19:框架还在卷,核心原理就这些
前端·vue.js
英勇无比的消炎药4 小时前
新手必看玩转TinyRobot一定要避开这些坑
前端·vue.js
英勇无比的消炎药5 小时前
别再盲目混用AI组件库和传统组件库差距原来这么大
前端·vue.js
英勇无比的消炎药7 小时前
前端提效神器全新AI组件库TinyRobot改写日常开发模式
前端·vue.js
英勇无比的消炎药7 小时前
前端提效神器TinyRobot
前端·vue.js
CDwenhuohuo7 小时前
uni 背景色渐变 全屏
前端·javascript·vue.js
爱怪笑的小杰杰7 小时前
Vue 项目交付第三方开发,如何隐藏核心 JS 源码?
前端·javascript·vue.js
小二·7 小时前
Vue 3 组合式 API 进阶实战
前端·javascript·vue.js
rising start9 小时前
九、vue3 组件通信:全场景详解
前端·vue.js·typescript
编程技术手记9 小时前
Vue Scoped CSS 与动态创建 DOM 的兼容性问题
前端·css·vue.js