前言
用vue-cli进行开发已经有一段时间,vue-router也常常会用到,但之前都是按需索取,并没有系统地学习过此插件,最近系统地学习了官网上的基础部分,把官网上的例子重新写一遍,故做出此篇总结。
目录
先放一张目录图(最右级的内容只是方便我去辨别这些内容)
以下每项内容都会提供一个完整的例子,例子内容与官网提供的例子大部分是一样的,但加上了注释,而且是一个可执行的html文件。
起步
在vue中实现路由配置的方式有两种,分别是声明式编程和函数式编程,两者的格式如下(官网截图)
前者是html中的标签,后者在js部分书写(note:常用写法this.$router.push(xxx))。这里简单介绍下前者,后者在下面详细介绍。
实例:
<html>
<head>
</head>
<body>
<div id="app">
<h1>hello world!</h1>
<!-- router-link是一个vue-router提供的链接,链接指向一个component,在router-view中显示 -->
<router-link to="/foo">Go to foo</router-link>
<router-link to="/bar">Go to bar</router-link>
<router-view></router-view>
</div>
<!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
// define the component
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
// 定义routes,配置url及相应的component
const routes = [
{
path: '/foo', component: Foo
},
{
path: '/bar', component: Bar
}
]
// create the router instance and pass the 'routes' option
const router = new VueRouter({
routes
})
// inject router in id
const app = new Vue({
router
}).$mount('#app')
</script>
</body>
</html>
效果:
路由配置的基本过程:
- routes数组中应该找到元素与router-link的to属性对应起来,如果没有,点击router-link时会跳转到空页面,在项目中经常配置一个全局路由捕捉这些跳转链接,显示错误链接(404)页面。其实就是编写一个这样的页面,当路由不存在时就显示此页面。这种用法非常常见,还可衍生为登陆失败,权限控制等情况
- 路由配置要在routes上下功夫
- routes的名称不固定
- 实际开发中组件不会这么简单,一般是单独作为一个文件引入
动态路由匹配
背景:我们常常会遇到需要引用同一个页面,但加载不同数据的情况。如用户登陆后进入自己的主页。以csdn为例,当我们来到自己的首页时,会发现有一个me.csdn.net的共同前缀,后面加上一个/id,即https://me.csdn.net/id,进入别人的主页也是如此(有些是blog.csdn.net)这些路由拥有共同的前缀,根据后面的id识别不同的登陆用户,这就涉及到动态路由配置的概念,vue-router提供了便利的方法配置这些路由。
实例:
<html>
<head>
<title>Vue Router</title>
</head>
<body>
<div id="app">
<h1>hello world!</h1>
<!-- 拥有共同的/user前缀 -->
<router-link to="/user/foo">Go to user foo</router-link>
<router-link to="/user/bar">Go to user bar</router-link>
<router-view></router-view>
</div>
<!-- 映入vue依赖文件 -->
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
// define the component
const User = {
// 通过$route.params数组获取参数,即foo或bar,并显示
template: '<div>user {{ $route.params.page }} </div>',
// 捕捉路由的变化
watch: {
$route(to, from) {
// react to route changes...
console.log('to', to);
console.log('from', from);
}
}
};
// define routes
const routes = [
{
// 动态识别所有/user/xxx路由,渲染到user组件
path: '/user/:page', component: User
},
]
// create the router instance and pass the 'routes' option
const router = new VueRouter({
routes
})
// inject router in id
const app = new Vue({
router
}).$mount('#app')
</script>
</body>
</html>
- 在$route.params可以拿到路由某字段的具体值(如示例中.page 和 :page 对应),$route.query可以拿到url中?后面传递的参数具体作用百度,非常方便,常用于传递数据到后台
- watch对象中的$route(to, from) {xxx}函数可以捕捉到路由的变化,to是目标路由参数,from是跳转前路由参数
嵌套路由
背景:有时我们会遇到这样一个场景,在一个视图中,有某一部分的内容根据路由的变化而变化(引用不同子组件),如图(官网截图):
这里路由形式与动态路由配置相似(两者常常结合起来用),但核心的不同点是,嵌套路由引用不同组件 ,这不单是嵌套路由,也是嵌套组件的设计
实例:
<html>
<head>
<title>Vue Router</title>
</head>
<body>
<div id="app">
<h1>hello world!</h1>
<router-link to="/user/page/home">Go to user home</router-link>
<router-link to="/user/page/foo">Go to user foo</router-link>
<router-link to="/user/page/bar">Go to user bar</router-link>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
// define the component
const Home = { template: '<div>home</div>' };
const Foo = { template: '<div>foo</div>' };
const Bar = { template: '<div>bar</div>' };
const User = {
template: `
<div>
<div>User {{ $route.params.page }}</div>
<router-view></router-view>
</div>`,
};
// define routes
const routes = [
{
path: '/user/:page', component: User,
// 在children里面配置子组件
children: [
{
// 不要加斜杆(/)否则无效
path: 'home',component: Home
},
{
path: 'foo', component: Foo
},
{
path: 'bar', component: Bar
}
]
},
]
// create the router instance and pass the 'routes' option
const router = new VueRouter({
routes
})
// inject router in id
const app = new Vue({
router
}).$mount('#app')
</script>
</body>
</html>
编程式导航
背景:编程式导航不再引用router-link和router-view,而是用router.push(xxx)代替,前者是html标签,常用于页面布局,后者是js中的函数,常用于跳转和灵活传递参数(note:常在方法中以this.$router.push({....})方式调用)各司其职
命名路由
背景:以上方式都是通过在router-link的to里面配置路径,其实也可以通过访问组件名的方法,访问到相应的页面
实例:
<!DOCTYPE html>
<html>
<head>
<title>Named Routes</title>
</head>
<body>
<div id="app">
<h1>Name Routes Test</h1>
<router-link to="/a">go to a</router-link>
<router-link :to="{ name: 'b' }">go to b</router-link>
<router-link :to="{ name: 'c', params:{ id: 333 } }">go to c</router-link>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
const comA = { template: '<div>component A</div>' };
const comB = { template: '<div>component B</div>' };
const comC = { template: '<div>component C id is {{ $route.params.id }}</div>' };
const routes = [
{
path: '/a',
name: 'a',
component: comA
},
{
path: '/b',
name: 'b',
component: comB
},
{
path: '/c/:id',
name: 'c',
component: comC
}
];
const router = new VueRouter({
routes
});
const app = new Vue({
router
}).$mount('#app');
</script>
</body>
</html>
命名视图
背景:有时,在一个页面中想同时引入多个组件,此时每个组件用一个名称(name)来标识即可。此功能可用来组件自己复杂的网页。(但在vue-cli中这种用法比较少,因为脚手架天然生成views目录,组建组件的操作一般在这目录中执行)
实例:
<!DOCTYPE html>
<html>
<head>
<title>Name Views</title>
</head>
<body>
<div id="app">
<h1>This is a name views test.</h1>
<router-link to="/foo">go to the foo</router-link>
<router-link to="/bar">go to the bar</router-link>
<router-view></router-view>
<router-view name="a"></router-view>
<router-view name="b"></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
const comA = { template: `<div> component A </div>` };
const comB = { template: `<div> component B </div>` };
const comC = { template: `<div> component C </div>` };
const routes = [
{
path: '/foo',
components: {
default: comA,
a: comB,
b: comC
}
},
{
path: '/bar',
components: {
default: comB,
a: comC,
b: comA
}
}
];
const router = new VueRouter({
routes
});
const app = new Vue({
router
}).$mount('#app');
</script>
</body>
</html>
重定向
背景:假设有两个路由,分别是/a和/b,重定向的意思是,每次申请到达/a,最后会重定向到/b,显示的内容当然也是/b对应的组件。
实例:
<!DOCTYPE html>
<html>
<head>
<title>Alias Test</title>
</head>
<body>
<div id="app">
<h1>This is redirect test.</h1>
<router-link to="/a">go to a</router-link>
<router-link to="/b">go to b</router-link>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
const comA = { template: '<div>component A</div>' };
const comB = { template: '<div>component B</div>' };
const comC = { template: '<div>component C</div>' };
const routes = [
{
path: '/a',
// 重定向的几种写法
// redirect: '/c'
// redirect: { name: 'c' }
redirect: to => { // the function receives the target route as the argument
console.log(to); // to 是目标路由,即对象内的 path: '/a'
return '/c' // return redirect path/location here.
}
},
{
path: '/b',
component: comB
},
{
path: '/c',
component: comC,
name: 'c'
}
];
const router = new VueRouter({
routes
});
const app = new Vue({
router
}).$mount('#app');
</script>
</body>
</html>
别名
背景:假设有三个链接/a,/b,/c,它们的名字不同,但他们最终到达的页面是一致的,也可理解为这三个链接都引用了相同的组件,所以它们互相是别名
实例:
<!DOCTYPE html>
<html>
<head>
<title>Alias Test</title>
</head>
<body>
<div id="app">
<h1>This is a alias test.</h1>
<router-link to="/a">go to a</router-link>
<router-link to="/b">go to b</router-link>
<router-view></router-view>
</div>
<script src="https://unpkg.com/vue/dist/vue.js"></script>
<script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
<script>
const comA = { template: '<div>component A</div>' };
const comB = { template: '<div>component B</div>' };
const comC = { template: '<div>component C</div>' };
const routes = [
{
path: '/a',
component: comA,
// 在地址栏中输入/c,/d地址,最终显示的都是comA的内容
alias: ['/c','/d']
},
{
path: '/b',
component: comB
},
];
const router = new VueRouter({
routes
});
const app = new Vue({
router
}).$mount('#app');
</script>
</body>
</html>
试着在地址栏中输入/c,/d会发现显示的内容和点击'go to a'是一致的,都是component A
体会
在学习过程中,有个比较深的体会是关于学习时间的分配,我花了大部分时间来编写官网上提供的例子,并测试这些结果,而花在阅读文档的时间相对少许多(应该说是盯着文档上的代码片段来编写例子)因为文档内容其实比较少,而且文档上提供的代码只是片段,如果不去看它提供的完整例子,很难去深入理解这些内容,而以前我就是这么干的,最终导致的结果是,代码跑通了,但如果遇到问题要花一段时间去定位,也没有完全理解这些代码,只是知道这样能用而已,而且内心隐隐会产生一种很轻微的恐惧感,因为我在使用自己并不了解的东西。当我耐着性子把这些内容跑一遍后,这种恐惧感就消失了,至少是减轻了很多。类似的代码也能看懂了。内心会有种自信的声音“以后遇到这类问题,可以独立去分析解决了”。我想只有学到了这种程度,才能算是入门了吧。从这一点上可以想到,有时内心的恐惧是进步的方向,也可以想到,有效的学习方式离不开实践,必须重视手写代码啊!单看文档学到的知识是不深刻的。