前言【vue-router源码】系列文章带你从0开始了解vue-router的具体实现。本系列文章源码参考vue-routerv4.0.15。源码地址:https://github.com/vuejs/router阅读本文的前提是你最好了解vue-router的基本使用。如果没有使用过,可以通过vue-router官网进行学习。本文将分析useRoute、useRouter、useLink的实现。use````使用`useLink`自定义我们自己的`RouterLink`,比如下面自定义的`MyRouterLink`,如果是外部链接,我们需要让它打开一个新的页面。<模板>scriptlang="ts"setup>import{useLink,useRoute,RouterLink}from'vue-router'import{computed}from'vue'constprops=defineProps({//@ts-ignore...RouterLink.props})const{route,href,navigate,isActive,isExactActive}=useLink(props)constisExternalLink=computed(()=>typeofprops.to==='string'&&props.to.startsWith('http'))constcurrentRoute复制代码=useRoute()constclasses=computed(()=>({'router-link-active':isActive.value||currentRoute.path.startsWith(route.value.path),'router-link-exact-active':是精确的。价值||currentRoute.path===route.value.path,}))`MyRouterLink`使用:MyRouterLinkExternalLinkMyRouterLink/home#useRouter、useRouteexportfunctionuseRouter():Router{returninject(routerKey)!}exportfunctionuseRoute():RouteLocationNormalizedLoaded{returninject(routeLocationKey)!}useRouter和useRoute都是通过inject获取对应的值。在`install`过程中注入相应的值。install(app){//...app.provide(routerKey,router)app.provide(routeLocationKey,reactive(reactiveRoute))//...}#useLinkexportfunctionuseLink(props:UseLinkOptions){//路由器实例const路由器=注入(路由器密钥)!//当前路由地址constcurrentRoute=inject(routeLocationKey)!//目的路由相关信息constroute=computed(()=>router.resolve(unref(props.to)))//获取活动记录的索引constactiveRecordIndex=computed(()=>{const{matched}=route.valueconst{length}=matched//目标路由匹配的完整路由constrouteMatched:RouteRecord|undefined=matched[length-1]constcurrentMatched=currentRoute.matched//如果没有匹配到的目标路由或者当前路由没有匹配到的路由return-1if(!routeMatched||!currentMatched.length)return-1//被当前路由匹配找到目标路由intherouteconstindex=currentMatched.findIndex(isSameRouteRecord.bind(null,routeMatched))if(index>-1)returnindex//目标路由匹配到的路由的父路由的路径(如果父路由是通过别名生成,走源路由的路径)constparentRecordPath=getOriginalPath(匹配[长度-2]作为RouteRecord|undefined)return(length>1&&//如果目标路由的父路由与getOriginalPath(routeMatched)相同===parentRecordPath&&//避免将子路由与父路由进行比较currentMatched[currentMatched.length-1].path!==parentRecordPath?currentMatched.findIndex(isSameRouteRecord.bind(null,matched[length-2])):index)})//当前router-link是否活跃,activeRecordIndex大于-1以及当前路由的params和params目标路由的相同constisActive=computed(()=>activeRecordIndex.value>-1&&includesParams(currentRoute.params,route.value.params))//是否精确匹配,目标路由必须匹配当前路由lastsameconstisExactActive=computed(()=>activeRecordIndex.value>-1&&activeRecordIndex.value===currentRoute.matched.length-1&&isSameRouteLocationParams(currentRoute.params,route.value.params))//路由跳转功能naviga使用push或replacete(e:MouseEvent={}asMouseEvent):Promise{//对于一些特殊情况如果(guardEvent(e)){返回路由器[unref(props.replace)?'replace':'push'](unref(props.to)).catch(noop)}returnPromise.resolve()}//devtoolsonlyif((__DEV__||__FEATURE_PROD_DEVTOOLS__)&&isBrowser){//...}return{route,href:computed(()=>route.value.href),isActive,isExactActive,navigate,}}在进行路由跳转时,有些特殊情况不能跳转,这些情况包括:1.按住窗口`?`(MAC的`commond`)键、`alt`键、`ctrl`键、任何`shift`键2.调用了`e.preventDefault()`3.右键单击??4.`target='_blank'`functionguardEvent(e:MouseEvent){//不要使用控制键重定向if(e.metaKey||e.altKey||e.ctrlKey||e.shiftKey)return//当调用preventDefault时不要重定向if(e.defaultPrevented)return//右键点击不重定向if(e.button!==undefined&&e.button!==0)return//不重定向iftarget="_blank"//@ts-expect-errorgetAttribute确实存在if(e.currentTarget&&e.currentTarget.getAttribute){//@ts-expect-errorgetAttributeexistsconsttarget=e.currentTarget.getAttribute('target')if(/\b_blank\b/i.test(target))return}//这可能是一个没有这个方法的Weex事件if(e.preventDefault)e.preventDefault()返回真值}