用 Vue.js + Vue Router 创建单页应用,是非常简单的。
-
安装 :npm install --save vue-router
-
引入并使用:main.js文件中引入全局使用
import VueRouter from ‘vue-router’
Vue.use(VueRouter) -
创建路由实例
const router = new VueRouter({
routes //es6(缩写) 相当于 routes: routes
})
- 挂载到Vue根实例上
new Vue({
render: h => h(App),
router
}).$mount('#app')
-
创建路由组件
定义两个路由组件Home.vue | About.vue -
定义路由
引入router.js中
import Home from './page/Home'
import About from './page/About'
const routes = [
{
path:"/",
component:Home
},
{
path:"/about",
component:About
}
]
- 渲染-----HTML
使用<router-link>
组件来导航,通过传入to
属性指定链接,<router-link>
默认会被渲染成一个<a>
标签,<router-view>
是路由出口,路由匹配到的组件将渲染在这里
<template>
<div>
<router-link to="/"> 首页 </router-link>
<router-link to="/about"> 关于 </router-link>
<router-view></router-view>
</div>
</template>
抽离路由
将路由文件放在主入口main.js里会使得代码杂乱不宜与维护。所以最好将路由作为一个单模块抽离出去。
- 在src下定义router文件夹,里边创建index.js文件
- 将代码提取到 index.js 文件中,并且加以优化,如下
import Vue from 'vue'
import Home from '../page/Home'
import About from '../page/About'
// 1.引入路由并使用
import VueRouter from 'vue-router'
Vue.use(VueRouter)
export default new VueRouter({
routes: [{
path: "/",
component: Home
},
{
path: "/about",
component: About
}
]
})
- 在主入口文件 main.js中引入router,并且挂载到 Vue 实例上
import router from './router/index'
new Vue({
render: h => h(App),
router
}).$mount('#app')
动态路由匹配(路由传递参数)
- 定义传递的key值
在路由文件 index.js 中的路由实例path属性上用 :标记动态参数
{
path:"/user/:username",
path:"/user/username/:username/age/:age", //多参
component:User
}
- 传递参数值
在HTML中对应路由组件配置
<router-link to="/user/iwen">用户</router-link>
<router-link :to="'/user/username/'+username+'/age/'+age">用户</router-link> //多参 在data中定义username 和 age
<input type="text" v-model="username">
//在input标签中双向数据绑定
<input type="text" v-model="age">
- 读取 :在User组件中使用 this.$route.params.属性名 来获取
用户 : {{this.$route.params.username}}
年龄 : {{this.$route.params.age}}
- 第二种读取方法 :
第一步 :
<router-link :to="{name:'Player',params:{songid:item.song_id}}">
</router-link>
第二步 :在路由中配置 props
{
path:"/player/:id",
name:"Player",
component:Player,
props:true
}
第三步 :然后在 Player.vue 组件中用props获取传递的参数
props:{
songid:{
type:[String,Number],
required:true //必选项
}
}
404 Not found 路由
捕获所有路由或 404 Not found 路由,当路径错误时匹配404页面
{
// 会匹配所有路径
path: '*',
component:NotFound(创建NotFound组件)
}
路由嵌套
- 创建两个子组件
- 引入路由中,配置到路由实例上
import Tiyu from '../childPage/Tiyu'
import Yule from '../childPage/Yule'
{
path:"/news",
component:News, //父组件
children:[ //子组件
{
path:"tiyu", //子路由无需添加路径
component:Tiyu
},
{
path:"yule",
component:Yule
}
]
}
- 在父组件中配置路由出口 to="/一级和/二级都得写"
<router-link to="/news/tiyu">体育</router-link> |
<router-link to="/news/yule">娱乐</router-link> |
<router-view></router-view>
编程式导航
- 除了使用
<router-link>
创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法来导航到不同的URL,使用 router.push 方法,这个方法会向 history 栈添加一个新的记录,当用户点击浏览器后退按钮时,则回到之前的 URL。
例:
第一步 :
在非首页组件定义一个按钮 <button @click="myclick">回到首页</button>
第二步 : this.$router.push('对应组件的路由的path属性的值')
myclick(){
this.$router.push('/') //写法有好几种
//this.$router.push({name:"shouye"})
}
-
另一个跟 router.push 很像的方法是 router.replace() ;二者不同之处就是 push 会向 history 添加纪录,而 replace 不会添加新纪录,只是替换掉当前的history记录。语法同上
-
router.go( n ) 意思是在 history 记录中向前或者后退多少步,类似 window.history.go(n),这个方法的参数是一个整数。
命名路由
通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。
第一步 :const router = new VueRouter({
routes: [
{
path: '/user/:userId',
name: 'user',
component: User
}
]
})
第二步 :给 router-link 的 to 属性传一个对象
<router-link :to="{name:'user'}">用户</router-link> //无参
<router-link
:to="{ name: 'user', params: { userId: 123,......}}">用户
</router-link> //有参
命名视图
同级展示多个视图,而不是嵌套展示,例如创建一个布局,有侧导航和主内容两个视图,这个时候命名视图就派上用场了。
第一步: {
path:"nav1",
name:"nav1",
components:{
default:Nav1,
Nav2:Nav2
}
}
第二步: <router-link :to="{name:'nav1'}">导航</router-link>
<router-view name="Nav2"></router-view>
重定向
重定向也是通过配置 routes 来完成
const router = new VueRouter({
routes: [
{
path:"/news",
component:News,
// redirect:{name:"yule"}, //重定向
redirect:"news/yule", //重定向
name:"news",
children:[
{
path:"tiyu",
component:Tiyu
},
{
path:"yule",
name:'yele',
component:Yule
}
]
}
]
})
别名:
{
path:"/news",
component:News,
alias:"/xinwen" //别名
}
路由组件传参
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。使用 props 将组件和路由解耦(使用props代替$route)。
路由:{
path:"/user/username/:username/age/:age",
// path:"/user/:username",
component:User,
props:true
}
User组件 :<template>
<div>
用:{{username}}---{{age}} 代理
<!-- {{this.$route.params.username}}
年龄 : {{this.$route.params.age}} -->
</div>
</template>
<script>
export default {
props:{
username:{
type:String,
default:''
},
age:{
type:[Number,String],
defualt:23
}
}
}
</script>
【注意】使用路由传递参数时,嵌套子组件不显示,这种情况在实际项目开发中一般也不会出现。
hash模式 和 History模式
URL路径的两种模式:
- http://localhost:8080/#/about #是hash模式,默认hash模式
- http://localhost:8080/about /是history模式
语法:const router = new VueRouter({
mode: 'history | hash',
routes: [...]
})
导航高亮效果配置
添加高亮效果时,首页的高亮会一直显示,需要添加属性 exact 来取消首个路由导航的高亮效果,例如: <router-link exact to="/">首页</router-link>
- router-link-active 设置链接激活时使用的 CSS 类名。默认值可以通过路由的实例选项通过 linkActiveClass 来全局配置。
例如 :添加全局样式 .router-link-active{
color:red;
}
此时,选中的父组件和重定向的子组件都显示红色
- router-link-exact-active 配置当链接被精确匹配的时候应该激活的 class。注意默认值也是可以通过路由构造函数选项通过 linkExactActiveClass 进行全局配置的。
例如:添加全局样式 .router-link-exact-active{
color:red;
}
或者:const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
linkActiveClass : "active",
routes
})
.active{
color : red;
}
此时只有选中的那个显示红色,因为加上 exact 代表精准匹配
导航守卫
- 全局前置守卫
router.beforeEach((to, from, next) => {
next();
})
守卫是异步解析执行,参数 :to–>即将要进入的目标 路由对象 ; from–>当前导航正要离开的路由 ; next–>是一个函数,必须调用,否则钩子永远都不会被解析或报错。
-
全局解析守卫
router.beforeResolve 和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。 -
全局后置钩子
也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身
router.afterEach((to, from) => {
// ...
//这里不能获取this,因为当守卫执行前,组件实例还没有被创建
})
- 路由独享的守卫
可以在路由配置上直接定义 beforeEnter 守卫,参数与全局前置一样。
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
next();
}
}
- 组件内的守卫
可以在路由组件内直接定义以下路由导航守卫:beforeRouteEnter【注】这里不能获取this,因为当守卫执行前,组件实例还没有被创建 , beforeRouteUpdate ,beforeRouteLeave
完整的导航解析流程
1.导航被触发。
2.在失活的组件里调用 beforeRouteLeave 守卫。
3.调用全局的 beforeEach 守卫。
4.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
5.在路由配置里调用 beforeEnter。
6.解析异步路由组件。
7.在被激活的组件里调用 beforeRouteEnter。
8.调用全局的 beforeResolve 守卫 (2.5+)。
9.导航被确认。
10.调用全局的 afterEach 钩子。
11.触发 DOM 更新。
12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,
创建好的组件实例会作为回调函数的参数传入。
路由元信息
需求:判断用户是否登录账号,如果登录则可以打开User页面,如果没有登录则当用户点击User页面的时候自动跳转到登录页面Login。
例如:
1.创建两个组件 User.vue 和 Login.vue
2.在路由中引入并且配置编辑,给user路由实例添加meta对象,isLogin是自定义
import User from '../views/User'
import Login from '../views/Login'
{
path:"/user",
name:"user",
component:User,
meta:{
isLogin:true //路由元信息
}
},
{
path:"/login",
component:Login
}
3.在全局前置守卫中编辑
router.beforeEach((to,from,next)=>{
if(to.meta.isLogin){
const token = true; //为true时可以打开User页面,false跳转到login页面
if(token){
next();
}else{
next({path:"/login"})
}
}else{
next();
}
})
4.在App.vue中配置路由出口
<router-link to="/user">User</router-link> |
<router-link to="/login">Login</router-link>
路由懒加载(重点)
当打包构建应用时,JS包会非常大,影响页面加载。路由懒加载就是把不同的路由对应的组件分割成不同的代码块,然后当路由被访问时才加载对应组件。经常用
{
path: '/about',
name: 'About',
component: () => import('../views/About.vue')
}
数据获取(重点)
有时候在进入路由时需要从服务器获取数据,可以在之前获取也可以在之后获取获取数据。
在导航完成之前获取数据:导航完成前,在路由进入的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。在为后面的视图获取数据时,用户会停留在当前的界面,因此建议在数据获取期间,显示一些进度条或者别的指示。如果数据获取失败,同样有必要展示一些全局的错误提醒。
例如:beforeRouteEnter(to,from,next){
next(vm => vm.getData()); //此处 不能!用this, 因为当守卫执行前,组件实例还没被创建
},
methods:{
getData(){ this.axios.get("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php").then(res=>{
console.log(res.data);
})
}
}
在导航完成之后获取数据:先完成导航,然后在接下来的组件生命周期钩子 created | mounted 中获取数据。在数据获取期间显示“加载中”之类的指示。
例如:methods:{
getData(){this.axios
.get("http://iwenwiki.com/api/blueberrypai/getIndexBanner.php")
.then(res=>{
console.log(res.data);
})
}
},
// created(){
//this.getData();
// }
mounted(){
this.getData();
}
过度动效
<router-view></router-view>
是基本的动态组件,所以我们可以用给它外层包裹<transition></transtion>
组件给它添加一些过渡效果,下面的用法会给所有路由设置一样的过渡效果。
例如:<transition name="fade">
<router-view/>
</transition>
.fade-enter-active, .fade-leave-active {
transition: opacity 1s;
}
.fade-enter, .fade-leave-to {
opacity: 0;
}
滚动行为
scrollBehavior方法 :使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。先让页面出现滚动条,然后:
例:const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes,
scrollBehavior(to,from,savedPosition){
return { x:0,y:800 }
}
})