1.路由简介
Vue 作为一个渐进式框架,在使用 vue-router 之后可以是程序变为一个 SPA 的单页面应用,
它的本质是使用了 js 的 History API:
Vue Router 利用浏览器的 History API(如 pushState
和 replaceState
),实现了无刷新的页面切换效果,同时保持 URL 的正确性。
1.SPA
单页应用程序: SPA【Single Page Application】是指所有的功能都在一个html页面上实现
多页应用: 每个功能都生成一个html页面来实现
单页应用网站: 网易云音乐
智慧园区: fe-hmzs-tj.itheima.net/rod/warnLis…
Hmzs%001
多页应用网站: 京东 jd.com/
单页应用 VS 多页面应用
单页应用类网站:系统类网站 / 内部网站 / 文档类网站 / 移动端站点
多页应用类网站:公司官网 / 电商类网站
单页面应用程序,之所以开发效率高,性能好,用户体验好
最大的原因就是:页面按需更新
要按需更新,首先就需要明确:访问路径和 组件的对应关系!
访问路径 和 组件的对应关系如何确定呢? 路由
Vue中的路由:路径和组件的映射关系
2.VueRouter的使用
Vue中安装VueRouter的基本步骤:4步固定流程(无需记忆)
- 下载 VueRouter 模块到当前工程,版本3.6.5
yarn add vue-router@3.6.5 或 npm i vue-router@3.6.5
2. main.js中引入VueRouter 和 安装注册 路由
import VueRouter from 'vue-router'
Vue.use(VueRouter)
3. main.js中创建路由对象,配置路由规则,并且注入到Vue实例中
const router = new VueRouter({
routes:[
//{path:'/xxx',component:xxxx},
//......其他路由
]
})
4. main.js中将路由对象注入到new Vue实例中,与Vue建立关联
new Vue({
render: h => h(App),
router
}).$mount('#app')
组件存放目录约定:
-
src/views文件夹页面组件 - 页面展示 - 配合路由用
-
src/components文件夹复用组件 - 展示数据 - 常用于复用
2.路由的用法
1. 模块抽离
// 承担的职责:封装路由相关的代码
import Vue from 'vue'
// 1. 导入vuerouter
import VueRouter from 'vue-router'
// 2. Vue使用一下->注册组件 -> 能够去使用<router-view> <router-link>
Vue.use(VueRouter)
// 3. 实例化VueRouter
import Find from '@/views/Find.vue'
import Friend from '@/views/Friend.vue'
import My from '@/views/My.vue'
const router = new VueRouter({
routes: [
{ path: '/find', component: Find },
{ path: '/my', component: My },
{ path: '/part', component: Friend },
]
})
// 4. 将router对象导出(es6模块标准)
export default router
import router from './router'
new Vue({
// 5. 关联路由对象和vue实例 data methods
render: (h) => h(App),
router,
}).$mount('#app')
2.声明式导航
定义: 使用 router-link方式进行的导航,叫做声明式导航
作用: router-link最终会生成一个a标签,用户点击即可跳转。
1. 导航高亮
router-link会自动给当前导航添加两个类名
<template>
<div>
<!--
router-link会自动给当前的链接添加两个类名
router-link-active: 激活的导航链接 模糊匹配
router-link-exact-active: 激活的导航链接 精确匹配
-->
<router-link to="/find">发现音乐</router-link>
<router-link to="/my">我的音乐</router-link>
<router-link to="/part">朋友</router-link>
<div class="top">
<router-view></router-view>
</div>
</div>
</template>
<style scoped>
.router-link-exact-active {
color: red;
}
.router-link-active {
color: red;
}
</style>
可以修改默认高亮的类名
const router = new VueRouter({
linkActiveClass: 'act',
linkExactActiveClass: 'exact',
// route: 一条规则
}
2.路由传参
目标: 在跳转路由时, 可以给路由对应的组件内传值
在router-link上的to属性传值, 语法格式如下
- 查询参数传参(query): /path?参数名=值
- 动态路由传参(params): /path/参数名”
查询参数传参
① 导航链接中传入参数: to="/path?参数名=值"
② 对应页面组件接收传递过来的值: $route.query.参数名
注意:$route在template中无需加this,在script中需要加this
动态路由传参
① 配置动态路由 -> router/index.js
② 配置导航链接to="/path/参数值"
③ 对应页面组件接收传递过来的值$route.params.参数名
- 创建views/Search.vue - 准备接收路由上传递的参数和值
<template>
<div>
<p>关键字: {{ $route.params.words }}</p>
</div>
</template>
2. 路由定义
{
path: "/search/:keywords",
component: Search
}
3. App.vue中导航跳转, 传值给Seach.vue组件
<router-link to="/search/黑马程序员">黑马程序员</router-link>
两种传参方式的区别:
3.编程式导航
定义: 用$router.push()等 跳转的方式,我们叫做编程式导航
作用: 一般用在方法中通过 编写 $router.push()代码 来完成跳转
语法: query / params 任选 一个
query传参方式:
params传参方式:
this.$router.push({
name: "路由名",
// query传参 -> 对应路由接收 $route.query.参数名 取值
query: {
"参数名": 值
}
// params传参 ->对应路由接收 $route.params.参数名 取值
params: {
"参数名": 值
}
})
4.路由的配置
1.重定向
匹配path后, 强制切换到目标path上
作用: 一般用在匹配上/后,强制切换到 其他path上
例如: 网页默认打开, 匹配路由"/", 强制切换到"/find"上
const routes = [
{
path: "/", // 默认hash值路径
redirect: "/find" // 重定向到/find
// 浏览器url中#后的路径被改变成/find-重新匹配数组规则
}
]
2.404
作用:如果路由hash值, 没有和数组里规则匹配,则给一个默认的404页面
3.路由模式
目标: 修改路由在地址栏的模式
hash路由例如: http://localhost:8080/#/home
history路由例如: http://localhost:8080/home (以后上线需要服务器端支持)
const router = new VueRouter({
routes,
mode: "history" // 打包上线后需要后台支持, 模式是hash
})
4.组件缓存(keep-alive)
就是想不销毁原有组件,缓存组件原有的状态
基本语法
思考:从面经 点到 详情页,又点返回,希望回到回来的位置!但是数据重新加载了 => 因为组件被销毁重建了。
如果希望组件被缓存下来,可以在外面包一个 keep-alive 组件
<template>
<div class="h5-wrapper">
<keep-alive>
<router-view></router-view>
</keep-alive>
</div>
</template>
keep-alive对应的两个钩子
当组件被keep-alive管理时,会多出两个生命周期钩子,activated / deactivated
export default {
...
activated() {
console.log('缓存组件被激活')
},
deactivated() {
console.log('缓存组件被隐藏')
}
};
3. 案例
<!--
* @Date: 2025-01-13 16:24:37
* @LastEditors: zl 1077167261@qq.com
* @LastEditTime: 2025-01-13 18:12:10
* @FilePath: \app_demo\src\App.vue
-->
<template>
<div class="h5-wrapper">
<keep-alive include="LayoutPage">
<router-view></router-view>
</keep-alive>
</div>
</template>
<script>
export default {
name: "AppVue",
};
</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>
<template>
<div class="h5-wrapper">
<div class="content">
<!-- 二级路由出口, -->
<router-view></router-view>
</div>
<nav class="tabbar">
<!-- 导航高亮
1. 将a标签,替换成router-link (to)
2. 结合高亮类名实现高亮效果 (router-link-active 模糊匹配)
-->
<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>
<template>
<div class="article-detail-page" v-if="article.id">
<nav class="nav">
<span class="back" @click="$router.back()"><</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>
// 请求地址: https://mock.boxuegu.com/mock/3083/articles/:id
// 请求方式: get
import axios from "axios";
export default {
name: "article-detail-page",
data() {
return {
article: {},
};
},
async created() {
this.article = {};
const { data } = await axios.get(
`https://mock.boxuegu.com/mock/3083/articles/${this.$route.params.id}`
);
this.article = data.result;
},
};
</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>
/*
* @Date: 2025-01-13 16:21:45
* @LastEditors: zl 1077167261@qq.com
* @LastEditTime: 2025-01-13 17:12:04
* @FilePath: \app_demo\src\router\index.js
*/
/*
* @Date: 2025-01-13 16:21:45
* @LastEditors: zl 1077167261@qq.com
* @LastEditTime: 2025-01-13 16:54:00
* @FilePath: \app_demo\src\router\index.js
*/
import Vue from 'vue';
import VueRouter from 'vue-router';
import LayoutPage from '@/views/LayoutPage.vue';
import ArticleDetailPage from '@/views/ArticleDetailPage.vue';
// 二级路由
import ArticlePage from '@/views/ArticlePage.vue';
import CollectPage from '@/views/CollectPage.vue';
import LikePage from '@/views/LikePage.vue';
import UserPage from '@/views/UserPage.vue';
Vue.use(VueRouter);
const routes = [
{
path: '/',
component: LayoutPage,
redirect: '/article',
children: [
{
path: '/article',
component: ArticlePage,
},
{
path: '/collect',
component: CollectPage,
},
{
path: '/like',
component: LikePage,
},
{
path: '/user',
component: UserPage,
},
],
},
{
path: '/detail/:id',
name: 'detail',
component: ArticleDetailPage,
},
];
const router = new VueRouter({
routes,
});
export default router;