前端路由实现起来其实很简单,本质就是监听URL的变化,然后匹配路由规则,显示相应的页面,并且无须刷新。vue-router共有三种守卫,分别是全局守卫、路由独享守卫、组件内的守卫。
导航守卫
- 全局守卫
你可以使用 router.beforeEach 注册一个全局前置守卫: ep:设置全局守卫,如果进入about页,没有登陆则重定向到login页
router.beforeEach((to,from,next) => {
console.log(to.meta)
// 判断是否登录
if (to.path === '/about' && !window.isLogin) {
next('/login?redirect='+to.path);
} else {
next();
}
})
复制代码
- 路由独享守卫
{
path: "/about",
name: "about",
meta: {requireLogin:true}, // meta 取值就是to.meta
beforeEnter(to,from,next) {
// 判断是否登录 路由独享守卫
if (!store.state.isLogin) {
next('/login?redirect='+to.path);
} else {
next();
}
},
component: () =>
import(/* webpackChunkName: "about" */ "./views/About.vue")
}
复制代码
效果等同于全局守卫
- 组件内的守卫
export default {
beforeRouteEnter(to, from, next) {}
beforeRouteUpdata(to, from, next) {}
beforeRouteLeave(to, from, next) {}
}
复制代码
完整的导航解析流程
- 导航被触发。
- 在失活的组件里调用离开守卫。
- 调用全局的 beforeEach 守卫。
- 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
- 在路由配置里调用 beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用 beforeRouteEnter。
- 调用全局的 beforeResolve 守卫 (2.5+)。
- 导航被确认。
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新。
- 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。
meta路由元信息
ep:通过route设置document.title
{
path: '/customer/new',
name: 'NewCustomer',
meta: {title: '新增客户'},
component: () => import('@/Views/CustomerNew')
},
router.beforeEach(function (to, from, next) {
if (to.meta && to.meta.title) {
document.title = ((to.meta && to.meta.title))
}
next()
})
复制代码
hash模式&&history模式
www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,不会向服务器请求数据,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面。
History 模式是 HTML5 新推出的功能,比之 Hash URL 更加美观源码实现
应用页面
<router-link to="#/foo">foo</router-link> |
<router-link to="#/bar">bar</router-link>
复制代码
router配置
const Foo = {
render() {
return <div>Foo</div>;
}
};
const Bar = {
render() {
return <div>Bar</div>;
}
};
export default new TRouter(Vue, {
routes: [
{
path: "/foo",
component: Foo
},
{
path: "/bar",
component: Bar
}
]
});
复制代码
源码实现
import Vue from "vue";
class TRouter {
constructor(Vue, options) {
this.$options = options;
this.routeMap = {}; // 全局的路由映射 key就是path(唯一的)
this.app = new Vue({ // 对vue的强依赖
data: {
current: "#/" // 当前路由
}
});
this.init();
this.createRouteMap(this.$options);
this.initComponent(Vue);
}
// 初始化 hashchange
init() {
// load事件在页面或某个资源加载成功时触发
window.addEventListener("load", this.onHashChange.bind(this), false);
// hashchange事件在 URL 的 hash 部分(即#号后面的部分,包括#号)发生变化时触发。
window.addEventListener("hashchange", this.onHashChange.bind(this), false);
}
createRouteMap(options) {
options.routes.forEach(item => {
this.routeMap[item.path] = item.component;
});
}
// 注册组件
initComponent(Vue) {
Vue.component("router-link", { // 全局注册
props: {
to: String // 去哪个地址
},
render: function (h) { // 传入函数 h 就是createElement
// <a :href="to"><slot></slot></a>
// 返回的是虚拟dom
return h("a", { attrs: { href: this.to } }, this.$slots.default);
}
});
const _this = this; // 全局this
Vue.component("router-view", { // 占位符
render(h) { // 根据当前路由发生变化 渲染当前组件所匹配的组件
var component = _this.routeMap[_this.app.current];
return h(component);
}
});
}
// 获取当前 hash 串
getHash() {
return window.location.hash.slice(1) || "/";
}
// 设置当前路径
onHashChange() { // 挂载的组件刷新
this.app.current = this.getHash(); // 获取最新hish赋值
console.log(this.app.current, 'this.app.current')
}
}
复制代码
总结
vue-router对vue也是强依赖关系,简单的也可以理解为是对history的封装,本质上还是实现根据路由不同,返回不同内容。