前言为什么要阅读源码?1.更好的使用api,解决工作中遇到的问题2.避免一些使用中的常见错误调用步骤1.安装路由器插件importrouterformvue-routerVue.use(router)2.实例化routerconstHome={template:'
home
'}letrouter=newRouter({routes:[{path:'/home',component:Home}]});3.mountrouternewVue({router,render(h){h(App)}})安装路由器插件Vue.use是Vue提供的插件安装机制。如果插件是对象,则必须提供安装方法。Vue.use会调用插件的install方法。router的install方法主要是对vue实例添加一些生命周期处理,在vue构造函数的原型中添加$router和$route的属性,也就是我们调用route时使用的this.$router和this.$.响应式定义_route,这样当路由对象发生变化时,依赖的视图文件也会更新。下面是vue-router的install的实现importViewfrom'./components/view'importLinkfrom'./components/link'exportlet_Vueexportfunctioninstall(Vue){//防止重复安装插件if(install.安装&&_Vue===Vue)returninstall.installed=true_Vue=VueconstisDef=v=>v!==undefinedconstregisterInstance=(vm,callVal)=>{leti=vm.$options._parentVnodeif(isDef(i)&&isDef(i=i.data)&&isDef(i=i.registerRouteInstance)){i(vm,callVal)}}//通过全局mixin注入一些生命周期处理Vue.mixin({beforeCreate(){if(isDef(this.$options.router)){this._routerRoot=this//定义私有属性_routerRoot,指向Vue实例,用于$router属性和$route属性从this读取值._router=this.$options.router//定义私有属性_router,指向VueRouter实例this._router.init(this)//初始化vue-routerVue.util.defineReactive(this,'_route',this._router.history.current)//定义私有属性_route,设置为responsive}else{this._routerRoot=(this.$parent&&这个.$parent._routerRoot)||this}registerInstance(this,this)//registerinstance},destroyed(){registerInstance(this)//destroyinstance}})//在Vue的原型上初始化$router和$路由的getter,设为只读,且无法更改。指向当前的Router实例和当前的Route信息,{get(){returnthis._routerRoot._route}})//全局定义RouterView和RouterLink组件Vue.component('RouterView',View)Vue.component('RouterLink',Link)}实例化router主要有两个实例化vue-router类过程中的东西,根据mode添加matcher属性和添加history属性。调用createMatcher方法返回一个Matcher对象,该对象包含两个方法:match和addRoutes。这两个方法主要被Vue-router上的原型方法match和addRoutes使用。match的主要作用是根据参数location获取目标路由对象。该方法主要是在每次路由切换时被transitionTo方法调用。addRoutes的作用是动态添加路由配置。在实际开发场景中,可能需要根据不同的人员权限输出不同的界面。添加history属性,根据mode参数选择实例化的类。该模式具有三个参数。浏览器环境下有两种方式,分别在HTML5History和HashHistory类中实现。它们都继承自History类。这是整个路由的核心部分,路由切换的核心逻辑处理都在这部分。exportdefaultclassVueRouter{constructor(options:RouterOptions={}){this.app=nullthis.apps=[]this.options=optionsthis.beforeHooks=[]this.resolveHooks=[]this.afterHooks=[]//创建matcher,一个包含addRoutes和匹配方法的对象this.matcher=createMatcher(options.routes||[],this)letmode=options.mode||'hash'this.fallback=mode==='history'&&!supportsPushState&&options.fallback!==falseif(this.fallback){mode='hash'}if(!inBrowser){mode='abstract'}this.mode=模式切换(模式){case'history':this.history=newHTML5History(this,options.base)breakcase'hash':this.history=newHashHistory(this,options.base,this.fallback)breakcase'abstract':this.history=newAbstractHistory(this,options.base)breakdefault:if(process.env.NODE_ENV!=='production'){assert(false,`invalidmode:${mode}`)}}}实例化vuenewVue({router,render(h){h(App)}})在options中传递router和renderingmethod,此时创建一??个Vue例如,会调用vueRouter的install方法中对应的beforeCreatehook,根据当前url(history.getCurrentLocation())完成第一次路由导航。这就是为什么我们开发完服务后,在hash模式中输入http://localhost:8080,url会变成http://localhost:8080/#/。另外监控history对象,vue实例的_route属性发生变化也会更新。init(app:any/*Vue组件实例*/){consthistory=this.historyif(historyinstanceofHTML5History){history.transitionTo(history.getCurrentLocation())}elseif(historyinstanceofHashHistory){constsetupHashListener=()=>{history.setupListeners()}history.transitionTo(history.getCurrentLocation(),setupHashListener,setupHashListener)}history.listen(route=>{this.apps.forEach((app)=>{app._route=route})})}通过代码我们可以看到VueRouter中使用了transitionTo方法来实现路由切换。这是History类的一个方法。总结本文根据调用vue的步骤大致描述了vue-router源码的主要流程,后面会展开一些细节的处理和实现。正常调用后,我们使用中的主要逻辑流程如下