router源码解读阅读请关注代码注释打个广告:谁能教教我怎么在sf中排版,我不会做菜单二级导航(plop.gif)
1.什么是router
首先会从源码中引入Router,然后传入参数实例化一个路由对象//router/index.jsimportRouterfrom'vue-router'newRouter({...})...Sourcebaseclass://Sourceindex.jsexportdefaultclassVueRouter{...constructor(options:RouterOptions={}){this.app=nullthis.apps=[]this.options=选项这个。beforeHooks=[]this.resolveHooks=[]this.afterHooks=[]this.matcher=createMatcher(options.routes||[],this)letmode=options.mode||'hash'//如果未选择任何模式,则默认使用哈希模式(!inBrowser){//非浏览器环境默认nodejs环境mode='abstract'}this.mode=modeswitch(mode){//根据参数选择三种模式之一case'history':this.history=newHTML5History(this,options.base)//根据HTML5版本历史的方法Modebreakcase'hash':this.history=newHashHistory(this,options.base,this.fallback)//利用url中的hash特性实现breakcase'abstract':this.history=newAbstractHistory(this,options.base)//这个模式原理不清楚breakdefault:if(process.env.NODE_ENV!=='production'){assert(false,`invalidmode:${mode}`)}}}...//一些api方法,你应该很熟悉,$router.push(...)push(location:RawLocation,onComplete?:Function,onAbort?:Function){this.history.push(location,onComplete,onAbort)}replace(location:RawLocation,onComplete?:Function,onAbort?:Function){这个。历史。replace(location,onComplete,onAbort)}go(n:number){这个。历史。go(n)}back(){this.go(-1)}forward(){this.go(1)}...}我们创建的路由是VueRouter类的实例,用于管理我们的[key-components-view],一个key(代码中的path)对应一个component,view就是
在模板中占一个空位,根据key显示对应的component。instance上的func可以让我们控制路由,也就是官网的api简单点说,路由就是一个“轮播图”,emmmmmmm,好像说轮播也不过分,写一个循环key是“轮播”的func,key就是轮播的索引,手动和有趣那么,vue-router是如何在不发送请求的情况下更新视图的呢?下面看看vue是如何使用route的实例化路由输出的:区分route和router2。router的工作原理如果要使用router,需要添加router//main.jsimprotxxxfromxxximportrouterfromxxxnewVue({el:'#app',router:router,components:{App},template:''})那么,Vue是如何使用这个参数的呢?vue-router作为插件加入,通过mixin(混合)影响每个Vue实例化。使用beforeCreatehook时会完成路由器的初始化,从参数获取路由器->调用init初始化->添加responsive(vue源码中使用较多的是defineReactive方法,也是核心方法在底层实现响应)//源代码install.jsVue.mixin({beforeCreate(){if(isDef(this.$options.router)){this._routerRoot=thisthis._router=this.$options.router//获取options中的router配置this._router.init(this)//初始化,这个init在VueRouter类的方法中,实例化后会继承这个方法,方法代码如下所示Vue.util.defineReactive(this,'_route',this._router.history.current)//这个是把_route加入到数据监控中,所以可以看_route}else{this._routerRoot=(this.$parent&&this.$parent._routerRoot)||这个}注册erInstance(this,this)},destroyed(){registerInstance(this)}})初始化会做什么:-判断主程序的状态(初始化后的vue实例不会重新初始化路由,也就是你不能手动再去init)-将实例添加到内置数组-判断history的类型,做一些类似优化的事情,比如hash模式的setupListeners方法,就是延迟监听hashChange事件,等到vue挂载后再listen,太细了无法深入-listen定义一个回调,listen定义在最底层的History类上,作用是定义一个回调,需要的时候会调用listen,这个回调路由改变时会执行//源码index.jsexportdefaultclassVueRouter{...init(app:any/*Vue组件实例*/){process.env.NODE_ENV!=='production'&&assert(install.installed,`notinstalled.确保在创建ro之前调用\`Vue.use(VueRouter)\``+`otinstance.`)this.apps.push(app)//这个apps存储了所有的Vue实例化(根组件),后面遍历的时候,会把当前的标签路由挂到所有根组件上,也就是vm._route是还有vm._router.history.current//主应用程序已经初始化。if(this.app){return}this.app=appconsthistory=this.historyif(historyinstanceofHTML5History){历史。过渡到(历史。getCurrentLocation())}elseif(historyinstanceofHashHistory){constsetupHashListener=()=>{history.setupListeners()}history.transitionTo(history.getCurrentLocation(),setupHashListener,setupHashListener)}history.listen(route=>{//注意后面会用到这个listenthis.apps.forEach((app)=>{app._route=route//所有根组件获取当前路由})})}...}的变化过程route会在后面的具体模式中进行讲解,这里先跳过,再说vue获取router后如何使用router渲染组件3.安装vue-router插件时如何使用routerforvueexportfunctioninstall(Vue){...Vue.component('RouterView',View)//&你应该很熟悉了。它本质上是一个vue组件。在看源码之前,我的猜测也是一个组件Vue.component('RouterLink',Link)...}你可能不会用到router-link,但你肯定会用到router-view,它以'window'的形式存在'渲染你需要显示的组件那么,从这个组件开始,一个前提是:vnode是通过render创建的,也就是说改变_route的值会执行render函数,Router-View组件自己定义render,省略大部分代码,这两行就够了,这就是你最终通过看到的view的样子_renderProxy,vm.$createElement)...}//路由源view.jsrender(_,{props,children,parent,data}){...consth=parent.$createElement...returnh(component,data,children)}first:hashHistory模式流程$router.push()-->HashHistory.push()-->History.transitionTo()-->History.updateRoute()-->{app._route=route}-->vm.render()1.关于hashurl中#后面的参数,别名hash值,hash的一些特性1.改变hash不会导致页面重新加载2.HTTP请求不会include#,所以使用hash不会影响其他功能3.改变#会改变浏览器的访问历史4.window.location.hash可以读取hash值5.JavaScript可以通过onhashchange监听hash值的变化,就是它意味着你可以知道用户在浏览器中手动更改了哈希值。因为有这些特点,才有了hashHistory。关于hash的详细介绍见网址中的数字符号——阮一峰的博客2.hashHistory源码首先,这三种模式是继承一个exportclassHashHistoryextendsHistory{...}fromabaseclassHistory,这三种模式必须属性相同,方法相同,肯定不会创建三次,所以继承一个基类,然后每个的一些属性或方法会有所不同。至于历史课,我就不细看了。反正我是看不懂。哈哈哈哈路由器上的实例属性和方法可以在VueRouter中找到,HashHistory/HTML5History/AbstractHistory,在History上找到,这里是HashHistory的几个func的实现,//路由器源码hash.jsexportclassHTML5HistoryextendsHistory{...go(n:number){window.history.go(n)}push(location:RawLocation,onComplete?:Function,onAbort?:Function){const{current:fromRoute}=thisthis.transitionTo(location,route=>{//历史类上的funcpushHash(route.fullPath)handleScroll(this.router,route,fromRoute,false)onComplete&&onComplete(route)},onAbort)}functionpushHash(path){if(supportsPushState){//浏览器环境是否支持pushstat方法,这个func会说pushState(getUrl(path))//如果支持,则在window.history中添加一条数据}else{window.location.hash=path//如果不支持,直接修改location的hash}}replace(location:RawLocation,onComplete?:Function,onAbort?:Function){const{current:fromRoute}=thisthis.过渡到(位置,路线=>{replaceHash(route.fullPath)handleScroll(this.router,route,fromRoute,false)onComplete&&onComplete(route)},onAbort)}//replace和push其实只有两个区别1.window.location.hash=pathwindow.location.replace(getUrl(path))2.if(replace){//replace调用这个func并将传递一个真实的history.replaceState({key:_key},'',url)}else{_key=genKey()history.pushState({key:_key},'',url)}...}还有一点就是在初始化hash方式路由的时候,会执行一个func来监听hashchange事件setupListeners(){window.addEventListener(supportsPushState?'popstate':'hashchange',()=>{constcurrent=this.currentif(!ensureSlash()){return}this.transitionTo(getHash(),route=>{if(supportsScroll){handleScroll(this.router,route,current,true)}if(!supportsPushState){replaceHash(route.fullPath)}})})}第二种:HTML5History模式HTML5--History科普主要是两个新的api1.历史。pushState()【优点写的清楚】HTML5History的push和replace和hash方式类似,所以代码就不用加了。标记是否支持HTML5的flag是这样写的。如果有必要,你可以把它挖回来并使用exportconstsupportsPushState=inBrowser&&(function(){constua=window.navigator.userAgentif((ua.indexOf('Android2.')!==-1||ua.indexOf('Android4.0')!==-1)&&ua.indexOf('移动版Safari')!==-1&&ua.indexOf('Chrome')===-1&&ua.indexOf('Windowsphone')===-1){returnfalse}returnwindow.history&&'pushState'inwindow.history})()还有一个是scrollBehavior,用来记录路由跳转时滚动条的位置。这个只能在HTML5模式下使用,即支持pushState方法时,有博客说只能在HTML5History下使用。这个明天验证的时候,个人觉得支持HTML5就够了。2、history.replaceState()也很直观,就是不新建一条记录而是覆盖一条记录。谁,我在哪里,谁打我)我有一个好包,我就知道我不会做前端~在学习router源码的时候,看了entropy和monads的codebook那篇文章。看完这篇文章,基本可以配合源码了。很容易掌握vue-router的大致思路。感谢作者。另外说明一下,这篇文章是我经过学习和自己的理解后,一个字一个字打出来的。可能有一些相似之处。侵删请联系本人。写文章的目的就是看看大家能不能表达清楚,掌握知识点,有什么不对的地方请大家指正~~谢谢潘同谢的指导(^▽^)当然我也很关心你的小??点个赞走~以上图片均来自MDN网页截图,vue官网截图,百度首页截图,没有版权问题/搞笑【注】:如有不当请指出或错误的内容正能量~转载请注明出处~谢谢合作!