完整的导航解析流程
导航被触发
-
在失活组件
(from)
中调用组件内守卫beforeRouteLeave
- 离开守卫通常用来禁止用户在还未保存修改前突然离开。本次导航可以通过
next(false)
来取消
- 离开守卫通常用来禁止用户在还未保存修改前突然离开。本次导航可以通过
-
调用全局的
beforeEach
守卫- 一般被用于登录验证
若是导航至重用组件,也就是相同组件,则此时调用组件内守卫
beforeRouteUpdate(2.2+)
调用路由配置中的
beforeEnter
-
开始解析异步路由组件
- 也就是这种形式导入的组件
component: () => import("url")
- 也就是这种形式导入的组件
-
在被激活的组件中调用
beforeRouteEnter
该守卫中无法使用
this
,因为此时组件实例还未被创建,所以为undefined
但可在
next
函数的参数中传入回调函数,该回调函数参数vm
,即为实例,于导航被确认时执行若是重用组件,则只会在第一次进入重用组件时调用该守卫,重用组件自身切换不会再次调用
-
调用全局的
beforeResolve
守卫(2.5+)
- 与
router.beforeEach
类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,该守卫才被调用
- 与
导航被确认
调用全局的
afterEach
守卫触发
DOM
更新用创建好的实例调用
beforeRouteEnter
守卫中传给next
的回调函数
原理
hash版
<a href="#/dasha">dasha</a>
-
若不为
a
标签,则监控点击事件即可<span onClick="routerChange('/dasha')">dasha</span>
直接传入路径作为参数
function routerChange(pathName) { location.hash = pathName; }
赋值后自动添加#
监听
hashchange
事件window.addEventListener("hashchange", () => {})
通过
location.hash
可获取页面hash
值-
第一次进入该页面时,不会有
hashchange
,所以可以使用DOMContentLoaded
事件做初始化window.addEventListener("DOMContentLoaded", () => {})
history版
html
<span onclick="routerChange('/')">首页</span>
<span onclick="routerChange('/chloe')">chloe</span>
-
js
通过
H5
中的history.pushState
实现不刷新页面,改变url
中的路径通过
popstate
事件监听浏览器的前进回退事件通过
location.pathname
可获取当前页面路径
function routerChange(pathName) {
history.pushState(null, null, pathName);
}
window.addEventListener("popstate", () => {})
hash与history区别
-
hash
- 带有
#
,不会和服务器产生交流
- 带有
-
history
- 没有
#
,正常的url
改变,会和服务端产生交流
- 没有
实现基础Vue Router
- 便于查看 所以未模块化
当前路由的信息
class History {
constructor() {
this.current = {
path: ""
};
}
}
router实例构造函数
class VueRouter {
constructor(options) {
this.routesMap = this.createRouteMap(options.routes || []);
history即为$route
this.history = new History();
拿到模式
this.mode = options.mode || "hash";
初始化route信息
this.init();
}
处理路由组件信息 以路径为key component为值
createRouteMap(routes) {
const routesMap = {};
for (let i = 0; i < routes.length; i++) {
routesMap[routes[i].path] = routes[i].component;
}
return routesMap;
}
初始化route信息
init() {
if (this.mode === "hash") {
location.hash || (location.hash = "/");
document.addEventListener("DOMContentLoaded", () => {
this.history.current.path = location.hash.slice(1);
});
window.addEventListener("hashchange", () => {
this.history.current.path = location.hash.slice(1);
});
} else {
document.addEventListener("DOMContentLoaded", () => {
this.history.current.path = location.pathname;
});
window.addEventListener("popstate", () => {
this.history.current.path = location.pathname;
});
}
}
}
Vue.use()会默认调用install方法
VueRouter.install = (Vue) => {
this.$options.router即为new VueRouter创建的实例
给所有组件都混入beforeCreate钩子 该组件的$options上若有router
则说明该组件为根实例 因为router只在new Vue()上设置了
Vue.mixin({
给根实例添加_router和_route属性
beforeCreate() {
if (this.$options.router) {
给_router赋值为该实例
this._router = this.$options.router;
给_route赋值 使用Vue的响应式方法 在_route改变时 重新渲染页面
Vue.util.defineReactive(this, "_route", this._router.history.current);
}
}
});
通过Object.defineProperty而不是Vue.prototype.$router给$router赋值
可实现在get方法中拿到调用$router的组件实例,this.$router
$router即为new VueRouter所创建的实例
Object.defineProperty(Vue.prototype, "$router", {
get() {
返回根实例上的router实例
return this.$root._router;
}
});
与$router同理
Object.defineProperty(Vue.prototype, "$route", {
get() {
返回根实例上的router上的history
return this.$root._route;
}
});
router-link组件
Vue.component("router-link", {
props: {
to: {
type: String,
require: true
},
tag: {
type: String,
default: "a"
}
},
methods: {
手动改变hash值
handleClick() {
const mode = this.$router.mode;
if (mode === "hash") {
location.hash = this.to;
} else {
history.pushState(null, null, this.to);
this.$router.history.current.path = this.to;
}
}
},
render(h) {
const mode = this.$router.mode;
const data = {};
if (this.tag === "a" && mode === "hash") {
data.attrs = { href: "#" + this.to };
} else {
data.on = {
若不是a标签 则触发点击事件
click: this.handleClick
};
}
通过h而不是JSX来创建 可渲染不同的tag
return h(this.tag, data, this.$slots.default);
}
});
router-view组件
Vue.component("router-view", {
functional: true,
render(h, { parent }) {
拿到routesMap
const routesMap = parent.$router.routesMap;
const path = parent.$route.path;
return h(routesMap[path]);
}
});
};
export default VueRouter;