vue-router 中常用的 hash 和 history 路由模式实现原理吗?
Two Point!!!
-
spa页面不能刷新:
// 1.hash
// 2.history api -
url变化显示对应内容:
// 1.router-view:占位容器
// 2.数据响应式,current路由 变化触发视图更新;
响应式得两种实现方式?- 方式1:借鸡生蛋 - new Vue({data: {current: '/'}})
- 方式2:Vue.util.defineReactive(obj, 'current', '/')
hash 模式的实现原理
早期的前端路由的实现就是基于 location.hash 来实现的。其实现原理很简单,location.hash 的值就是 URL 中 # 后面的内容。比如下面这个网站,它的 location.hash 的值为 '#search':
hash 路由模式的实现主要是基于下面几个特性:
-
URL 中 hash 值只是客户端的一种状态,也就是说当向服务器端发出请求时,hash 部分不会被发送;
-
hash 值的改变,都会在浏览器的访问历史中增加一个记录。因此我们能通过浏览器的回退、前进按钮控制hash 的切换;
-
可以通过 a 标签,并设置 href 属性,当用户点击这个标签后,URL 的 hash 值会发生改变;或者使用 JavaScript 来对 loaction.hash 进行赋值,改变 URL 的 hash 值;
-
我们可以使用 hashchange 事件来监听 hash 值的变化,从而对页面进行跳转(渲染)。
依据配置的路由表进行路由匹配 ---- > routeMap获取需要更新的组件
history 模式的实现原理
HTML5 提供了 History API 来实现 URL 的变化。其中做最主要的 API 有以下两个:history.pushState() 和 history.repalceState()。这两个 API 可以在不进行刷新的情况下,操作浏览器的历史纪录。唯一不同的是,前者是新增一个历史记录,后者是直接替换当前的历史记录,如下所示:
window.history.pushState(null,null, path);
window.history.replaceState(null,null, path);
history 路由模式的实现主要基于存在下面几个特性:
-
pushState 和 repalceState 两个 API 来操作实现 URL 的变化 ;
-
使用 popstate 事件来监听 url 的变化,从而对页面进行跳转(渲染);
-
history.pushState() 或 history.replaceState() 不会触发 popstate 事件,这时我们需要手动触发页面跳转(渲染)。
注意:
- histroy模式需要后端配合配置;eg: nginx
- 本地路由需要匹配404页面,路由配置在最后 *;
hash和histrory比较
hash 模式相比于 history 模式的优点:
- 兼容性更好,可以兼容到IE8
- 无需服务端配合处理非单页的url地址
hash 模式相比于 history 模式的缺点:
- 路径多个#,比较丑
- 导致锚点功能失效
- 相同 hash 值不会触发动作将记录加入到历史栈中,而 pushState 则可以。
综上所述,当我们不需要兼容老版本IE浏览器,并且可以控制服务端覆盖所有情况的候选资源时,可以使用 history 模式了
手写实现简单vue-router
let Vue;
// 1.实现插件
class VueRouter {
constructor(options) {
this.options = options;
// 数据响应式,current必须是响应式的,这样他变化,使用它的组件就会重新render
Vue.util.defineReactive(
this,
"current",
window.location.hash.slice(1) || "/"
);
// 监控url变化
window.addEventListener("hashchange", () => {
this.current = window.location.hash.slice(1);
});
}
}
// Vue插件要实现一个install方法
VueRouter.install = function(_Vue) {
Vue = _Vue;
// 注册router实例
// 通过全局混入:Vue.mixin({beforeCreate})
Vue.mixin({
beforeCreate() {
// 仅在根组件创建时执行一次,Vue实例则有该$router
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
},
});
// 注册router-view和router-link
Vue.component("router-view", {
render(h) {
// url => component
// url
// window.location.hash
// router: this.$router
let component = null;
const { current, options } = this.$router;
// 简易的路由表匹配
const route = options.routes.find((route) => route.path === current);
if (route) {
component = route.component;
}
console.log(current, options);
return h(component);
},
});
Vue.component("router-link", {
props: {
to: {
type: String,
required: true,
},
},
render(h) {
// <router-link to="/about">xxx</router-link>
// <a href="#/about">xxx</a>
// JSX写法: return <a href={"#" + this.to}>{this.$slots.default}</a>;
return h("a", { attrs: { href: "#" + this.to } }, this.$slots.default);
},
});
};
export default VueRouter;
STRONG
- 查看vue-router源码,并自己尝试实现嵌套路由
网友评论