Vue-Router 基础
简介
Vue-Router
是用来将一个Vue
程序的多个页面进行路由的。比如一个Vue
程序(或者说一个网站)有登录
、注册
、首页
等模块,那么我们就可以定义/login
、/register
、/
来映射每个模块。
安装
通过script
加载进来:
使用CDN
:
加载最新版的:
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
加载指定版本的:
<script src="https://unpkg.com/vue-router@3.0.7/dist/vue-router.js"></script>
下载到本地:
<script src="../../lib/vue-router.js"></script>
通过npm
安装:
npm install vue-router
文档
官方文档:https://router.vuejs.org/zh/
github地址:https://github.com/vuejs/vue-router
基本使用
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vuedemo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <div class="container-fluid"> <div class="row"> <div class="col-md-8 col-md-offset-2"> <ul class="nav nav-tabs"> <li role="presentation" class="active"> <router-link to="/find">发现音乐</router-link> </li> <li role="presentation"> <router-link to="/my">我的音乐</router-link> </li> <li role="presentation"> <router-link to="/friend">朋友</router-link> </li> </ul> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> </div> </div> </div> <script> var find = Vue.extend({template: "<h1>发现音乐</h1>"}); var my = Vue.extend({template: "<h1>我的音乐</h1>"}); var friend = Vue.extend({template: "<h1>朋友</h1>"}); var routes = [ {path: "/find", component: find}, {path: "/my", component: my}, {path: "/friend", component: friend}, {path: "/", component: find} ]; const router = new VueRouter({routes}); new Vue({router}).$mount("#app"); </script> </body> </html>
解释:
- 在
vue-router
中,使用<router-link>
来加载链接,然后使用to
表示跳转的链接。vue-router
最终会把<router-link>
渲染成<a>
标签。 <router-view>
是路由的出口,也就是相应url
下的代码会被渲染到这个地方来。Vue.extend
是用来加载模板的。routes
是定义一个url
与组件的映射,这个就是路由。VueRouter
创建一个路由对象。$mount
是挂载到哪个组件上。
动态路由
在路由中有一些参数是会变化的,比如查看某个用户的个人中心,那肯定需要在url
中加载这个人的id
,这时候就需要用到动态路由了。
示例代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vuedemo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id='app'> <ul> <li> <router-link to="/">首页</router-link> </li> <li> <router-link to="/profile/123">个人中心</router-link> </li> </ul> <router-view></router-view> </div> <script> let index = Vue.extend({template: "<h1>首页</h1>"}); let profile = Vue.extend({ template: "<h1>个人中心:{{$route.params.userid}}</h1>", mounted() { console.log(this.$route); console.log(this.$router); } }); let router = new VueRouter({ routes: [ {path: "/", component: index}, {path: "/profile/:userid", component: profile}, ] }); new Vue({ el: '#app', router }) </script> </body> </html>
解释:
:userid
:动态的参数。this.$route.params
:这个里面记录了路由中的参数。
组件复用
当使用路由参数时,例如从/user/foo
导航到/user/bar
,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。
复用组件时,想对路由参数的变化作出响应的话,你可以简单地watch(监测变化)
$route
对象:
const User = { template: '...', watch: { '$route' (to, from) { // 对路由变化作出响应... } } }
或者是使用导航守卫:
const User = { template: '...', beforeRouteUpdate (to, from, next) { // react to route changes... // don't forget to call next() } }
示例
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vuedemo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id='app'> <ul> <li> <router-link to="/profile/张三">张三的个人中心</router-link> </li> <li> <router-link to="/profile/李四">李四的个人中心</router-link> </li> </ul> <router-view></router-view> </div> <script> let index = Vue.extend({ template: "<h1>首页</h1>" }); let profile = Vue.extend({ template: "<h1>个人中心:{{$route.params.userid}}</h1>", mounted() { console.log(this.$route.params.userid); }, // watch:{ // "$route": function(to,from){ // console.log("to:",to); // console.log("from:",from); // } // } beforeRouteUpdate: function (to, from, next) { console.log("to:", to); console.log("from:", from); // next() } }); let router = new VueRouter({ routes: [ {path: "/", component: index}, {path: "/profile/:userid", component: profile} ] }); new Vue({ el: '#app', router }) </script> </body> </html>
匹配404错误
在路由规则中,*
代表的是任意字符。所以只要在路由的最后添加一个*
路由,那么以后没有匹配到的url
都会被导入到这个视图中。示例代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>vuedemo</title> <script src="https://cdn.jsdelivr.net/npm/vue"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id='app'> <router-view></router-view> </div> <script> let index = Vue.extend({ template: "<h1>首页</h1>" }); let aboutus = Vue.extend({ template: "<h1>关于我们</h1>" }); let profile = Vue.extend({ template: "<h1>个人中心:{{$route.params.userid}}</h1>", mounted() { if (this.$route.params.userid != '123') { this.$router.replace("/404") } }, watch: { "$route": function (to, from) { // to // 从to中提取userid,然后判断是不是等于123,如果不等于,那就跳转到404 // 从to中提取userid,然后发送给服务器,服务器判断存不存在,如果不存在,同样跳转到404 } } }); let notfound = Vue.extend({ template: "<h1>404页面没找到</h1>", }); let router = new VueRouter({ routes: [ {path: "/", component: index}, {path: "/aboutus", component: aboutus}, {path: "/profile/:userid", component: profile}, {path: "/404", component: notfound}, {path: "*", component: notfound} ] }); new Vue({ el: '#app', router: router }) </script> </body> </html>
嵌套路由
有时候在路由中,主要的部分是相同的,但是下面可能是不同的。比如访问用户的个人中心是/user/111/profile/
,查看用户发的贴子是/user/111/posts/
等。这时候就需要使用到嵌套路由。示例代码如下:
const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ { // 当 /user/:id/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: 'profile', component: UserProfile }, { // 当 /user/:id/posts 匹配成功 // UserPosts 会被渲染在 User 的 <router-view> 中 path: 'posts', component: UserPosts } ] } ] });
编程式导航
之前我们学习了使用<router-link>
可以在用户点击的情况下进行页面更新。但有时候我们想要在js
中手动的修改页面的跳转,这时候就需要使用编程式导航了。
$router.push
跳转:
想要导航到不同的URL
,则使用router.push
方法。这个方法会向history
栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的URL
。
当你点击<router-link>
时,这个方法会在内部调用,所以说,点击<router-link :to="...">
等同于调用router.push(...)
。
声明式 | 编程式 |
---|---|
<router-link :to="..."> |
router.push(...) |
// 字符串
router.push('home') // 对象 router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: '123' }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }})
注意:如果提供了path
,params
会被忽略,上述例子中的query
并不属于这种情况。取而代之的是下面例子的做法,你需要提供路由的name
或手写完整的带有参数的path
:
const userId = '123' router.push({ name: 'user', params: { userId }}) // -> /user/123 router.push({ path: `/user/${userId}` }) // -> /user/123 // 这里的 params 不生效 router.push({ path: '/user', params: { userId }}) // -> /user
router.replace(location, onComplete?, onAbort?)
:
跟 router.push 很像,唯一的不同就是,它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
router.go(n)
:
这个方法的参数是一个整数,意思是在history
记录中向前或者后退多少步,类似 window.history.go(n)
。
// 在浏览器记录中前进一步,等同于 history.forward() router.go(1) // 后退一步记录,等同于 history.back() router.go(-1) // 前进 3 步记录 router.go(3) // 如果 history 记录不够用,那就默默地失败呗 router.go(-100) router.go(100)
命名路由
有时候,通过一个名称来标识一个路由显得更方便一些,特别是在链接一个路由,或者是执行一些跳转的时候。你可以在创建Router
实例的时候,在routes
配置中给某个路由设置名称。
const router = new VueRouter({ routes: [ { path: '/user/:userId', name: 'user', component: User } ] })
要链接到一个命名路由,可以给router-link
的to
属性传一个对象:
<router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
这跟代码调用 router.push() 是一回事:
router.push({ name: 'user', params: { userId: 123 }})
命名视图
有时候想同时 (同级) 展示多个视图,而不是嵌套展示,例如创建一个布局,有sidebar
(侧导航) 和main
(主内容) 两个视图,这个时候命名视图就派上用场了。你可以在界面中拥有多个单独命名的视图,而不是只有一个单独的出口。如果router-view
没有设置名字,那么默认为default
。
<router-view class="view one"></router-view> <router-view class="view two" name="a"></router-view> <router-view class="view three" name="b"></router-view>
一个视图使用一个组件渲染,因此对于同个路由,多个视图就需要多个组件。确保正确使用 components
配置 (带上s
):
const router = new VueRouter({ routes: [ { path: '/', components: { default: Foo, a: Bar, b: Baz } } ] })
重定向和别名
重定向也是通过routes
配置来完成,下面例子是从/a
重定向到/b
:
const router = new VueRouter({ routes: [ { path: '/a', redirect: '/b' } ] })
重定向的目标也可以是一个命名的路由:
const router = new VueRouter({ routes: [ { path: '/a', redirect: { name: 'foo' }} ] })
“重定向”的意思是,当用户访问/a
时,URL
将会被替换成/b
,然后匹配路由为/b
,那么“别名”又是什么呢?
/a
的别名是/b
,意味着,当用户访问/b
时,URL
会保持为/b
,但是路由匹配则为 /a
,就像用户访问/a
一样。
上面对应的路由配置为:
const router = new VueRouter({ routes: [ { path: '/a', component: A, alias: '/b' } ] })