vue-router
# 三种路由模式
vue-router 默认是 hash 模式
hash 模式
history 模式
abstract 模式
vue-router 在不支持 history 模式的浏览器 , 会自动会退到 hash 模式。(源码是通过 fallback 进行降级处理)
# 原理
vue-router 原理主要分成两部分,一部分是安装,另一个是实现数据监控和页面跳转。
- 安装
通过混入 beforeCreate 实现路由的绑定和监听操作。初始化 router。调用 Vue 工具类方法 defineReactive,当 router 发生改变时,页面能及时响应更新。最后通过 registerInstance 来实现对 router-view 的挂载操作。
(1)混入 beforeCreate(实现路由的绑定和监听操作)和 destoryed 方法;
(2)全局挂载$router和$route;
(3)注册 router-link 和 router-view 两个组件。
- 数据监控和页面跳转
路由更新 -> 视图。路由发生变化(hash 有 hashChange 监听方法,history 有 popstate),改变浏览器里的地址,再更新视图。采用 hash 或者 history 的路由模式,前端实现路由跳转。history 模式中,主要通过 pushstate、replaceState、go 实现,它们负责改变浏览器的路由,但是不跳转,这就实现了前端的路由,而 popstate 是监听方法,处理路由改变后,前端页面的显示问题。就是用栈来实现。
# 1. hash 模式
利用 URL 中的 hash("#");
'#' 和后面 URL 片段标识符被称为 hash, 可通过 window.location.hash
属性读取.
hash 虽然出现在 url 中,但不会被包括在 http 请求中,它是用来指导浏览器动作的,对服务器端完全无用,因此,改变 hash 不会重新加载页面。
使用 window.addEventListener("hashchange", fun) 监听路由的变化,然后使用 transitionTo(功能是路由跳转) 方法更新视图
每一次改变 hash(window.location.hash
),都会在浏览器访问历史中增加一个记录。
利用 hash 的以上特点,就可以来实现前端路由"更新视图但不重新请求页面"的功能了。
HashHistory.push()
原理: 通过 Vue.mixin()
方法,全局注册一个混合,影响注册之后所有创建的每个 Vue 实例,该混合在 beforeCreate 钩子中通过 Vue.util.defineReactive()
定义了响应式的_route 属性。所谓响应式属性,即当\_route
值改变时,会自动调用 Vue 实例的render()
方法,更新视图。
顺序:\$router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app.\_route=route}-->vm.render()
HashHistory.replace()
原理: 调用 window.location.replace
方法将路由进行替换
- 监听地址栏
hash 值的改变会触发 hashchange 事件
上面的 VueRouter.push()
和 VueRouter.replace()
是可以在 vue 组件的逻辑代码中直接调用的,除此之外在浏览器中,用户还可以直接在浏览器地址栏中输入改变路由,因此还需要监听浏览器地址栏中路由的变化 ,并具有与通过代码调用相同的响应行为,在 HashHistory 中这一功能通过 setupListeners 监听 hashchange 实现:
需要注意的是调用 history.pushState()或 history.replaceState()不会触发 popstate 事件。只有在做出浏览器动作时,才会触发该事件,如用户点击浏览器的回退按钮(或者在 Javascript 代码中调用 history.back()或者 history.forward()方法)
# 2. history 模式
主要 HTML5 History API
使用 history.back(), history.forward()和 history.go() 方法来完成在用户历史记录中向后和向前的跳转。等操作会主动触发 popstate 事件
HTML5 History API 方法:
pushState 添加历史记录条目
replaceState 修改历史记录条目
popstate 当活动的历史记录项发生变化时, popstate 事件都会被传递给 window 对象
go() 用 go() 方法载入到会话历史中的某一特定页面, 通过与当前页面相对位置来标志 (当前页面的相对位置标志为 0)
window.history.go(-1); // 向后移动一个页面
window.history.go(1); // 向前移动一个页面
- back() 在 history 中向后跳转
# 3. abstract 模式
abstract 模式没有使用浏览器 api,可以放到 node 环境或者桌面应用中。
# hash 模式和 history 模式区别
url 不同: hash 带#,histary 不带#
pushState 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发记录添加到栈中
兼容性: hash 兼容 IE8 以上,history 兼容 IE10 以上 ,hash 支持低版本浏览器和 IE 浏览器,history 模式下需要支持 H5 的浏览器,使用的是 H5 的 api
底层监听的事件不一样
hash 模式: window.addEventListener('hashChange', function() { // ... });
history 模式:window.addEventListener('popstate', function() { // ... });
- 第三种路由模式 Abstract: 支持所有 javascript 运行模式。如果发现没有浏览器的 API,路由会自动强制进入这个模式。
# 三种路由守卫
# 完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用 beforeRouteLeave 守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
# 全局路由守卫
- 全局前置守卫 beforeEach
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
2
3
4
5
参数:
to: Route: 即将要进入的目标 路由对象
from: Route: 当前导航正要离开的路由
next: Function: 一定要调用该方法来 resolve 这个钩子。执行效果依赖 next 方法的调用参数。
- 全局解析守卫 beforeResolve
在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
- 全局后置钩子 afterEach
# 路由独享守卫
- beforeEnter
# 组件内路由守卫
- beforeRouteEnter
在渲染该组件的对应路由被 confirm 前调用
- beforeRouteUpdate
在当前路由改变,但是该组件被复用时调用
- beforeRouteLeave
导航离开该组件的对应路由时调用
# 其它问题
# route 和 router 有什么区别?
# $route 是“路由信息对象”(是正在跳转的这个路由的局部对象),
包括 path,params,hash,query,fullPath,matched,name 等路由信息参数。
- $route.path
字符串,等于当前路由对象的路径,会被解析为绝对路径,如 "/home/news" 。
- $route.params
对象,包含路由中的动态片段和全匹配片段的键值对
- $route.query
对象,包含路由中查询参数的键值对。例如,对于 /home/news/detail/01?favorite=yes ,会得到$route.query.favorite == 'yes' 。
- $route.router
路由规则所属的路由器(以及其所属的组件)。
- $route.matched
数组,包含当前匹配的路径中所包含的所有片段所对应的配置参数对象。
- $route.name
当前路径的名字,如果没有使用具名路径,则名字为空。
# $router 是“路由实例”对象
包括了路由的跳转方法,钩子函数,是 VueRouter 的一个对象,通过 Vue.use(VueRouter)和 VueRouter 构造函数得到一个 router 的实例对象
# vue-router 跳转和 location.href 有什么区别
vue-router 进行路由更新,静态跳转,页面不会重新加载;location.href 会触发浏览器,页面重新加载一次
vue-router 使用 diff 算法,实现按需加载,减少 dom 操作