路由
<body>
<div id="box">
<div>
<router-link to="/home">Home</router-link>
<!--router-link用来展示组件导航-->
<!--to属性用于指定链接-->
<!--<router-link>标签会默认被渲染成a标签-->
<!--当前被点击的路由会自带.router-link-active属性-->
<router-link to="/news">News</router-link>
</div>
<div>
<!--路由出口,路由默认会被渲染在这里-->
<router-view></router-view>
</div>
</div>
</body>
<script>
//创建路由组件
var Home = {
template: "<h4>我是主页</h4>"
},
News = {
template: "<h4>我是新闻</h4>"
};
//定义组件
var routes=[
{path:"/home",component:Home},
{path:"/news",component:News},
{path:"*",redirect:"/home"}
]
//创建路由实例,配置路由实例
var router=new VueRouter({
routes:routes,
linkActiveClass:"color"
})
//创建并将路由挂载到根实例
new Vue({
el:"#box",
router:router
})
</script>
动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全部映射到同一个组件,例如我们有一个user组件用来展示用户信息,那么对于不同ID的用户,我们都需要使用这个组件来渲染,那么我们就可以在vue-router的路由路劲中使用动态路径参数来达到这个效果
body>
<div id="box">
<div>
<router-link to="/home/1">Home</router-link>
<router-link to="/home/2">News</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
<template id="home">
<div>
<h4>this's homr</h4>
</div>
</template>
<template id="news">
<div>
<h4>this's news</h4>
</div>
</template>
<script>
var Home = {
template: "#home"
},
News = {
template: "#news"
};
var routes=[
{path:"/home/:id",component:Home},
{path:"/news",component:News}
];
//在routes中路径参数使用:标记,那么在匹配路由时,所有 /home/参数 格式的路径都会被匹配到Home组件,当然也可以同时使用一个或多个 例如 /home/:id/name/:id 路径参数使用:标记那么表示这个参数可以是任意值,:id的作用只是一个占位符,也可以使用正则来匹配,如:/:id(\\d+)
var router=new VueRouter({
routes:routes
});
new Vue({
el:"#box",
router:router
})
//在路由中设置多段路径参数,对应的值都会设置到$route.params中,例如:
// /user/:username匹配到路径/user/tom,那么我们可以通过$router.params获取到{username:"tom"}
</script>
</body>
当我们使用路由参数时,例如从/user/foo跳转到/user/bar时,原来的组件会被复用,这样更加高效,但是同时也意味着组件的生命周期函数不是再被调用,如果想要对路由参数的变化做出响应的话,可以所使用watch监测$route对象
<script>
var User = {
template: "#user",
watch:{
'$route'(to,from){
console.log(to);
console.log(from);
//to表示从哪个路由开始跳转
//from表示跳转到哪个路由
//在跳转时会触发$route函数
}
}
}
</script>
路由嵌套
实例一
<body>
<div id="box">
<div>
<router-link to="/home">Home</router-link>
<router-link to="/news">News</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
<template id="home">
<div>
<h4>this's home</h4>
<ul>
<li>
<router-link to="/home/tom">Tom</router-link>
<router-link to="/home/aix">Aix</router-link>
</li>
<li>
<router-view></router-view>
</li>
</ul>
</div>
</template>
<template id="news">
<div>
<h4>this's news</h4>
</div>
</template>
<script>
var Home = {
template:"#home"
},
News = {
template: "#news"
},
Tom={
template:`
<p>my name's tom</p>
`
},
Aix={
template:`
<p>my name's aix</p>
`
};
var routes=[
{
path:"/home",
component:Home,
children:[
{
path:"tom",
component:Tom
},
{
path:"aix",
component:Aix
}
]
},
{path:"/news",component:News}
]
var router=new VueRouter({
routes
})
new Vue({
router
}).$mount("#box")
</script>
</body>
实例二
<body>
<div id="box">
<div>
<h5>
<router-link to="/user/foo">A</router-link>
<router-link to="/user/foo/bar">B</router-link>
<router-link to="/user/foo/cat">C</router-link>
</h5>
<router-view></router-view>
</div>
</div>
<script>
const User = {
template: `
<div>
<p>{{$route.params.id}}</p>
<router-view></router-view>
</div>
`
}
const userHome = {
template: `
<div>Home</div>
`
}
const bar = {
template: `
<div>A</div>
`
}
const cat = {
template: `
<div>
<div>{{$route.matched}}</div>
<div>B</div>
</div>
`
}
const router = new VueRouter({
routes: [
{
path: "/user/:id",
component: User,
children: [
{path: "", component: userHome},
{path: "bar", component: bar},
{path: "cat", component: cat}
]
}
]
})
new Vue({
router
}).$mount("#box")
</script>
</body>
编程式导航
在vue中我们除了使用<router-link>创建a标签来定义导航链接,还可以杰作router的实例方法,通过编写代码来实现,我们使用router-link标签时,实际在内部会调用router.push方法,所以说点击<router-link :to="/home">等同于调用router.push("/home")
<body>
<div id="box">
<div>
<li @click="home">Home</li>
<li @click="news">News</li>
</div>
<div>
<router-view></router-view>
</div>
</div>
<script>
const Home = {
template: `
<div>
<h3>this's Home</h3>
</div>
`
};
const News = {
template: `
<div>
<h3>this's News</h3>
</div>
`
};
const router = new VueRouter({
routes: [
{path: "/home", component: Home},
{path: "/news", component: News}
]
})
new Vue({
router,
methods: {
home() {
router.push("/home")
},
news() {
router.push("/news")
}
}
}).$mount("#box")
</script>
</body>
-
router.push()
该方法回想histiry栈添加一个新的记录,所以当用户点击浏览器后退按钮时,会回到之前的URL
-
router.repalce()
该方法和router.push相似,唯一不同在于,它不会向history添加新纪录,而是替换掉当前的history记录,所以用户不能点击浏览器后退按钮,该方法相当于<router-link :to="..." replace>
-
router.go(n)
该方法的参数是一个整数,意思实在history记录中向前或者后退多扫不,类似window.history.go(n),如果浏览器history记录本身小于n,那么会失败,并且不会返回失败信息
push以及replate的参数可以是一个字符串路径,或则一个描述的对象:
//字符串
router.push("/home")
//对象
router.push({psth:"/home"})
//命名的路由
router.push({name:"user",params:{userId:123}})
//带查询参数,变成/register?plan=private
router.push({path:'register',query:{plan:'private'}})
命名路由
有时候,通过一个名称来表示一个路由显的更方便一些,特别是在链接一个路由或执行一些跳转的时候,我们可以在创建Router实例的时候,在routers配置中给某个路由设置名称
<body>
<div id="box">
<div>
<router-link :to="{name:'home',params:{homeId:123}}">Hmoe</router-link>
<!--params表示传入的动态路由homeId的参数123,该参数会传入到路由的url地址中-->
<router-link :to="{name:'news'}">ews</router-link>
</div>
<div>
<router-view></router-view>
</div>
</div>
<script>
const Home = {
template: `
<div>
<h5>this's Home</h5>
</div>
`
},
News = {
template: `
<div>
<h5>this's News</h5>
</div>
`
};
var router = new VueRouter({
routes: [
{
path: "/home/:homeId",
name: "home",
//在router中为路由设置名称后,在router-link中:to 直接接受路由名称即可
component: Home
},
{
path:"/news",
name:"news",
component:News
}
]
})
new Vue({
router
}).$mount("#box")
</script>
</body>
命名视图
如果我们想要同时在统计展示多个视图,而不是嵌套展示,例如创建一个布局,有导航栏和主内容两个视图,我们可以使用命名视图,利用命名视图我们可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口,如果router-view标签没有设置名字,那么默认为default
<style>
.nav {
width: 100%;
height: 40px;
color: forestgreen;
}
.main {
width: 100%;
height: 100px;
color: forestgreen;
border: 1px solid #000;
}
</style>
</head>
<body>
<div id="box">
<div>
<router-link to="/nav">Nav</router-link>
<router-link to="/main">Main</router-link>
</div>
<div>
<router-view name="navView" class="nav"></router-view>
<router-view name="mainView" class="main"></router-view>
</div>
</div>
<script>
const Nav = {
template: `
<div>
<h5>this's nav</h5>
</div>
`
},
Main = {
template: `
<div>
<h5>this's main</h5>
</div>
`
};
const router=new VueRouter({
routes:[
{
path:"/nav",
components:{
"navView":Nav
}
},
{
path:"/main",
components:{
"mainView":Main
}
}
]
})
new Vue({
router
}).$mount("#box")
</script>
</body>
重定向和别名
重定向是指将当前路由的指向重新定位到其它路由上,使用redirect来实现
const router=new VueRouter({
routes:[
{path:"/a",
redirect:"/b"}
//此时的/a的路由点击会被重定向到/b路由上
]
})
//一般我们使用重定向来进行初始化页面的展示
{ path:"*",redirect:"/b"}
//重定向也可以传入一个路由名称
{path:"/a",redirect:"{name:'b'}"}
别名和重定向的大致作用是类似的,但是区别在与
使用重定向时当用户访问/a时,匹配路由为/b,URL地址中也会被替换为/b
使用别名时当用户访问/a时,匹配的路由为/b,但是URL地址中不会被替换,依然是/a,就像用户访问/a一样
const router=new VueRouter({
routes:[
path:"/a",
alias:"/b"
]
})
导航钩子
vue-router提供了导航钩子主要用来拦截导航,或则完成跳转或取消,vue-router的导航钩子会在路由发生改变时执行,有多种方式可以在路由发生改变时执行钩子:全局的,单个路由独享的,或者是组件级的
-
全局钩子
使用router.beforeEach注册一个全局的before钩子
const router=new VueRouter({...}) router.beforeEach((to,form,next)=>{ ..... })
当一个导航触发时,全局的before钩子会按照创建顺序调用,钩子是异步解析执行,此时导航在所有钩子执行完之前一直处于等待中
每个钩子接收三个参数:
- to 表示将要进入的目标路由对象
- form 表示当前导航将要离开的路由
- next 如果了解node的话可以理解为node中的next()中间件,next()表示进行下一个导航,next(false)表示中断当前的导航,也就是不再执行下一个跳转导航的操作,next("/")或者next({path:"/",...})表示跳转到我们指定的导航
一定要注意,一定要调用next()方法,否则之后的操作都不会被执行
同样可以注册一个全局的after钩子,不过它没有next方法,不能改变导航
router.afterEach(function (to,form) { console.log(to); console.log(form); alert("离开"+form.fullPath+",进入"+to.fullPath); })
-
单个路由的钩子
通过为单个路由设置beforeEnter方法,该方法接收的参数和beforeEach是一样的
const router = new VueRouter({ routes: [ { path: "/home", component: Home, children: [ { path: "info", component: Info, meta: { requiresAuth: true,age:18 }, beforeEnter:function (to,form,next){ console.log(to); console.log(form); next(); } }, {path: "set", component: Set} ] }, {path: "/news", component: News} ] })
同样需要注意一定调用next(),否则不会进入到该导航
-
组件内的钩子
也可以在路由组件内直接定义导航钩子
- beforeRouteEnter
- beforeRouteUpdata(2.2)
- beforeRouteLeave
News = {
template: `
<div>
<h4>this's News</h4>
</div>
`,
data() {
return {
msg: 1
}
},
beforeRouteEnter: function (to, form, next) {
//注意在这里不能调用实例的this,因为该方法是在组件被创建之前调用的,所以此时实例是不存在的
next(vm => {
console.log(vm.$data.msg);
});
//但是可以通过为next传入一个回调,把组件实例作为回调参数的方法来访问组件的实例
},
beforeRouteUpdate:function (to,form,next) {
//该方法只有在当前组件被复用时会调用
//例如,对于一个带有动态参数的路径,foo/:id,在foo/1和foo/2之间跳转的时候
//由于会渲染同样的foo组件,因此逐渐实例会被复用,而这个钩子会被调用,类似动态组件中的watch
//可以访问实例的this
},
beforeRouteLeave:function(to,form,next) {
//在导航离开组件的对应路由时调用
//可以访问组件实例this
//这个钩子通常用于在用户还未保存修改钱突然离开,可以通过next(false)来取消导航
}
}
路由元信息
定义路由的时候可以定义路由的meta字段,在meta中定义的字段,可以在路由记录中获取,可以用来对对每个路由进行配置,在vue中,我们称呼router配置中的每个路由对象为路由记录,路由记录可以是嵌套的,因此,当一个路由匹配成功后,它可能匹配多个路由记录,例如我们匹配到/foo/bar这个URL将会匹配副路由记录以及自路由记录,一个路由匹配到的所有路由记录会暴露为$route.matched数组,因此,我们可以遍历$$router.matched来检查路由记录中的meta字段
const router = new VueRouter({
routes: [
{
path: "/home",
component: Home,
children: [
{
path: "info",
component: Info,
meta: {
requiresAuth: true,
title:"主页"
}
},
{path: "set", component: Set}
]
},
{path: "/news", component: News}
]
})
...........
router.beforeEach((to,form,next)=>{
if(to.matched.some(record => record.meta.requiresAuth)){
//if的判断条件是用来判断meta中设置的requiresAuth变量是否为true
//可以用来判断该页面是否需要用户登录才能进入该页面
//如果我们设置一个路由的meta中requiresAuth变量为true,在全局路由钩子函数执行的时候都会进入if判断
console.log(to.matched.some(record => record.meta.requiresAuth));//true
//meta中设置的值也可以用来保存该路由中的信息,例如在meta中设置title属性和值,在进入该页面的时候获取该title值设置到页面中等操作
document.getElementById("test").innerText=to.meta.title;
if(用户没有登录){
next("/login"); //跳转到登录页面
}else {
next()
}
}else{
next()
//如果该页面不需要登录操作,执行用户操作
}
})
注意,在to和form中我们可以获取到跳转的路由的信息,下面是打印的to的信息:
{name: undefined, meta: {…}, path: "/home/info", hash: "", query: {…}, …}
滚动行为
使用前端路由,当我们切换到新路由时,想要页面滚动到顶部,或者是保持原先的滚动位置,就像重新加载页面那样,vue-router提供了更好的方法来让自定义路由切换页面是如何滚动
注意,这个功能只在HTML5 history模式下可用
在创建一个router实例的时候,可以提供一个scrollBehavior方法,该方法会在用户进行路由切换时触发
const router=new VueRouter({
routes:[
{
path:"/",
component:Home
}
],
scrollBehavior(to,form,savedPosition){
//scrollBehavior方法接收to,form路由对象,第三个参数savedPosition当且仅当在浏览器前进后退按钮触发时才可用
//该方法会返回滚动位置的对象信息,如果返回一个布尔假肢,或者是一个空的对象,那么不会发生滚动
//我们可以在该方法中设置返回值来指定页面的滚动位置,例如:
return {x:0,y:0}
//表示在用户切换路由时让是所有页面都返回到顶部位置
//如果返回savedPosition,那么在点击后退按钮时就会表现的像原生浏览器一样,返回的页面会滚动过到之前按钮点击跳转的位置,大概写法如下:
if(savedPosition){
return savedPosition
}else{
return {x:0,y:0}
}
//如果想要模拟滚动到锚点的行为:
if(to.hash){
return {
selector:to.hash
}
}
}
})
router-link属性
-
to
表示目标路由的链接,当被点击时,内部会立刻把to的值传到router.push(),通过to传递的链接会被保存在history记录中,所以可以使用浏览器回退
-
replace
如果设置replace属性的话,当路由被点击时,会调用router.replace(),本次操作不会留下history记录
<router-link to="/home" replace></router-link>
-
append
在设置append属性后,则表示正在当前相对路径前添加路径,例如我们是从/a导航到/b,那么在设置了append属性后路由导航会跳转到/a/b,而不是/b
<router-link to="a" append>a</router-link> //注意不要写/a的形式
-
tag
如果想要把router-link渲染成除a外的其它标签,可以使用tag来指定渲染之后应该是哪种标签,同样他还是会监听点击,触发导航
<router-link to="/home" tag="li"></router-link>
-
exact
表示是否激活全局,可以理解为如果一个导航的路径是/home开头的,并且我们设置了点击样式,在/home路径的导航设置了exact的情况下所有以、home开头的路径的导航的样式都会同时改变
https://jsfiddle.net/8xrk1n9f/ 这是vue官方的一个简单直观的例子
-
将激活时的css类名应用在外层元素
有时候我们需要让css类应用在外层元素,而不是a标签本身,那么可以用router-link渲染外层元素,包裹着内层原生a标签
<router-link tag="li" to="/home"> <a>Home</a> </router-link>
这种情况下,a将作为真实的链接,它会获得正确的href,而激活时的css类名则会设置到外层的li
events
用来可以用来触发导航的事件,默认是"click"
router-view属性
- name
如果 <router-view>设置了名称,则会渲染对应的路由配置中 components 下的相应组件