Vue Router的手写实现方法实现
为什么需要前端路由
在前后端分离的现在,大部分应用的展示方式都变成了SPA(单页面应用SinglePageApplication)的模式。为什么会选择SPA呢?原因在于:
- 用户的所有操作都在同一个页面下进行,不进行页面的跳转。用户体验好。
- 对比多页面,单页面不需要多次向服务器请求加载页面(只请求一次.html文件),只需要向服务器请求数据(多亏了ajax)。因此,浏览器不需要渲染整个页面。用户体验好。
归根结底,还是因为SPA能够提供更好的用户体验。
为了更好地实现SPA,前端路由是必不可少的。假设一个场景:用户在SPA页面的某个状态下,点击了强制刷新按钮。如果没有前端路由记住当前状态,那么用户点击该按钮之后,就会返回到最开始的页面状态。这不是用户想要的。
当然,需要前端路由另一个点在于:我们可以更好地进行SPA页面的管理。通过将组件与路由发生配对关联,依据路由的层级关系,可为SPA内部的组件划分与管理提供一个依据参考。
Hash路由模式与History路由模式
这是两种常见的前端路由模式。
Hash路由模式
Hash模式使用了浏览器URL后缀中的#xxx部分来实现前端路由。默认情况下,URL后缀中的#xxxhash部分是用来做网页的锚点功能的,现在前端路由看上了这个点,并对其加以利用。
比如这个URL:http://www.abc.com/#/hello,hash的值为#/hello。
为什么会看上浏览器URL后缀中的hash部分呢?原因也简单:
- 浏览器URL后缀中的hash改变了,不会触发请求,对服务器完全没有影响。它的改变不会重新加载浏览器页面。
- 更关键的一点是,因为hash发生变化的url都会被浏览器记录下来,从而你会发现浏览器的前进后退都可以用了,页面的状态与浏览器的URL就发生了挂钩。
hash模式背后的原理是onhashchange事件,可以在window对象上监听这个事件。
History路由模式
随着HTML5中historyapi的到来,前端路由开始进化了。hashchange只能改变#后面的代码片段,historyapi(pushState、replaceState、go、back、forward)则给了前端完全的自由。简单讲,它的功能更为强大了:分为两大部分,切换和修改。
路由切换
参考MDN,切换历史状态包括back、forward、go三个方法,对应浏览器的前进,后退,跳转操作。
history.go(-2);//后退两次 history.go(2);//前进两次 history.back();//后退 hsitory.forward();//前进
路由修改
修改历史状态包括了pushState,replaceState两个方法:
/** **参数含义 **state:需要保存的数据,这个数据在触发popstate事件时,可以在event.state里获取 **title:标题,基本没用,一般传null **url:设定新的历史记录的url */ window.history.pushState(state,title,url) //假设当前的url是:https://www.abc.com/a/ //例子1 history.pushState(null,null,'./cc/')//此时的url为https://www.abc.com/a/cc/ //例子2 history.pushState(null,null,'/bb/')//此时的url为https://www.abc.com/bb/
同样的,history模式可以监听到对应的事件:
window.addEventListener("popstate",function(){ //监听浏览器前进后退事件,pushState与replaceState方法不会触发 });
History模式的注意点
和Hash模式相比,History模式存在着更多的选择。但是也有一些自身的注意点:在用户点击强制刷新的时候,History模式会向服务器发送请求。
为了解决这个问题,需要服务器做对应的处理。服务器可以针对不同的URL进行处理,当然,也可以简单处理:只要是未匹配到的URL请求,一律返回同一个index.html页面。
VueRouter做了什么?
VueRouter作为Vue生态系统中非常重要的一个成员,它实现了Vue应用的路由管理。可以说,VueRouter是专门为Vue量身定制的路由管理器,功能点非常多。它的内部实现是与Vue自身是有强耦合关系的(VueRouter内部利用了Vue的数据响应式)。
我们来看一个典型的VueRouter配置:
importVuefrom"vue"; importAppfrom"./vue/App.vue"; importVueRouterfrom'vue-router'; //以插件的形式,使用VueRouter Vue.use(VueRouter); //路由配置信息,可以从外部文件引入,在此直接写是为了方便演示 constFoo={template:'foo'} constBar={template:'bar'} constroutes=[ {path:'/',component:Foo}, {path:'/bar',component:Bar} ] //初始化并与Vue实例关联 constrouter=newVueRouter({routes}); newVue({ router, render:h=>h(App), }).$mount("#root");
可看出,VueRouter是作为插件的形式引入到Vue系统内部的。而将具体的router信息嵌入到每个Vue实例中,则是作为Vue的构造函数参数传入。
同时来看看如何使用它:
//routerExample.vueAppTest
foo bar
上面的代码中,我们可以直接使用router-link和router-view这两个组件。它们是随着VueRouter一起引入的,作为全局组件使用。
这就是一个最简单的VueRouter的使用方式。我们下面就来看看,该如何自己实现上面的简单功能,做一个自己的VueRouter。
一个简单的VueRouter实现
看了上面的这个过程,最简单的VueRouter应该包括以下实现步骤:
实现Vue规定的插件的写法,将我们自己的VueRouter作为插件引入Vue系统中。
- router功能一:解析传入的routes选项,以备调用
- router功能二:监控URL变化(两种路由方式:history、hash)
实现两个全局组件:router-link和router-view
看看自定义的VueRouter的实现:
//FVueRouter.js letVue;//保存Vue构造函数的引用,与Vue深度绑定 classFVueRouter{ constructor(options){ this.$options=options; //保存路由的路径与路由组件的对应关系 this.routerMap={}; //当前的URL必须是响应式的,使用一个新的Vue实例来实现响应式功能 this.app=newVue({ data:{current:"/"} }) } init(){ //监听路由事件 this.bindEvents(); //解析传入的routes this.createRouterMap(); //全局组件的声明 this.initComponent(); } bindEvents(){ window.addEventListener('hashchange',this.onHashChange.bind(this)); } onHashChange(){ this.app.current=window.location.hash.slice(1)||'/'; } createRouterMap(){ this.$options.routes.forEach(route=>{ this.routerMap[route.path]=route; }) } initComponent(){ //形式:转换目标=> xxx Vue.component("router-link",{ props:{ to:String, }, render(h){ //h(tag,data,children) returnh('a',{ attrs:{href:'#'+this.to} },[this.$slots.default]) }, }); //获取path对应的Component将它渲染出来 Vue.component("router-view",{ render:(h)=>{ //此处的this能够正确指向FVouter内部,是因为箭头函数 constComponent=this.routerMap[this.app.current].component; returnh(Component) } }) } } //所有的插件都需要实现install方法,传入参数是Vue的构造函数 FVueRouter.install=function(_Vue){ //将Vue的构造函数保存起来 Vue=_Vue; //实现一个混入操作的原因,插件的install阶段非常早,此时并没有Vue实例 //因此,使用mixin,延迟对应操作到Vue实例构建的过程中来执行。 Vue.mixin({ beforeCreate(){ //获取到Router的实例,并将其挂载在原型上 if(this.$options.router){ //根组件beforeCreate时只执行一次 Vue.prototype.$router=this.$options.router; this.init(); } } }) } exportdefaultFVueRouter;
这里是最为简单的一种实现。有几个值得注意的点:
- 如上代码,将最基本的一个VueRouter的代码架子搭建起来了,能够运行。但细微处依然需要酌情考虑。
- 关于插件的写法:自定义插件内部必须实现一个install方法,传入参数是Vue的构造函数。
- 使用了一个新的Vue实例,将URL的hash变量进行数据响应化处理。
- 关于渲染函数render的参数h,它实际上是createElement函数。具体用法值得深究。代码中使用的是最为简单的处理方式。
结尾
在本文中,我们讲解了前端路由常见的两种模式:Hash模式与History模式。同时,我们尝试自己实现了一个最为简单的VueRouter。更多相关的VueRouter的细节,可以参考其官网。希望本文对你有用。
到此这篇关于VueRouter的手写实现方法实现的文章就介绍到这了,更多相关VueRouter手写内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。