VueRouter
前端路由原理
监听 url 的变化,然后匹配路由规则,显示相应页面,无需刷新。
目前前端路由只有两种实现方式
- hash 模式
监听 hash 值变化(url 后的,从#号开始)window.addEventListener('hashchange',()=>{})
1 | //监听hash变化 |
因为 hash 发生变化的 url 都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用.
特点: hash 虽然出现在 URL 中,但不会被包括在 HTTP 请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
- history 模式
通过此模式改变 url 同样不会引起页面刷新,只会更新浏览器的历史记录’
修改为 history 模式:在 router 中添加mode: 'history'
.
history.pushState
替换当前历史记录history.replaceState
点击后退触发 popState 事件window.addEventListener('popState',e=>{})
切换历史状态
包括 back,forward,go 三个方法,对应浏览器的前进,后退,跳转操作.
1 | history.go(-2); //后退两次 |
修改历史状态
包括了 pushState,replaceState 两个方法,这两个方法接收三个参数:stateObj,title,url
.
1 | history.pushState({color:'red'}, 'red', 'red'}) |
通过 pushstate 把页面的状态保存在 state 对象中,当页面的 url 再变回这个 url 时,可以通过 event.state 取到这个 state 对象.
history 模式缺点
通过 history api,我们丢掉了丑陋的#,但是它也有个毛病:
不怕前进,不怕后退,就怕刷新,f5(如果后端没有准备的话),因为刷新是实实在在地去请求服务器的,不玩虚的。
在 hash 模式下,前端路由修改的是#中的信息,而浏览器请求时是不带它玩的,所以没有问题.但是在 history 下,你可以自由的修改 path,当刷新时,如果服务器中没有相应的响应或者资源,会请求失败.
所以,如果你想在 github.io 上搭一个单页博客,就应该选择 hash 模式
vue-router 路由
- 安装 vue-router
npm install --save vue-router
- 引用
1 | import router from "vue-router"; |
- 配置路由文件,并在 Vue 实例中注入
1 | import Vue from "vue"; |
- 确定视图加载的位置
1 | <router-view></router-view> |
路由跳转
在路由文件中,一般是router/index.js
.就是把路由单独写一个文件
1 | import Vue from "vue"; |
然后在页面组件中
1 | <template> |
vue-router 路由参数传递(传参)
- 必须在路由内加入路由的
name
- 必须在
path
后加/:
加传递的参数 - 传递参数(传值)和接收参数(具体看下面两种方法)
传递参数方法:
1 | //params: |
注意: params 传参,push 里只能是 name:’xxx’,不能是 path:’/xxx’,因为 params 只能用 name 来引入路由,如果这个写成 path,接收参数页面会是 undefined.
1 | //两种方式: |
1 | <router-link :to="{path: '/helloearth',query:{msg: 只有一个地球}}"> |
导航守卫
导航守卫就是路由跳转过程中的一些钩子函数.
1 | //钩子函数执行后输出的顺序 |
导航守卫分为:全局的、单个路由独享的、组件内的三种.
全局的
分别是beforeEach
,beforeResolve
,afterEach
1 | const router = new VueRouter({ ... }) |
beforeEach
: 全局前置守卫.在路由跳转前触发,参数:to,from,next.这个钩子函数主要用于登录验证.beforeResolve
: 全局解析守卫.和beforeEach
类似.区别是在beforeEach
和组件内beforeRouteEnter
之后,afterEach
之前调用.afterEach
: 全局后置钩子.和beforeEach
相反,在路由跳转完成后触发,参数: to,from.他发生在beforeEach
和beforeResolve
之后.
三个参数(to,from,next)
to
:Route
: 即将要进入的目标路由对象from
:Route
: 当前导航正要离开的路由next
:Function
: 必须调用该方法来 resolve 这个钩子.否则不能进入路由.执行效果依赖 next 方法的调用函数.next(false)
: 中断当前导航.如果浏览器的 URL 改变,那么 URL 会重置到 from 路由对应的地址.next('/')
: 跳转到一个不同的地址.next(error)
: 如果传入的 next 的参数是一个 Error 实例.则导航会被终止且该错误会被传递给router.onerror()
注册过的回调.
确保要调用
next
方法,否则钩子就不会被resolved
.
路由独享的守卫 beforeEnter
只在进入路由时调用.
可以在路由配置上直接定义 beforeEnter 守卫:
1 | const router = new VueRouter({ |
组件内的守卫
beforeRouteEnter
进入路由前beforeRouteUpdate
路由复用同一个组件时beforeRouteLeave
离开当前路由时
1 | const Foo = { |
beforeRouteEnter 守卫不能访问 this
因为钩子在组件实例还没被创建的时候调用,可以通过传一个回调给next
来访问组件实例 。
不过,可以通过传一个回调给 next 来访问组件实例.在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数.
但是回调的执行时机在 mounted 后面,所以在我看来这里对 this 的访问意义不太大,可以放在created
或者mounted
里面。
1 | beforeRouteEnter(to, from, next){ |
beforeRouteLeave
导航离开该组件的对应路由时调用,我们用它来禁止用户离开,比如还未保存草稿,或者在用户离开前,将 setInterval
销毁,防止离开之后,定时器还在调用。
1 | beforeRouteLeave (to, from , next) { |
路由钩子函数的错误捕获
如果在导航守卫的钩子函数中有错误,可以这样捕获:
1 | router.onError((callback) => { |
假设是从 a 组件离开,第一次进入 b 组件
完整的路由导航解析流程:
- 触发进入其他路由。
- 调用要离开路由的组件守卫
beforeRouteLeave
- 调用全局前置守卫:
beforeEach
- 在重用的组件里调用
beforeRouteUpdate
- 调用路由独享守卫
beforeEnter
- 解析异步路由组件
- 在将要进入的路由组件中调用
beforeRouteEnter
- 调用全局解析守卫
beforeResolve
- 导航被确认
- 调用全局后置钩子
afterEach
- 触发 DOM 更新(mounted)
- 执行
beforeRouteEnter
守卫中传给 next 的回调函数
疑难杂症
监听物理返回键或页面返回
1 | //unit.js |
1 | //index.vue |