Vue.js之vue-router路由

时间:2024-02-17 14:47:53

vue学习的一系列,全部来自于表哥---表严肃,是我遇到过的讲课最通透,英文发音最好听的老师,想一起听课就去这里吧 https://biaoyansu.com/i/hzhj1206

 

1概述

vue-router是vue的一个库,可以快速的开发一个单页应用;

在导航切换时,页面根本就不刷新,没有整页刷新的概念,所以用户的输入可以被保留下来,不丢失状态,不丢失数据;

不用每切换一次导航就重新拉取一遍数据,只需要取一次数据,就可以一直用;

在网页上最频烦的操作就是点点点,这样页面不刷新,就可以极大的节省前端和后端的资源。

 

2安装和基本配置

引用vue文件和vue-router的库文件,https://cdn.bootcss.com/vue-router/3.0.6/vue-router.js

例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

 

配置说明:

定义一个数组rounts,数组中的每一项都是一个配置,path代表路由的地址,/就表示首页(默认页);可以直接传进一个component,这个component和普通的component一样,该有的功能都可以用;

然后把定义的规则传给构造的路由,var router=new VueRouter();定义一个routes,把数组传给它就可以了;

在new Vue中加一个router属性,把定义的构造器router传进去;

html中,要加一个标签router-link,用to指定地址,再用router-view表示显示的视图,它显示的就是template中定义的内容

 

3传参及获取传参

vue-router中传参有两种方式,

第一种,User后面加个冒号:/user/:name,在template中通过{{$route.params.name}}来获取。

例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由-传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <!-- <p>我今年{{$route.query.age}}岁</p> -->
                    </div>
                    `
                }
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

 

第二种,通过query,就是地址后面跟着?这样,例如:

http://127.0.0.1:8848/vuetest.html?age=20#/,在template中把params换成query就可以了,{{$route.query.age}}

例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由-传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <p>我今年{{$route.query.age}}岁</p>
                    </div>
                    `
                }
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

 

4子路由

在上面例子的基础上,想实现“大美女后面加个/more,就是嵌套的路由,

再加一个children的配置项,代表子路由,也是一个数组,写法和父级的rounts一样,path可以写成more,传参写法也一样,$route.params.name。

然后在父级路由添加链接<router-link>,这里的to的写法有两种方式:

一种是用v-bind,中间动态的部分就用传参的形式写,

:to="\'/user/\'+$route.params.name+\'/more\'",就类似这样的。

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由-传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <router-link :to="\'/user/\'+$route.params.name+\'/more\'">更多</router-link>
                           <router-view></router-view>
                    </div>
                    `
                },
                children:[{
                    path:\'more\',
                    component:{
                        template:`
                    <div>
                        用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多...
                    </div>
                    `
                    }
                    
                }
                ]
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

 

另一种比较简洁,to的值直接写成more,然后给router-link加个属性append,表示追加的意思,这样就是在原来的后面加上一个more了,但是这种写法只能点击一次,再点“更多”,就又会多追加一个/more

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由-传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <router-link to="more" append>更多</router-link>
                           <router-view></router-view>
                    </div>
                    `
                },
                children:[{
                    path:\'more\',
                    component:{
                        template:`
                    <div>
                        用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多...
                    </div>
                    `
                    }
                    
                }
                ]
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

 

5手动访问和传参

5.1手动访问

想实现,点击某个按钮,button,加个点击事件,每隔一秒去访问一个链接,这样用户只点一次,就自动访问设置的链接了,使用this.router.pushpush这个接口就是专门用来访问某个链接的,写在push中的链接就被推到了历史记录中,

:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>手动访问和传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
                <button @click="surf">漫游</button>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <router-link to="more" append>更多</router-link>
                           <router-view></router-view>
                    </div>
                    `
                },
                children:[{
                    path:\'more\',
                    component:{
                        template:`
                    <div>
                        用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多...
                    </div>
                    `
                    }
                    
                }
                ]
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router,
            methods:{
                surf:function(){
                    setTimeout(function(){
                        this.router.push(\'/about\');
                        setTimeout(function(){
                            this.router.push(\'/user/大美女\');
                        },2000);
                    },2000);
                }
            }
        })
    </script>
</html>

 

这种手动触发的方式,虽然比较麻烦,但能做到更细粒度的控制,做出来的功能可以很强大,

router-link方式更方便简单,多数情况下还是用router-link

 

5.2手动传参

push中直接传一个对象,{name:’user’},这里的name指的是路由的名称,是在上面写规则那里指定的name,

 

然后传参可以写在params里,也是一个对象params:{name:\'大美女\'}

 

完整示例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>手动访问和传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/about">关于</router-link>
                <router-link to="/user/大美女">大美女</router-link>
                <router-link to="/user/小东西">小东西</router-link>
                <button @click="surf">漫游</button>
            </div>
            <div>
                <router-view></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/about\',
                component:{
                    template:`
                    <div><h1>关于</h1></div>
                    `
                }
            },
            {
                path:\'/user/:name\',
                name:\'user\',
                component:{
                    template:`
                    <div>
                        <p>我叫{{$route.params.name}}</p>
                        <router-link to="more" append>更多</router-link>
                           <router-view></router-view>
                    </div>
                    `
                },
                children:[{
                    path:\'more\',
                    component:{
                        template:`
                    <div>
                        用户 {{$route.params.name}} 的详细信息:内容内容在这里写很多...
                    </div>
                    `
                    }
                    
                }
                ]
            }
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router,
            methods:{
                surf:function(){
                    setTimeout(function(){
                        this.router.push(\'/about\');
                        setTimeout(function(){
                            this.router.push({
                                name:\'user\',
                                params:{
                                    name:\'大美女\'
                                }
                            });
                        },2000);
                    },2000);
                }
            }
        })
    </script>
</html>

 

6命名视图

如果页面中有两个router-view,可以用name属性进行区分,

然后写rounts时,把原来的component换成components,也是一个对象,键就是name,值又是一个对象,在对象里面定义template,例:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>路由-传参</title>
    </head>
    <body>
        <div id="app">
            <div>
                <router-link to="/">首页</router-link>
                <router-link to="/user">用户管理</router-link>
                <router-link to="/post">文章管理</router-link>
            </div>
            <div>
                <router-view></router-view>
                <router-view name="sidebar"></router-view>
                <router-view name="content"></router-view>
            </div>
        </div>
    </body>
    <script src="js/vue.js"></script>
    <script src="js/vue-router.js"></script>
    <script>
        var rounts=[
            {
                path:\'/\',
                component:{
                    template:`
                    <div><h1>首页</h1></div>
                    `
                }
            },
            {
                path:\'/user\',
                components:{
                    sidebar:{
                        template:`
                        <div>
                            <ul>
                                <li>用户1</li>
                                <li>用户2</li>
                            </ul>
                        </div>
                        `
                    },
                    content:{
                        template:`
                        <div>
                            内容1在这里...
                        </div>
                        `
                    },
                }
            },
            {
                path:\'/post\',
                components:{
                    sidebar:{
                        template:`
                        <div>
                            <ul>
                                <li>文章1</li>
                                <li>文章2</li>
                            </ul>
                        </div>
                        `
                    },
                    content:{
                        template:`
                        <div>
                            内容2在这里...
                        </div>
                        `
                    },
                }
            },
            
        ];
        
        var router=new VueRouter({
            routes:rounts
        });        
        
        var app=new Vue({
            el:\'#app\',
            router:router            
        })
    </script>
</html>

想命名多少个router-view都可以,但不建议命名太多,不好维护,一个页面2-5个比较合适。

 

 

7导航钩子 

状态和权限的检查,最好在路由层面就检查好,该驳回的驳回,该访问的访问,这样节省资源。而不要再到组件级别去检查,因为页面中组件太多。 

例: 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>导航钩子</title>
</head>
<body>

<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/login">登录</router-link>
        <router-link to="/post">帖子管理</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>

<script src="lib/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
var rounts=[
    {
        path:\'/\',
        component:{
            template:`
            <h1>首页</h1>
            `
        }
    },
    {
        path:\'/login\',
        component:{
            template:`
            <h1>登录</h1>
            `
        }
    },
    {
        path:\'/post\',
        component:{
            template:`
            <h1>帖子管理</h1>
            `
        }
    }
];

var router=new VueRouter({
    routes:rounts
});
    
    
var zyx=new Vue({
    el:\'#app\',
    router:router
});
</script>
</body>    
</html>

 

 

 

现在想实现,访问“帖子管理”时,会进行检查, 

vue中通过router的实例的一个方法beforeEach实现, 

参数说明: 

to,表示到哪里去 

from,表示从哪里来 

next,指定接下来要怎么做: 

  直接next();就是正常执行, 

  如果next(false);那么所有的路由都不工作了, 

  还可以传个地址, next(\'/login\');

例: 

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>导航钩子</title>
</head>
<body>

<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/login">登录</router-link>
        <router-link to="/post">帖子管理</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>

<script src="lib/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
var rounts=[
    {
        path:\'/\',
        component:{
            template:`
            <h1>首页</h1>
            `
        }
    },
    {
        path:\'/login\',
        component:{
            template:`
            <h1>登录</h1>
            `
        }
    },
    {
        path:\'/post\',
        component:{
            template:`
            <h1>帖子管理</h1>
            `
        }
    }
];

var router=new VueRouter({
    routes:rounts
});

router.beforeEach(function(to,from,next){
    //是否登录
    var logged_in=false;
    //如果没登录并且要访问post
    if(!logged_in && to.path==\'/post\'){
        //转到登录页
        next(\'/login\');
    }else{
        //正常执行
        next();
    }
});
    
var zyx=new Vue({
    el:\'#app\',
    router:router
});
</script>
</body>    
</html>

如果logged_infalse,那么点击帖子管理会跳到登录页,为true时才会正常访问

 

说白了这就是个中间件,也可称为路由的生命周期。

 

还可以在访问之后进行操作,用router.afterEach,其参数只有tofrom,不大常用。因为表示路由已加载完毕,可以真正运行这个路由下的所有组件了,那么就可以在这里发送一些全局的异步请求,写一些业务逻辑。这里面的内容放到组件的生命周期也可以。 

例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>导航钩子</title>
</head>
<body>

<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/login">登录</router-link>
        <router-link to="/post">帖子管理</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>

<script src="lib/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
var rounts=[
    {
        path:\'/\',
        component:{
            template:`
            <h1>首页</h1>
            `
        }
    },
    {
        path:\'/login\',
        component:{
            template:`
            <h1>登录</h1>
            `
        }
    },
    {
        path:\'/post\',
        component:{
            template:`
            <h1>帖子管理</h1>
            `
        }
    }
];

var router=new VueRouter({
    routes:rounts
});

router.beforeEach(function(to,from,next){
    //是否登录
    var logged_in=true;
    //如果没登录并且要访问post
    if(!logged_in && to.path==\'/post\'){
        //转到登录页
        next(\'/login\');
    }else{
        //正常执行
        next();
    }
});

router.afterEach(function(to,from){
    console.log(\'to:\',to);
    console.log(\'from:\',from);
});
    
var zyx=new Vue({
    el:\'#app\',
    router:router
});
</script>
</body>    
</html>

 

8元数据及路由匹配

还是上面的登录的例子,如果帖子管理中还有子路由,地址是/post/article

如果没有登录,那么post/”后面所有内容,都是不能访问的,

可以使用to的属性matched,意思是匹配了的路由,

例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>元数据及路由匹配</title>
</head>
<body>

<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/login">登录</router-link>
        <router-link to="/post">帖子管理</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>

<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
var rounts=[
    {
        path:\'/\',
        component:{
            template:`
            <h1>首页</h1>
            `
        }
    },
    {
        path:\'/login\',
        component:{
            template:`
            <h1>登录</h1>
            `
        }
    },
    {
        path:\'/post\',
        component:{
            template:`
            <div>
                <h1>帖子管理</h1>
                <router-link to="article" append>文章一</router-link>
                <router-view></router-view>
            </div>
            `
        },
        children:[
            {
                path:\'article\',
                component:{
                    template:`<h2>文章一的内容...</h2>`
                }
            }
        ]
    }
];

var router=new VueRouter({
    routes:rounts
});

router.beforeEach(function(to,from,next){
    //是否登录
    var logged_in=false;
    //如果没登录并且要访问post
    if(!logged_in && to.matched.some(function(item){
        return item.path==\'/post\';
    })){
        //转到登录页
        next(\'/login\');
    }else{
        //正常执行
        next();
    }
});
    
var zyx=new Vue({
    el:\'#app\',
    router:router
});
</script>
</body>    
</html>

访问/post/post/article都是跳到登录页

 

 

Tipssome方法,

some() 方法用于检测数组中的元素是否满足指定条件(函数提供)。

some() 方法会依次执行数组的每个元素:

如果有一个元素满足条件,则表达式返回true , 剩余的元素不会再执行检测。

如果没有满足条件的元素,则返回false

详情:https://www.runoob.com/jsref/jsref-some.html

 

还有第二种方法,如果页面中有非常多的路由,不可能一个个的都去写匹配,太麻烦,那么可以在路由的配置中来写,可以加一个meta的配置,元数据的意思,可以在meta中自定义一个login_required:true,然后return item.meta.login_required; 这样只要路由加上了这个配置,就能控制登录权限了,不用每一个都去js中判断了。

例:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>元数据及路由匹配</title>
</head>
<body>

<div id="app">
    <div>
        <router-link to="/">首页</router-link>
        <router-link to="/login">登录</router-link>
        <router-link to="/post">帖子管理</router-link>
        <router-link to="/test">新加测试</router-link>
    </div>
    <div>
        <router-view></router-view>
    </div>
</div>

<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
var rounts=[
    {
        path:\'/\',
        component:{
            template:`
            <h1>首页</h1>
            `
        }
    },
    {
        path:\'/login\',
        component:{
            template:`
            <h1>登录</h1>
            `
        }
    },
    {
        path:\'/post\',
        meta:{
            login_required:true
        },
        component:{
            template:`
            <div>
                <h1>帖子管理</h1>
                <router-link to="article" append>文章一</router-link>
                <router-view></router-view>
            </div>
            `
        },
        children:[
            {
                path:\'article\',
                component:{
                    template:`<h2>文章一的内容...</h2>`
                }
            }
        ]
    },
    {
        path:\'/test\',
        meta:{
            login_required:true
        },
        component:{
            template:`
            <h1>测试</h1>
            `
        }
    }
];

var router=new VueRouter({
    routes:rounts
});

router.beforeEach(function(to,from,next){
    //是否登录
    var logged_in=false;
    //如果没登录并且要访问post
    if(!logged_in && to.matched.some(function(item){
        return item.meta.login_required;
    })){
        //转到登录页
        next(\'/login\');
    }else{
        //正常执行
        next();
    }
});
    
var zyx=new Vue({
    el:\'#app\',
    router:router
});
</script>
</body>    
</html>