一. 了解router#
VueRouter 是Vue的插件,它可以通过根据地址栏动态的变换进而实现对不同路由组件进行切换。
比如:http:localhost:8080/path/123?username=xxx&password=xxx#route
在上面的url中,#后面部分我们称之为锚点,也称为hash值,而vue-route就是根据这个锚点去动态的在不同的组件之间切换。
当然,url中的hash信息我们是可以通过js获取出来的,如下:
console.log(window.location)
在控制台上的输入如下:
我们在日常的开发中大多数情况下是在编写 xxx.vue, 然后这个vue文件中会如下的三部分。这其实就是一个vue 的模版/组件, 每个组件是有name或者是path的,那VueRouter实际上要解决的问题就是根据不同的锚点找到不同的组件, 这也就是所谓的路由分发。
路由分发就面临着几个问题:
- 传递参数
- 进行事件的处理和调用
路由底层的实现依赖了window.history对象
<template> <div> ... </div> </template> <script> ... </script> <style> ... </style>
二. 工作流程#
- 首先我们的初始化一个前端项目
- 下载vuejs,他是我们的核心
- 下载vue-router, router是vue的插件,因此我们要将下载好的vue-router注册进vue
- vue-router中定义规则。根据这个规则可以实现根据页面上的hash值找到对应的模版。
- 在页面中可以通过a标签定义转发的路由
- 通过
<router-view>
对路由对应模版的内容进行占位
三. 简单的Demo#
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <style> body { background-color: #eee; } li { list-style: none; display: inline-block; } nav { text-align: center; } a { color: #333333; text-decoration: none; display: inline-block; padding-left: 5px; text-align: center; } .content { text-align: center; } </style> </head> <body> <div id="app"> <nav> <!--四种定义请求地址--> <ul> <li><a href="#/bar"> Bar </a></li> <li><a href="#/about"> About </a></li> <li><a href="#/">Home</a></li> <li> <router-link to="/notfound">Not Found</router-link> </li> <li> <router-link to="/foo">Foo</router-link> </li> </ul> </nav> <hr> <div class="content"> <!--通过路由,组件最终被渲染的位置--> <router-view></router-view> </div> </div> </body> <script> // 创建模版 const NotFound = {template: `<p>Page not found</p>`} const Home = {template: '<p>Home page</p>'} const About = {template: '<p>About page</p>'} const Foo = {template: '<div>foo</div>'} const Bar = {template: '<div>bar</div>'} // 将我们的模版和hash值进程绑定,封装成对象数组 const routes = [ {path: '/', component: Home}, {path: "/about", component: About}, {path: "/notfound", component: NotFound}, {path: "/foo", component: Foo}, {path: "/bar", component: Bar} ] // new路由对象 const router = new VueRouter({ // 将存贮有 hash和组件的数组注册进路由器 routes }) var app = new Vue({ el: "#app", router, // ES6语法糖,相当于 router:router data: { title: "this is title" } }) </script> </html>
实现的效果:点击nav中的标签,下面的内容会随着变化
上面的例子是必须要理解的,不然真的就是仅仅知道有这么个效果,但是前因后果,以及对我们在做什么是不清楚的。大概的思路就是,我们首先创建组件,然后为每一个组件找一个唯一的hash值组成一个对象,多个组件+路由就可以形成一个数组,我们将这个数组注册进VueRouter对象,再把VueRouter对象以插件的形式注册进Vue对象。 此时环境就搭建好了。
我们再去点击这种连接时,浏览器的地址栏中就会出现#xxx 的锚点
<a href="#/">Home</a> <router-link to="/notfound">Not Found</router-link>
流程如下:
四. 理解template和route的组合#
通过如下的例子理解template和route是如何配合使用的, 我把理解写在代码中的注释上
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <style> body { background-color: #eee; } li { list-style: none; display: inline-block; } nav { text-align: center; } a { color: #333333; text-decoration: none; display: inline-block; padding-left: 5px; text-align: center; } .content { text-align: center; } </style> </head> <body> <div id="app"> <nav> <!--四种定义请求地址--> <ul> <li><a href="#/bar"> 静态模版 </a></li> <li><a href="#/dynamic"> 动态模版 </a></li> </ul> </nav> <hr> <div class="content"> <!--通过路由,组件最终被渲染的位置--> <router-view></router-view> </div> </div> </body> <script> // 静态模版,之所以叫他静态模版是因为我们可以直接看到它里面的数据全部是写死的,换句话说,这中静态模版除非我们真的仅仅是做一个展示页面,不需要和后端互动数据。除此之外,再无他用 const Bar = {template: '<div>bar</div>'} // 动态模版 // 可以看到,这种模版其实就是我们在开发中大量自己编写的模版,一般他们都会被抽出去单独称为一个 xxx.vue的文件。在这里我们可以得心应手的编写和后端交互的逻辑,作为一个完整vue组件的它拥有包括vue组件生命周期在内的所有函数 // 第一个问题:这个动态组件是如何注册进Vue中的呢? // 回答: 往下看看下面new Vue()部分的代码。将当前的动态组件注册在Vue的components部分即可。 // 第二个问题:如何找到这个动态组件并将其渲染在页面上的呢? // 回答:首先来说,路由的跳转肯定是依赖于浏览器地址兰中 hash值的变化,所以我们得先通过a标签,或者router-link标签发送请求,让浏览器的地址栏hash值发生变化,具体的编码往上看。当地址栏中hash值发生变化后vue-router拿着变化的hash值去找对应的组件,找不到就报错,找到了就渲染 // 第三个问题:vue-router获取到hash值后,是和Vue对象的components中的注册信息的name比对,还是去跟vue-router中的path信息去比对? 还是去跟vue-router中的定义的name去比对呢? // 回答:和vue-router中的path比对,命中后才算数 // 第四个问题:vue对象的components部分的name有什么用呢? // 回答:能问这个问题,恐怕你是忘记了,组件需要映射成标签才能用,而这个name就是标签名,是可以自定义的 // 第五个问题:VueRouter中的路由对象中的name叫啥呢? // 回答:首先这个router并不是必填项,如果有了那么,我们称其为具名路由 // 第六个问题:当我们把如下的部分单独抽取成单独的文件时,如果对外暴露这个组件呢? // 回答:这个问题在下面通过一张图片直观的看出来。 const Home = { template: '<div>{{title}}</div>', data: function () { return { title: "this is a title" } }, methods: xxx , computed: xxx , } // 将我们的模版和hash值进程绑定,封装成对象数组 const routes = [ {path: "/bar", component: Bar}, {path: "/dynamic", component: Home} ] // new路由对象 const router = new VueRouter({ // 将存贮有 hash和组件的数组注册进路由器 routes }) var app = new Vue({ el: "#app", components:{ "dynamic":Home }, router, // ES6语法糖,相当于 router:router }) </script> </html>
实现的效果如图:
如何将动态组件抽取出去?如何导入其他抽取出去的组件使用?
像上图中我们导入其他的组件后,注册进当前组件的 components部分,就是,通过这种方式vue可以将组件映射成标签。
其实现在就能小结一下,画一张不完整的图,来理解这个过程
五. Vue-Router-GoBack记录返回#
Vue-Router-GoBack指的是路由的记录返回。比如我们依此点击路由: 从1 跳转到 2 跳转到 3 跳转到 4 ,goback就可以实现路由分别从 4 3 2 1会退回来。
vue-router-goback底层依赖的就是浏览器的内置对象 window.history
问题1: window.history的作用。
可以很直观的认为它里面会存储着浏览器地址栏中曾经访问过的地址
常用的api如下:
window.history.forword() // 前进 window.history.back() // 回退
当然这两个api能起作用的前提是,浏览器中确实存在历史记录才行。
问题2: vue-router-goback 如果实现回退呢?
如下:
// 一个{component + path}是一个路由对象 // 如下代码中的 this.$router即路由器对象,也就是多个路由对象的集合 window.history.length > 1 ? this.$router.go(-1) : this.$router.push("/")
六. Router-Link#
不怕多嘴,因为必须得知道:路由的切换是根据浏览器地址栏url中的hash值而定的
在早期的vue中,通过设置a标签中的href属性以#/开头,来实现浏览器地址栏的变化
vue2.x后,router-link标签的诞生,就是为了替换掉a标签
看下router-link的源码如下:
router-link以一个指令标签的方式被注册在vue上,name是RouterLink,实体是 Link
不知道大家有没有多想一下,因为如果是一个自定义的组件的话,我们想将其映射成标签,在html元素中使用,需要我们手动的将把它注册在vue组件的 components部分。 上面标黄色的部分,就是为什么我们可以直接使用类似下面的标签
<router-view> </router-view> <router-link to=“/xxx/”> </router-link>
看看Link的详情
模拟实现router-link
<div> <my-link to="/bar"> 自定义指令 </my-link> </div> <script> // 自定义comonent const myLink = { props: [ 'to' ], template:"<a :href='\"#\"+to'><slot></slot></a>" } Vue.component("my-link", myLink) ... </script>