Vue中会使用官方提供的vue-router插件来使用单页面,原理就是通过检测地址栏变化后将对应的路由组件进行切换(卸载和安装),从而达到单页面运用的,使用户通过某些操作更改地址栏url之后,动态的进行不同模板内容的无刷新切换。
1.vue-router的安装
方法一:直接下载安装
下载地址:https://unpkg.com/vue-router/dist/vue-router.js
<script src="/path/to/vue.js"></script>
//在引入vue.js之后引入即可
<script src="/path/to/vue-router.js"></script>
方法二:NPM安装
在当前项目的根目录下,打开命令行,输入npm install vue-router
npm install vue-router
方法三:如果是在Vue-cli这个脚手架中使用,在配置项目文件的时候,选中router即可(空格键选中)
2.vue-router的使用
因为笔者用的是vue-cli脚手架创建的项目,所以就以此方式介绍router的使用
1、在根目录下创建一个文件夹(vue-cli默认提供了router文件夹,并默认创建好了一个index.js的文件),里面创建一个index.js的文件,用来管理所有的路由。
在此文件中引入vue以及vue-router环境
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
2、创建router路由器
new Router({
routes:[
{path:"/home",component:Home}
]
})
3、创建路由表并配置在路由器中
var routes = [
{path,component}//path为路径,component为路径对应的路由组件
]
var router = new Router({
routes
})
export default router
4、在根实例里注入router,目的是为了让所有的组件里都能通过this.
r
o
u
t
e
r
、
t
h
i
s
.
router、this.
router、this.route来使用路由的相关功能api
在项目的主入口js文件中,需要注入router
import Vue from 'vue'
import router from './router/index.js'
new Vue({
router, //注册router
render: h => h)
}).$mount('#app')
5、利用router-view来指定路由切换的位置
比如,当用户需要访问/films地址的时候,我们先准备一个films.vue的组件。
<template>
<div class="films">
<h2>这是轮播图</h2>
<h3>这是导航栏</h3>
<!--路由容器,接受二级路由传来的组件-->
<router-view></router-view>
</div>
</template>
<script>
export default {
}
</script>
<style lang="scss" scoped>
</style>
我们需要在主页面,也就是我们所说的单页面上,写一个router-view的标签,就是预留给组件插进来的位置。
<template>
<div id="app">
我是入口App
<!-- 3.使用组件one -->
<!-- 这个标签用来给插进来的组件占位置 -->
<router-view></router-view>
<tabbar></tabbar>
</div>
</template>
当然,在其他页面上使用组件,是需要注册才能使用的,所以在我们使用组件的页面,也要加上下面js的代码。
<script>
//1.引入组件filmsfilms
import films from "@/views/Films.vue"
//2.引入公共的组件Tabbar
import tabbar from "@/components/Tabbar.vue"
export default{
//2.注册组件films
components:{
films,
//注册公共组件tabbar
tabbar
}
}
</script>
3.多级路由
在创建路由表的时候,可以为每一个路由对象创建children属性,值为数组,在这个里面又可以配置一些路由对象来使用多级路由,注意:一级路由path前加’/’
例如,下面的代码是一个二级路由的配置代码。建议将二级路由的path值加上对应一级路由的path值。当然,component指的是对应的path值应该使用的组件,配置这些组件,都需先引进组件。
{
path:"/films",
component:films,
//2.2 配置一个二级路由,用children书写
children:[
{
path:"/films/nowplaying",
component:nowplaying
}
]
}
4.默认路由和重定向
当我们进入应用,默认像显示某一个路由组件,或者当我们进入某一级路由组件的时候想默认显示其某一个子路由组件,我们可以配置默认路由:
//4.路由的重定向问题
//当我们在地址栏不写路由的地址时候,默认去films路由接口
{ // / 代表项目的根目录
path:"/",
redirect:"/films"
// 当去访问"/"的时候,就会默认转到"/films"路径下
}
//当我们在地址栏输入的路由地址不存在时候,会进入我的error组件的页面
{
path:"*",
component:error
}
5.路由的跳转
声明式导航 router-link实现跳转。
vue还提供了一个跳转方式,即声明式的跳转,可以达到类似a标签的效果,但是只是改变了地址栏的地址,从而改变在页面中渲染的组件,达到刷新页面的效果,这个标签就是router-link标签,to表示要跳转的路径,tag是将这个标签渲染成我们想要的标签,router-link在页面默认渲染出来的是标签。active-class="active"这个样式会使我们点击的时候产生一个选中的效果,并且页面的渲染内容保持一致。
<router-link to="/films" tag="li" active-class="active">电影</router-link>
编程式导航实现跳转
有的时候需要在跳转前进行一些动作,需要在方法里使用$router的方法。因为router-link是直接跳转,达不到需求。下面是一个运用场景。
<template>
<div class="cinema">
<h2>我是cinema组件</h2>
<hr>
<ul>
<!--
1.涉及到了动态的操作,我们需要给每一个li加上key属性
2.给每一个li加上点击事件,并把他们的id属性带上
-->
<li
v-for="data in datalist"
:key="data.id"
@click="infor(data.id)"
>{{data.title}}</li>
</ul>
</div>
</template>
<!-- 当我们点击某一个电影的时候,会进入到电影的详情页面,我们用detial组件实现,但是我们怎么区分电影呢?我们可以用组件的$router上面提供了很多的跳转方法,把这些跳转的方式称之为 编程式导航 -->
<script>
export default {
//1.模拟一个电影的数据
data:function(){
return {
datalist:[
{id:1,title:"电影1"},
{id:2,title:"电影2"},
{id:3,title:"电影3"},
{id:4,title:"电影4"}
]
}
},
methods:{
infor:function(id){
this.$router.push(`/detail/${id}`)
}
}
}
</script>
<style>
</style>
当然,this.$router.push的写法还有很多种,
// 字符串
this.$router.push('index')
// 对象
this.$router.push({path: 'login-pw'})
// 带参数
this.$router.push({path: 'login-pw', query: {'account': this.account.account}})
// 跳转后的页面获取参数
this.account.account = this.$route.query.account
还有一个 this.$router.replace() 的方法,但是此方法与push()的不同之处在于,此方法实现的跳转不会被记录,即当我们在浏览器上返回上一级的时候,其实是返回到了上上一级。
6.路由模式
路由有两种模式:hash、history,默认会使用hash模式,但是如果url里不想出现丑陋hash值,在new VueRouter的时候配置mode值为history来改变路由模式,本质使用H5的histroy.pushState方法来更改url,不会引起刷新。
hash模式下的路由地址栏,中间会带有#标识符,比如下面的地址
https://m.maizuo.com/v5/?co=mzmovie#/city
history模式下的路由地址,没有#,比如百度的地址
https://www.baidu.com/?tn=62095104_19_oem_dg
二者的区别:
hash模式下的地址栏因为带有#看起来比较的丑陋,但是有一个优点,就是在我们输入的地址是不存在的时候,他是不会跳转到404页面的,除非是我们自定义了一个地址不存的提示页面。
history模式下,当我们输入一个错误的地址的时候,会直接跳转到404页面,所以,这个问题需要我们在服务端配置文件,如果 URL 匹配不到任何静态资源,则应该返回同一个 index.html 页面,这个页面就是你 app 依赖的页面。
7.路由守卫(路由钩子)
什么是路由守卫?可以想象这样有个场景:点击淘宝的购物车页面,如果你没有登录,会直接跳转到登录页面,只有当你登录了,才会让你进入到购物车页面。假设购物车的地址是“/shopcar”,当我们进入这个组件之前,需要先判断是否登录。这就是路由守卫的作用之一。
全局路由钩子:需要写在配置router的index.js里面
//进入到某个路由组件之前
router.beforeEach((to, from, next) => {
//会在任意路由跳转前执行,next一定要记着执行,不然路由不能跳转了
console.log('beforeEach')
console.log(to,from)
next()
})
//进入到某个路由组件之后
router.afterEach((to, from) => {
//会在任意路由跳转后执行
console.log('afterEach')
})
举个栗子:
//全局路由的前置守卫
//to:Route 即将要进入的目标
//from:Route 当前导航正要离开的路由
router.beforeEach((to,from,next)=>{
if(from.path==="/cinema"){
console.log("从影院这边过来的哦...")
}
next() //一定需要调用next
})
单个路由钩子:只有beforeEnter,在进入前执行,to参数就是当前路由。这个配置需要写在配置路由的相应位置上。
{
path: '/foo',
component: Foo,
//当进入到foo路由之前,就会触发
beforeEnter: (to, from, next) => {
//......
next() //必须要执行next之后,对应的Foo组件才可以正常显示出来
}
路由组件钩子:需要写在对应的组件里面
<template>
<div class="center">
center...
</div>
</template>
<script>
export default {
//进入Center之前
beforeRouteEnter(to,from,next){
console.log("Center---beforeRouteEnter...",this)
next()
},
//从Center离开的时候
beforeRouteLeave(to,from,next){
console.log("Center-beforeRouteLeave",this)
next()
},
beforeRouteEnter(to,from,next){
if(localStorage.getItem("token")){//说明用户已经登录了
next()
}else{
next("/login") //如果用户没有登录直接跳转到登录界面进行用户登录
}
}
}
</script>
<style>
</style>
附上几个完整的js文件,可供参考
主入口的js文件:
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
import store from './store'
Vue.config.productionTip = false
new Vue({
router, //为了让路由组件可以访问到this.$route 和 this.$router相关的路由api属性或方法
store,
render: h => h(App)
}).$mount('#app')
//App组件为单页面默认绑定的组件
主组件vue文件
(这个组件和我们的单页面绑定了,其他的组件都可以引入到这个组件里面然后就可以在单页面上显示出来)
<template>
<div id="app">
<!--路由容器 基于slot进行封装的 根据url路径显示不同的路由组件-->
<router-view></router-view>
<Tabbar></Tabbar>
</div>
</template>
<script>
//引入Tabbar组件
import Tabbar from "@/components/Tabbar"
export default {
components:{
Tabbar
}
}
</script>
<style lang="scss">
*{
padding: 0;
margin:0;
}
ul,li{
list-style: none;
}
</style>
配置router的index.js文件
(这个文件配置了所有路由)
import Vue from 'vue'
import VueRouter from 'vue-router'
//这行代码如果没有,this上面就没有route、router的相关方法了
Vue.use(VueRouter)
//1.使用路由第一步,引入我们的组件
// import center from "@/views/Center.vue" //可以改为懒加载的写法
import cinema from "@/views/Cinema.vue"
import films from "@/views/Films.vue"
import nowplaying from "@/views/Nowplaying.vue"
import error from "@/views/Error.vue"
import detail from "@/views/Detail"
const routes = [
//2.1 配置组件的使用路径,就是我们在地址栏输入对应的地址就会将对应的组件渲染上
{
path:"/films",
component:films,
//2.2 配置一个二级路由,用children书写
children:[
{
path:"/films/nowplaying",
component:nowplaying
}
]
},
{
path:"/cinema",
component:cinema,
},
//配置一个可以带参数的路由地址,由this.$router.push(`/detail/${id}`)可以设置,在详情页面的this.$route.params.id可以获取
{
path:"/detail/:id",
component:detail
},
{
path:"/center",
component:()=>import (/* webpackChunkName:'cinema' */"@/views/Center.vue") //懒加载的写法 /*webpackChunkName:'这里可以改懒加载在network里面请求的js文件名称'*/
},
//3.要在对应的页面上准备一个插槽<router-view></router-view>,我们在App.vue文件上准备一个插槽
//当我们在地址栏输入的路由地址不存在时候,会进入我的404页面
{
path:"*",
component:error
},
//4.路由的重定向问题
//需要设置,当我们在地址栏不写路由的地址时候,默认去films路由接口
{
path:"/",
redirect:"/films"
}
]
const router = new VueRouter({
routes
})
export default router