路由概念
路由这一概念最早出现在后端(即后端渲染),前端是没有的。
它经历了三个阶段的演进:由后端路由(让服务器去处理)到前后端分离的开发模式(也就是Ajax的出现),再到单页面富应用阶段(Simple Page web Application,即SPA)
目前前端流行的三大框架,都有自己的路由实现:
- Angular的ngRouter
- React的ReactRouter
- Vue的vue-router(是官方的路由插件,它和vue.js是深度集成的,适合用于构建单页面应用)
vue-router是基于路由和组件的
- 路由用于设定访问路径,将路径和组件映射起来
- 在vue-router的单页面应用中,页面的路径的改变就是组件的切换
Vue前端路由的核心:
- 改变URL,但整体页面不再进行刷新,区别于H5的a标签。
而我们常见的大多数网站,当改变URL的时候,都会向服务器发送请求数据,然后再渲染到页面上,这一过程会导致页面重新加载刷新,而使用前端路由实现则不会如此。
Vue实现前端路由的两种方式:
- URL的hash,即我们通常称哈希模式(#)。
- H5的history模式:pushState | replaceState | go | back
哈希模式:location.hash="path"
URL中的hash
- URL中的hash也就是锚点(#),本质上是改变window.location的href属性(hype reference,超链接引用)
- 通过直接赋值location.hash来改变href,致使页面不会发送刷新
history模式(栈结构)
- 栈结构就像一个弹夹,最新进栈都会被压在栈底,
- 原理是先进后出,出栈只能先从栈顶往下,然后拿东西,
- 队列是先进先出。
history.pushState({}, '', 'paht'),即入栈模式
history.back()返回上一层的页面(类似返回上一次的历史记录,即出栈模式)。
history.replaceState()替换模式
替换模式不是压入栈,此外,返回按钮也不能用。
小结:
- history.back()等价于 history.go(-1)
- history.forward()等价于 history.go(1)
- 这两个接口等同于浏览器界面上的前进后退按钮
安装路由
如果在脚手架创建模板的时候,没有勾选配置路由,现在是可以通过npm进行安装的。
-
使用Vue CLI2版本配置的路由,出现在src源码目录下。
为了帮助我们更好地加深认知router,直接删除改文件夹,自己手动配置路由。
Vue CLI2脚手架手动配置路由具体步骤如下:
-
在src源码目录下创建一个名叫router的文件夹,在该文件夹(router)下创建一个index.js的脚本文件,在index.js这个脚本文件里面配置路由相关的信息。
-
index.js文件配置路由相关信息详情:
-
在main.js入口文件进行导入操作使用。
- 到这里,路由的框架已经具备搭建好了,现在只差配置路由的映射关系。
为了保持整洁的页面,将脚手架默认创建的组件删掉(即Hello World.vue文件),保持一个空的components目录即可。
清除App.vue的模板内容,保持整洁。
在components目录下创建一个About.vue组件和一个Home.vue组件。
内容如下:
-
在index.js里面配置路径的映射关系。
-
一个对象是一个映射关系。
-
来到根组件App.vue配置路由模板,router-link是vue-router自己注册的组件,他们是全局组件,可以在任何位置使用。
-
页面url效果,类似h5的a标签,其实最终渲染的也是a标签。
-
路由默认值的设置,给用户第一视角,进来的时候就显示出整个网站的首页。
纠错:重定向路由这里应该是redirect: 'Home',少了引号。
-
将默认的哈希hash模式改为history模式,因为hash模式自带#号作为默认,看起来有点别扭。
-
router-view 决定组件渲染的位置,它实现的效果是占位。
-
router-view切换组件效果
router-link属性
- 属性to :用于跳转指定的路径。
- 属性tag: 默认渲染成a标签,如果想使用其他标签。通过tag属性绑定h5标签使用。
-
属性replace: 只允许在页面切换,不能使用浏览器的返回按钮。
要重新打开8080端口,才能看到实际效果。
属性replace会自带一个class样式的活跃状态类。
有了它,我们可以写样式效果。
- active-class属性:用于修改默认的router-link-active样式,一般在进行高亮导航菜单或者底部的tabbar时,会用到该类。
-
linkActiveClass属性:统一修改样式,只不过,它不再是router-link的属性了,而是属于router对象实例属性了。
我们来到index.js
路由代码实现跳转方式
-
有时候,页面的跳转可能需要执行对应的JS代码,这个时候,就可以使用第二种跳转方式了。
但不能点击自身(首页)超过2次否则会报错,首页和分页轮流切换不会报错。
使用replace可以解决。
Vue动态路由
- 上面设置的路由都是静态路由。
- 在某些情况下,一个页面的path路径可能时不确定的,比如我们进入用户界面时,希望是如下这种路径/user/zhangsan或者是/user/lisi
- 除了有前面的/user之外,我们通常会见到某些网站后面还跟上了对应的id号。
- 这种path和component的匹配关系,我们称之为动态路由,它也是路由传递数据的一种方式。
代码试图:
-
创建一个User.vue组件。
-
在index.js里面配置组件的映射关系。
-
在App.vue组件里面实现动态绑定路由。
-
需求:在User.vue组件获取当前用户界面的信息。
路由懒加载方案
- 当我们通过npm run build打包构建应用的时候,js包会变得非常巨大,会影响页面的加载进程效果。
- 为什么会这样?
因为,我们知道路由中通常会定义了很多不同的页面组件,这个页面最后会被打包放在一个js文件中,但是这么多页面组件被压挤在一个js文件中,必然会造成这个页面非常的庞大。
如果我们一次性从服务器请求下这个页面,可能需要花费一定的时间,甚至导致用户的浏览器出现了短暂的空白情况,用户体验十分不好。 - 我们能否避免这种页面短暂空白的情况呢?
官方给出了解释:使用路由懒加载方案可以实现,如果我们能把不同路由对应的组件分割成不同的代码块进行处理,然后当路由被访问的时候加载对应的组件,这样就变得更加的高效了。 -
路由懒加载到底做了什么?
路由懒加载的主要作用就是将对应的组件打包成一个个的js代码块。
只有在这个路由被访问到的时候,才开始加载对应的组件。
打包js文件的解析
路由懒加载的方式(三种)
早期出现2种,一种使用的是异步解决方案,另一种是AMD写法,了解即可。
-
方式三:ES6方案,在ES6中,我们可以用更加简洁的写法(箭头函数)来组织Vue异步组件和Webpack的代码分割。
-
为了使用路由懒加载模式,我们需要在index.js里面进行代码重构。
-
通过npm run build 打包后。
嵌套路由
-
嵌套路由是一个非常常见的功能,比如我们想在home(首页)中继续进行细分页面的时候就会应用到了嵌套路由了,我们希望通过home首页访问new页面(即/home/new)或者通过/home/detail访问一些内容。
一个路径映射一个组件,因此,访问这两个路径也会分别渲染2个组件。
- 路径和对应组件的关系如下:
实现嵌套路由步骤: -
创建对应的子组件,填写简洁模板即可。
- 在index.js中配置路由映射,并且是配置对应的子路由(这里我们配置的是home首页的子路由),使用到children(子路由)数组可以包含多个对象。
路由懒加载配置
children配置子路由
-
在组件内部使用占位标签<router-view>,因为我们的新闻页面(news)和详情页(details)是在首页里面配置子路由的,所以我们是在home页里面使用占位标签<router-view>进行显示。
但当我们点击其他页面,然后再回来点击首页,发现我们点击原来子路由的(新闻页面)时,新闻页面的内容消失了,而我们希望是默认显示新闻页面的。
所以我们需要在子路由下面进行重定向,默认显示news页面。
路由的参数传递,传递参数主要有2种类型:分别是params和query
params的类型
-
配置路由格式(index.js): /router/:id
-
传递的方式(App.vue):在path后面跟上对应的值
- 传递后形成的路径(User.vue):/router/id
-
从Profile.vue中取App.vue的值。
query的类型
- 配置路由格式:/router,也就是普通配置路由即可。
创建Profile.vue组件。
index.js文件中,进行路由懒加载配置。
普通路由映射配置。
-
传递的方式(App.vue中):对象中使用query的key作为传递方式
-
传递后形成的路径:/router?id=123, /router?id=abc
-
从Profile.vue中取App.vue的值。
路由代码实现路由参数跳转方式
-
定义2个按钮,绑定相对应的路由点击事件。
全局导航守卫
全局导航守卫是个啥?
- vue-router提供的导航守卫主要用来监听路由的进入和离开的。
- vue-router提供了beforeEach前置守卫(guard)钩子(hook)函数和afterEach的后置导航钩子函数(路由跳转完后实现回调机制),他们会在路由即将改变前和改变后触发。
- 简单点就是想监听路由来回跳转的过程,获取路由跳转到哪个位置,然后在监听函数里面搞事情。
全局导航守卫实际效果是啥?
-
就是想实现我们的网页置顶title标签
- 当我们点击首页的时候,title变成首页,点击用户页面的时候,title变成用户,有点类似小程序和app头顶上的NavigationBar导航,点击了哪个导航,就显示对应的title。
怎么实现上述效果呢?
给每个对应的参与路由跳转的组件添加生命周期钩子函数,created.
我们来看下title的效果:
小结:
- 我们上面采用的是比较普通的实现方式,我们会想到其实修改标题的位置就是每一个路由对应的.vue组件文件。
-通过created生命周期函数,执行对应的代码进行修改即可 - 虽然效果可实现了,但一个个去修改显然有点唐突+狼狈了(几百个页面的时候就显然不合适这样的操作去维护)。
导航守卫的使用
- 真正的全局守卫导航是在router/index.js里面配置的。
-
我们首先拿到router对象,这个router对象里面有一个beforeEach()方法,这个beforeEach()方法里面有三个参数,分别是to, from, next
- 我们可以利用beforeEach来完成对导航标题的修改。
- 我们可以在钩子当中用meta来定义一些标题。
- 其次呢,利用导航守卫修改我们的标题。
beforeEach导航钩子三个参数的解释:
- to: 即将进入的目标路由对象。
- from: 当前导航即将要离开的路由对象。
- next: 调用该方法后,才能进入下一个钩子,不调用一下next()方法的话,整个页面的路由跳转将会被拦截,next方法告诉浏览器下一步要干嘛,千万别给我拦截了。
- 大概就是从from跳转到to
显然我们不能拿到to.title这样一个的数据
-
所以要在组件映射里面添加每个对应的meta属性(元数据:描述数据的数据)
这样全局导航守卫效果总体就实现了,但还有一点瑕疵,就是当我们点击首页的时候,title变成了undefine。
为什么会这样?
这和我们在首页里面使用了嵌套子路由有关系。
我们尝试打印to对象看下。
所以我们只要找到matched(匹配)的0号位,就能解决子路由这块的小bug
如果是后置钩子函数,即afterEach,不需要主动调用next()函数。
- 上面我们使用的导航守卫,叫做全局守卫
- 此外还有路由独享的守卫、组件内的守卫。
keep-alive遇上vue-router
keep-alive可以保留组件内部切换回来的显示之前用户的浏览状态。
比如我们当前在首页,点击了新闻详情页面子路由,然后去点击了档案,现在再回去点击首页,发现它停留在了新闻页面,而不是我们想回去刚刚浏览过的新闻详情页面,遇到这种状况的话,就要使用keep-alive去解决了。
那为什么会出现这样的情况呢?
我们用生命周期函数测试下就明白了。
我们可以看到,当我们点击首页的时候,组件被创建生成,点击其他页面的时候,组件已经销毁了,如此一直循环下去,默认显示的都是新闻页面,而不是新闻详情页面。
keep-alive(保持组件活着)概念理解,结合代码解决上面提出的问题。
- keep-alive是Vue内置的一个组件,可以使被包含的组件保留状态,或避免重新渲染。
keep-alive有2个重要属性 -
include - 字符串或正则表达,只有匹配的组件会被缓存。
-exclude - 字符串或正则表达,任何匹配的组件都不会被缓存。
-
router-view也是一个组件,如果直接被包在keep-alive里面,所以路径匹配到的试图组件都会被缓存。
我们还要注销掉首页下,默认新闻页面内容展示。
我们需要用到组件内的守卫知识:beforeRouteLeave
官网:https://router.vuejs.org/zh/guide/advanced/navigation-guards.html