Vue学习笔记 (2023黑马版 第六天)

第六天

1.路由进阶

路由的封装分离

好处:拆分模块,利于维护
步骤:

  1. 新建router文件夹,并在里面新建文件index.js
  2. 再main.js中将跟路由有关的代码剪切到index.js中
  3. 再在main.js中引入router
    在这里插入图片描述
    注意事项:
    快速引入组件(从根路径开始)
    在这里插入图片描述

声明式导航 - 导航链接

需求:实现导航高亮

vue-router提供了一个全局组件 router-link(取代a标签)

好处:

  1. 能跳转,配置to属性指定路径(必须)。本质还是a标签,to前缀无需"#"
  2. 能高亮,默认就会提供高亮类名,可以直接设置高亮样式

声明式导航 - 两个类名

router-link自动给当前导航栏添加了两个高亮类名

  1. router-link-active 模糊匹配(用的多)
to = "/my" 可以匹配	"/my"	"/my/a"		"/my/b"		...
  1. router-link-exact-active 精确匹配
to = "\my" 仅可以匹配 "/my" 

自定义router-link的两个高亮类名:

const router = new VueRouter({
  routes: [
    { path: '/find', component: Find },
    { path: '/my', component: My },
    { path: '/friend', component: Friend },
  ],
  linkActiveClass:'active',
  linkExactActiveClass:'exact-active'
})

声明式导航 - 跳转传值

  1. 查询参数传参(比较适合传多个参数)
    ①语法:
<router-link to="/path?参数名=值">我是router-link</router-link>

②对应页面组件接收传递过来的值

$route.query.参数名
  1. 动态路由传参(优雅简洁,传单个参数比较方便)
    ①配置动态路由
const router = new VueRouter({
  routes: [
    { path: '/search/:words', component: Search }
  ]
})

注意事项:/search/:words 表示,必须要传参数。如果不传参数,也希望匹配,可以加个可选符"?",例如以下

{ path: '/search/:words?', component: Search }

②配置导航链接

<router-link to="/path/参数值">我是router-link</router-link>

③对应页面组件接收传递过来的值

$route.params.参数名

2.Vue路由

Vue路由 - 重定向

语法:

const router = new VueRouter({
  routes: [
    { path: '/A', redirect: '/B'},//匹配到A路径时,自动跳转到B路径
  ]
})

Vue路由 - 404

作用:当路径找不到匹配时,给个提示页面
位置:配在路由最后
语法:path:“*”(任意路径)

const router = new VueRouter({
  routes: [
    { path: '/', redirect: '/home'},
    { path: '/home', component: Home },
    { path: '/search/:words?', component: Search },
    { path: '*', component: NotFound }//配置在最后一条
  ]
})

Vue路由 - 模式设置

  • hash路由(默认)
    例如:http://localhost/8080/#/home
  • history路由(常用)
    例如 http://localhost/8080/home(以后上线需要服务器端支持)
const router = new VueRouter({
  mode:'history',
})

3.编程式导航

编程式导航 - 基本跳转

编程式导航:用js代码来进行跳转

  1. path路径跳转(简易方便)
//简易写法
this.$router.push('路由路径')

//完整写法
this.$router.push({
	path:'路由路径'
})
  1. name命名路由跳转(适合path路径长的场景)
this.$router.push({
	name: '路由名'
})
{ name: '路由名', path: '/path/xxx', component: xxx },

编程式导航 - 跳转传参

  1. path路径跳转
    ① query传参
this.$router.push('/路径?参数名1=参数值1&参数名2=参数值2')
//或者
this.$router.push({
	path:'/路径',
	query: {
		参数名1: '参数值1',
		参数名2: '参数值2'
	}
})

②动态路由传参(需要配动态路由)

this.$router.push('/路径/参数值1')
//或者
this.$router.push({
	path: '/路径/参数值'
})
  1. name命名路由跳转
    ①query传参
this.$router.push({
	name: '路由名字',
	query: {
		参数名1: '参数值1',
		参数名2: '参数值2'
	}
})

②动态路由传参(需要配动态路由)

this.$router.push({
	name: '路由名字',
	params: {
		参数名: '参数值',
	}
})

4.keep-alive

keep-alive:Vue的内置组件,包裹动态时,可以缓存
优点:组件切换时,把切换出去的组件保留在内存中(提升性能)
keep-alive的三个属性(了解)

  1. include:组件名数组,只有匹配的组件会被缓存
  2. exclude:组件名数组,任何匹配的组件都不会背缓存
  3. max:最多可以缓存多少组件实例
<template>
  <div class="h5-wrapper">
    <keep-alive :include="['组件名']">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

keep-alive的使用触发两个生命周期函数(了解)
activated 当组件被激活(使用)的时候触发 → 进入页面触发
deactivated 当组件不被使用的时候触发 → 离开页面触发

5.案例实践:面经基础版

分析:配路由 + 实现功能

  1. 配路由
  • ①首页和面经详情,两个一级路由
  • ②首页内嵌四个可切换页面(嵌套二级路由)
    嵌套路由的关键配置项:children
  1. 实现功能
  • ①首页请求渲染
  • ②跳转传参到详情页,详情页渲染
    查询参数传参, $ route.query.参数名(适合多个参数)
    动态路由传参,$route.params.参数名(简洁直观)
  • ③组件缓存,优化性能
    内置组件 keep-alive

效果示意图:
在这里插入图片描述
核心代码:

<!--Layout.vue-->
<template>
  <div class="h5-wrapper">
    <div class="content">
      <router-view></router-view>
    </div>
    <nav class="tabbar">
      <router-link to="/article">面经</router-link>
      <router-link to="/collect">收藏</router-link>
      <router-link to="/like">喜欢</router-link>
      <router-link to="/user">我的</router-link>
    </nav>
  </div>
</template>

<script>
export default {
  name: "LayoutPage",
}
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
    }
    a.router-link-active {
      color: orange;
    }
  }
}
</style>
<!--Article.vue-->
<template>
  <div class="article-page">
    <div
      v-for="item in articles" 
      :key="item.id"
      @click="$router.push(`/detail/${item.id}`)"
      class="article-item"
      >
      <div class="head">
        <img :src="item.creatorAvatar" alt="" />
        <div class="con">
          <p class="title">{{ item.stem }}</p>
          <p class="other">{{ item.creatorName }} | {{ item.createdAt }}</p>
        </div>
      </div>
      <div class="body">
         {{ item.content }}
      </div>
      <div class="foot">点赞 {{ item.likeCount }} | 浏览 {{ item.views }}</div>
    </div>
  </div>
</template>

<script>
// 请求地址: https://mock.boxuegu.com/mock/3083/articles
// 请求方式: get
import axios from 'axios'

export default {
  name: 'ArticlePage',
  data () {
    return {
      articles: []
    }
  },
  async created () {
    const res = await axios.get('https://mock.boxuegu.com/mock/3083/articles')
    this.articles = res.data.result.rows
    console.log(this.articles)
  }
}
</script>

<style lang="less" scoped>
.article-page {
  background: #f5f5f5;
}
.article-item {
  margin-bottom: 10px;
  background: #fff;
  padding: 10px 15px;
  .head {
    display: flex;
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
    .con {
      flex: 1;
      overflow: hidden;
      padding-left: 15px;
      p {
        margin: 0;
        line-height: 1.5;
        &.title {
          text-overflow: ellipsis;
          overflow: hidden;
          width: 100%;
          white-space: nowrap;
        }
        &.other {
          font-size: 10px;
          color: #999;
        }
      }
    }
  }
  .body {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-top: 10px;
    overflow: hidden;
    text-overflow: ellipsis;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
  }
  .foot {
    font-size: 12px;
    color: #999;
    margin-top: 10px;
  }
}
</style>
<!--ArticleDetail.vue-->
<template>
  <div class="article-detail-page" v-if="article.id">
    <nav class="nav"><span @click="$router.back()" class="back">&lt;</span> 面经详情</nav>
    <header class="header">
      <h1>{{ article.stem }}</h1>
      <p>{{ article.createdAt }} | {{ article.views }} 浏览量 | {{ article.likeCount }} 点赞数</p>
      <p>
        <img
          :src="article.creatorAvatar"
          alt=""
        />
        <span>{{ article.creatorName }}</span>
      </p>
    </header>
    <main class="body">
      {{ article.content }}
    </main>
  </div>
</template>

<script>
import axios from 'axios'
// 请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
// 请求方式: get
export default {
  name: "ArticleDetailPage",
  data() {
    return {
      article: {}
    }
  },
  async created () {
    const id = this.$route.params.id
    const { data } = await axios.get(`https://mock.boxuegu.com/mock/3083/articles/${id}`)
    this.article = data.result
    console.log(this.article)
  }
}
</script>

<style lang="less" scoped>
.article-detail-page {
  .nav {
    height: 44px;
    border-bottom: 1px solid #e4e4e4;
    line-height: 44px;
    text-align: center;
    .back {
      font-size: 18px;
      color: #666;
      position: absolute;
      left: 10px;
      top: 0;
      transform: scale(1, 1.5);
    }
  }
  .header {
    padding: 0 15px;
    p {
      color: #999;
      font-size: 12px;
      display: flex;
      align-items: center;
    }
    img {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      overflow: hidden;
    }
  }
  .body {
    padding: 0 15px;
  }
}
</style>
<!--App.vue-->
<template>
  <div class="h5-wrapper">
    <keep-alive :include="['LayoutPage']">
      <router-view></router-view>
    </keep-alive>
  </div>
</template>

<script>
export default {
  name: "h5-wrapper",
}
</script>

<style>
body {
  margin: 0;
  padding: 0;
}
</style>
<style lang="less" scoped>
.h5-wrapper {
  .content {
    margin-bottom: 51px;
  }
  .tabbar {
    position: fixed;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: flex;
    background: #fff;
    border-top: 1px solid #e4e4e4;
    a {
      flex: 1;
      text-decoration: none;
      font-size: 14px;
      color: #333;
      -webkit-tap-highlight-color: transparent;
      &.router-link-active {
        color: #fa0;
      }
    }
  }
}
</style>
//index.js
import Vue from 'vue'
import VueRouter from "vue-router";
import Layout from '@/views/Layout'
import Article from '@/views/Article'
import Collect from '@/views/Collect'
import Like from '@/views/Like'
import User from '@/views/User'
import ArticleDetail from '@/views/ArticleDetail'
Vue.use(VueRouter)

const router = new VueRouter({
  routes: [
    { path: '/', component: Layout, redirect: '/article',
      children: [
        {
          path: '/article', component:Article
        },
        {
          path: '/collect', component: Collect
        },
        {
          path: '/like', component: Like
        },
        {
          path: '/user', component: User 
        }
      ]
    },
    { path: '/detail/:id', component: ArticleDetail}
  ]
})

export default router
  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值