一.什么是单页面应用?
- 单页只有一个HTML页面,按需更新性能高,开发效率快,用户体验好,但学习成本高,首屏加载慢,SEO较差
- 多页即多个HTML页面,整页更新的特点让其性能较低,开发效率一般,用户体验一般,学习成本中等,但首屏加载快,SEO较好
*SEO:搜索引擎优化,可以理解为搜索结果的友好度
使用场景:
- 单页:系统类网站,内部网站,文档类网站,移动端网站,比如:网易云音乐
- 多页:公司官网,电商类网站,比如;:淘宝,京东
二.什么是路由?
由于单页面应用的按需更新的特点,我们必须明确访问路径和组件的对应关系
这就需要用到路由,它实际上是一种映射关系
生活中的路由和Vue中的路由
路由器:IP和设备之间的映射关系
Vue中的路由:路径和组件之间的映射关系
示例:
http://localhost:8080#home路径 映射到 Home.vue组件
http://localhost:8080#comment路径 映射到 Comment.vue组件
http://localhost:8080#search路径 映射到 Search.vue组件
三.VueRouter(重点)
0.引出
在单页面应用中,点击导航栏的不同标签,需要实现:
1,地址栏中的地址要发生变化.2.导航栏下方的组件也要对应变化
----Vue官方提供了一个插件可实现以上功能,即VueRouter
1.介绍
- 作用:修改地址栏地址时,切换显示匹配的组件
- 本质:Vue官方的一个路由插件,是一个第三方包
2.下载与使用(5+2)
2.1 五个基本步骤
- 下载:下载VueRouter模块到当前工程项目中(Vue.2对应的版本是3.6.5)*
安装命令:yarn add vue-router@3.6.5
注:此命令安装报错,最后用了这个命令:
npm install vue-router@3.6.5 -g
*对于安装版本的匹配,有个口诀叫"233 344",即:
Vue2+VueRouter3.x+Vuex3.x
"344"同理
- 引入
//main.js==>若不考虑路由的封装和抽离,所有路由相关代码可先堆在main.js中
import VueRouter from 'vue-router'
- 安装注册
//由于VueRouter是Vue的插件,所以必须先安装注册后才能使用
Vue.use(VueRouter)//内部会对组件进行全局的注册,即插件的初始化
- 创建路由对象
const router=new VueRouter();//此时的路由对象和Vue实例还没有建立关联
- 注入:将路由对象注入到new Vue实例中,建立关联
new Vue({
render:h=>h(App),
//router:router可简写如下:
router
}).$mounted("#app");
//注入完成,代表着这个Vue实例已经被路由对象接管了
//效果:地址栏最后会多出一个"#"
完整代码:main.js
//1.下载:在根目录文件下下载:npm install vue-router@3.6.5 -g
//main.js
//2.引入
import VueRouter from "vue-router"
//3.安装注册
Vue.use(VueRouter);//VueRouter插件初始化
//4.创建路由对象
const router=new VueRouter();//后续可以在里面添加路由规则
//5.注入
new Vue({
render:h=>h(app),
router,//router:router,简写成router,(router:rOther)
}).$mount("#app")//当前vue实例已被路由对象所管理
//成功标志:地址栏多了"#"
2.2 两个核心步骤
- 项目代码
//App.vue
<div class="footer_warp">
<a>发现音乐</a>
<a>我的音乐</a>
<a>好友</a>
</div>
//新建Views文件夹下的FindMusic.vue,MyMusic.vue和MyFriends.vue三个组件,
//内容随意,引入过程略
- step1:配置规则
//main.js或router/index.js
import FindMusic from "..."
...
const router =new VueRouter({
routes:[//配置规则
{path:'/FindMusic',component:FindMusic},
{path:'/MyMusic',component:MyMusic},
{path:'/MyFriends',component:MyFriends},
//此处可直观看到路由的定义:path和component之间的映射
});
*注意:path中的路径没有"."在前面,不要写成相对路径
- step2:配置导航
<a href="#/FindMusic"">发现音乐</a>
<a href="#/MyMusic">我的音乐</a>
<a href="#/MyFriends">好友</a>
<div>
<!-- 这个标签的位置决定了组件内容展示的位置-->
<router-view></router-view>
</div>
最终效果:点击不同的a标签,地址栏显示不同的"#"(路径)
四.路由的封装抽离
1.把路由配置从main.js中抽离出来,放在router/index.js中
把所有的路由配置都放在main.js显然是不合适的,因此要将路由模块抽离出来,更利于维护
- 步骤
把刚刚在main.js 中写的路由相关的代码挪到新建的router/index.js中然后在main,js中导入
import router from "./router/index.js"
- 注意事项
- 把代码放入index.js 后,相对路径会发生变化,此时要么调整成正确的相对路径,要么使用"@"(代表src文件夹),写成绝对路径
- 需要在index.js中重新引入Vue,因为使用到了
Vue.use(VueRouter)
,其实main.js也有,只是引用语句import Vue from "vue"
在main,js中是自动生成的 - 最后需将路由对象导出:
export default router;
2.同是存在组件的文件夹,views和components有何区别?
views目录
此文件夹中存放的是路由级的页面组件,对应的是路由配置的入口组件,包含完整的页面结构和业务逻辑,
比如Home.vue,Search.vue
等
components目录
此文件夹中存放的是可复用的UI组件,这些组件通常是被多个视图或组件调用的复用组件,一般来说只有单一的功能,
比如:Card.vue,Banner.vue
等
五.声明式导航
在Vue2中:声明式导航就是<router-link to=""></router-link>
,
<router-link to=""></router-link>
就是声明式导航-----与声明式导航相对的是编程式导航
1.导航链接
使用vue-router的全局组件<router-link to=""></router-link>
代替a标签,可以实现导航高亮效果
特点一:能跳转
<router-link to="/FindMusic">发现音乐</router-link>
<!-- to不可省略且不用在路径中加"#", 对比a标签:-->
<a href="#/FindMusic"">发现音乐</a>
特点二:能高亮
声明式导航能实现自动高亮,默认提供高亮类名,可以通过高亮类名设置高亮样式
(在控制台能看到选中的a标签自动获得两个类名)
route-link本质上还是a标签,在控制台查看元素,发现渲染出来的还是<a></a>
2.两个高亮类名
<router-link to=""></router-link>
会自动给当前导航添加两个高亮类名,
分别叫:router-link-exact-active
和router-link-active
2.1.区别
- router -link-active
这个用得多,它是模糊匹配的
如:a href="#/my"
标签中有这个类,那么它将匹配所有以my开头的路径:/my /my/a /my/b
- router-link-exact-active
这个类是精确匹配的,写的什么就匹配什么
2.2.为什么支持模糊匹配的类用得更多?
场景:网易云音乐在"发现音乐"的一级导航栏下面还有二级导航栏:推荐,排行榜,歌单等等
此时我们对页面有一个需求,
即用户在二级菜单下来回点击的时候,一级导航栏"发现音乐"要保持高亮状态
这时候就是模糊匹配中的/my/a和/my/b情况(/FindMusic/、 /FindMusic/TopList 和/FindMusic/PlayList
)
2.3.自定义高亮类名
这两个类名太长了,可以在router路由对象中进行定制:
//main.js或index.js
const router=new VueRouter({
routes:[...],
linkActiveClass:"新类名1",//比如"active"
linkExactActiveClass:"新类名2",//比如:"exact-active"
});
效果:
2.4.自定义高亮类名设置样式(全局样式)
创建assets/css/global.css
/* 通过deepseek配置全局样式文件示例 */
.active { /* 模糊匹配 */
color: #42b983;
position: relative;
}
.active::after { /* 下划线动画效果 */
content: '';
position: absolute;
bottom: -5px;
left: 0;
width: 0;
height: 2px;
background: #42b983;
transition: width 0.3s;
}
.exact-active { /* 精确匹配 */
font-weight: 700;
background: linear-gradient(90deg, #42b98333 0%, #ffffff 100%);
}
/* 激活状态下的动画 */
.router-link-active.active::after {
width: 100%;
}
/*把此css文件在入口文件main.js中导入:import "@/assets/css/global.css"*/
3.跳转传参
- 目标:在跳转路由的时候传值
- 场景:
在搜索栏下方的热门搜索词中,点击其中一个,会跳转到搜索该词条的结果页,即:
这个词条就是跳转过程中所传的值 - 方法:查询参数传参和动态路由传参
3.1.查询参数传参:在配置路径的同时进行传参
- 语法:和地址栏传参的格式一致,故被称为查询参数传参
to="path?参书名1=参数值1&参数名2=参数值2"
- 获取参数:对应页面的组件接受传过来的值
$route.query.参数名
- 示例:在首页点击不同文字后跳转到搜索页,并传参
//首页:Home.vue
<!-- 第一步:路径后加?属性名和属性值键值对 -->
<router-link to="/search?key=特朗普">特朗普</router-link>
<router-link to="/search?key=中美关税">中美关税</router-link>
<router-link to="/search?key=贸易战">贸易战</router-link>
//搜索页:Search.vue
<div class="search">
<!-- 第二步:对应页面组件接受传值 -->
<p>搜索关键字{{$route.query.key}}</p>
<p>搜索结果
<ul>
<li>...</li>
</ul>
</p>
</div>
/*
思考:在搜索页中什么时候和如何拿到参数?
答:在created中获取,此时要加this
created(){
console.log(this.$route.query.key);
}
*/
- 踩坑
验证此例的时候出现了路由无法跳转的问题,Search页地址栏或组件显示都无效,
原因: 错误地在App.vue注册和引入了Home页
错误写法:<Home><Home>
,正确写法:<router-view></router-view>
3.2.动态路由传参:路由不再是写死的,而是动态的
- step1:配置动态路由
routes:[
{
path:"/search/:word", /*word是参数名,冒号不能省略,此时能匹配多个路径*/
components:Search
}
]
- step2:配置导航链接
to="/path/参数值"
- step3:对应页面的组件接受传过来的值
$route.params.参数名,即:
$route.params.word
- 示例
//router/index.js
routes:[
{path:"/home",component:Home},
{path:"/search/:word",component:Search};//step1:配置动态路由
]
//首页:Home.vue
//step2:配置导航链接
<router-link to="/search/特朗普">特朗普</router-link>
<router-link to="/search.中美关税">中美关税</router-link>
<router-link to="/search/贸易战">贸易战</router-link>
//搜索页:Search.vue
//step3:接收传值
<p>搜索关键字:{{$route.params.word}}
//同理在created中:
created(){
console.log(this.$route.params.word);//可以从获取到的值来对搜索结果进行渲染
}
/*
注意区别:
获取查询参数:this.$route.query.参数名
获取动态路由参数:this.$route.query.参数名
*/
3.3.动态路由传参的可选符(?)
如果没有可选符可能会遇到的问题–
当配置路由的写法是这样的时候:
{path:"/search/:word",component:Search}
意味着必须携带参数,否则不通过传参直接进入搜索页时显示是空白的
解决方法:使用可选符(?)
{path:"/search/:word?",component:Search}
直接进入搜索页可以正常渲染,不会徐成空白
3.4.路由重定向
就是给路径A重定向为路径B,最常见的应用场景是让根路径http://localhost:8080/
重定向为首页http://localhost:8080/Home
写法:
//index.js
routes:[
//{path:"路径A",redirect:"/路径B"},
{path:"/",redirect:"/home"},
{path:"/home",component:Home}
]
3.5.找不到页面:404 not found Page
若用户输入的路径不在routes数组内,则会报错,这时候一般设置一个404页面,
设置的逻辑是在routes数组的最后匹配"*"路径,将除了前面的路径以外,都跳转到404页面中去
//新建文件views/NotFound.vue并在index.js中引入
//index.js
routes:[
//{path:"路径A",redirect:"/路径B"},
{path:"/",redirect:"/home"},
{path:"/home",component:Home},
{path:"/search",component:Search},
{path:"*",component:NotFound}
]
六.编程式导航
1.什么是编程式导航
编程式导航通过JS控制页面跳转,和声明式导航不同,它不依赖模板语法,而是通过调用路由实例的API实现动态导航,
本质是运行时逻辑控制的路由切换
2.路由跳转
2.1.path路径跳转
//Home.vue
//html
<input type="text"><button @click="handleSearch">搜索一下</button>
//js
methods:{
handleSearch(){
//简写:
//this.$router.push("/search");
//详写:
this.$router.push({
path:"/search"
});
}
}
2.2.命名路由跳转
//router/index.js
routes:[
{path:"/home",component:Home},
{name:"searchPage",path:"/search",component:Search},//给路由命名
]
//Home.vue
handleClick(){
this.$router.push({
name:"searchPage"
})
}
3.路由传参
声明式导航有两种传参方式:
- 查询参数传参
- 动态路由传参
这两种方式在编程式路由中也可使用
3.1.path路径跳转+查询参数传参
//Home.vue
//html
<input type="text" v-model="inpValue"><button @click="handleSearch"></button>
//js
data{}{return{ inpValue:""}},
methods:{
handleSearch(){
this.$router.push({//注意:父组件是通过$router传参,...
path:"/search",
query:{
//参数名1=参数值1
key:`${this.inpValue}`
}
});
}
}
//Search.vue
<p>搜索词:{{$route.query.key}}</p>//...而子组件是通过$route来接收的
3.2.path路径跳转+动态路由传参
//router/index.js
routes:[
{path:"/search/:word",component:"Search"}//参数名--设置动态路由
]
//Home.vue
handleSearch(){
this.$router.push({
path:`/search/${this.inpValue}`,//参数值
});
}
//Search.vue
<p>搜索词:{{$route.params.word}}</p>
3.3.命名路由跳转+查询参数传参
//router/index.js
routes:[
{name:"searchPage",path:"/search",component:Search},//命名路由
]
//Home.vue
handleClick(){
this.$router.push({
name:"searchPage",
query:{
//参数名1:参数值1
key:`${this.inpValue}`
}
});
}
//Search.vue
<p>搜索词:{{$route.query.key}}</p>
3.4.命名路由跳转+动态路由传参
//router/index.js
routes:[
{name:"searchPage",path:"/search/:word",component:Search},//参数名---命名路由+动态路由
]
//Home.vue
handleClick(){
this.$router.push({
name:"searchPage",
params:{
//参数名1:参数值1
key:`${this.inpValue}`
}
});
}
//Search.vue
<p>搜索词:{{$route.params.word}}</p>
七.其他
1.$router实例
和$route对象
的区别
特性 | $router (路由实例) | $route (当前路由对象) |
---|---|---|
本质 | VueRouter的全局实例对象,整个应用仅有唯一一个 | 当前激活路由的状态信息(每个路由独享) |
主要作用 | 路由操作(跳转,前进,后退) | 路由信息获取(路径,参数,查询字符串等) |
访问方式 | this.$router | this.$route |
数据特性 | 包含操作方法,如:push,go 等 | 包含响应式属性,如:path,query 等 |
类比关系 | 类似导航系统(控制方向) | 类似GPS定位(显示当前位置信息) |
2.查询参数传参
和动态路由传参
的优劣
特性 | 查询参数传参(query) | 动态路由传参(params) |
---|---|---|
URL表现形式 | /user/123 路径参数 | /search?keyword=vue&page=1 (URL查询字符串) |
路由配置要求 | 必须预先定义动态路由,如:path:'/user/:id' | 无需特殊路由配置 |
参数可见性 | 参数直接暴露在路径中 | 参数显示在URL的? 之后 |
参数类型 | 始终为字符串(需要手动类型转换) | 始终为字符串(需要手动类型转换) |
参数必要性 | 通常是必填参数(如资源ID) | 常用于可选参数(如过滤条件,分页等) |
浏览器历史记录 | 完全不同的URL会被视为新纪录 | 同一路径不同查询参数会被视为同一个路由 |