Router

路由

<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 下的相应组件
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,242评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,769评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,484评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,133评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,007评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,080评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,496评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,190评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,464评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,549评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,330评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,205评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,567评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,889评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,160评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,475评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,650评论 2 335

推荐阅读更多精彩内容