当前位置: 首页 > Web前端 > vue.js

提供了可自定义的路由加载方式,Vue是怎么做到的呢?-小智内部团队分享

时间:2023-04-01 00:37:53 vue.js

有梦想,有干货,微信搜索【大千世界】关注这位凌晨还在洗碗的洗碗智慧。本文已收录到GitHubhttps://github.com/qq449245884/xiaozhi,里面有完整的测试站点、资料和我的一线厂商访谈系列文章。背景在开始之前先介绍一下我们目前新项目使用的技术栈的前端公共库:vue3+typescript+jsx+antdVue后台项目:vue3+typescript+jsx+antdVue是的,我们现在使用的是ts+jsx语法来开发一个新的项目,这里可能有朋友会说,你不要模板吗,安装什么。这里有很多值得讨论的地方,下次有机会再分享。我今天不讨论这个问题。回到正文~~这个月老大在技术优化(前端公库)方面给我发了几个任务,其中一个就是“路由注册改造,在组件中使用异步加载”,大家肯定会想,就是这样?,这个不是配合router.beforeEach和router.afterEach加个库NProgress来显示进度条,就完了。没错,传统的方式存在一些问题,后面会讲到,这里我们先看看传统的方式是怎么做的。传统的方法大家应该都用过,就是在切换路由的时候,在最上面显示一个加载进度条。我们在这里使用的库是NProgress。第一步安装插件:yarnaddnprogress第二步将插件引入main.ts。importNProgressfrom'nprogress'import'nprogress/nprogress.css'第三步监听路由跳转,进入页面执行插件动画。Router.beforeEach((to,from,next)=>{//打开进度条NProgress.start()next()})跳转结束router.afterEach(()=>{//关闭进度条NProgress.done()})是一个非常简单的配置。运行之后,当我们切换路由的时候,会在最上面看到一个进度条:这个模式有两个问题(目前能想到的):弱网情况,页面会卡在那里,移动很慢。断网时,进度条会一直处于loading状态,没有及时反馈加载失败。有特殊需求的时候,比如加载菜单二的时候,我想用骨架屏加载菜单三的时候,我想用传统的菊花样式加载。在这种情况下,我们目前的解决方案是很难做到的。弱网我们来模拟一个弱网,打开浏览器控制台,切换到NetWork,把网络改成Slow3G,然后切换路由。下面是我实际操作的效果:可以看到当我们切换到菜单2时,进度条会走得很慢,页面没有及时切换到菜单2的界面。页面内容越多,效果会越明显。网络断开。我们再模拟一下断网的情况。切换到NetWork,将网络切换到Offline,再切换路由。下面是我实际操作的效果:你会看到在没有网络的情况下,进度情况还在,一直转,一直加载,没有及时反馈,体验也很差。我们想要什么效果?我们团队想要的效果是只要点击菜单,页面就会切换。即使在网络弱的情况下,加载失败也应该给出失败反馈,而不是每次都让用户在那里等待支持。求一个路由跳转时独特的加载效果的解决方案。为了解决以上问题,我们需要一个可以异步加载和自定义加载的方法。查阅了官方文档,在Vue2.3中新增了一个异步组件,允许我们自定义加载方式,用法如下:constAsyncComponent=()=>({//需要加载的组件(应该是一个`Promise`对象)component:import('./MyComponent.vue'),//异步组件加载时使用的组件loading:LoadingComponent,//加载失败时使用的组件error:ErrorComponent,//显示组件加载时的延迟时间。默认值为200(毫秒)delay:200,//如果提供超时并且组件加载也超时,//使用加载失败时使用的组件。默认值为:`Infinity`timeout:3000})注意,如果要在VueRouter的路由组件中使用以上语法,必须使用VueRouter2.4.0+版本。但是我们现在是用Vue3开发,所以得看看Vue3中有没有类似的方法。查阅了官方文档,也找到了一个方法defineAsyncComponent,大致使用如下:...,//如果设置了timeout,加载组件的时间超过了设置的值,就会显示错误的组件//默认值:Infinity(即永不超时,单位ms)timeout:3000,//定义是否componentcanbesuspended|defaultValue:truesustainable:false,/****@param{*}error错误信息对象*@param{*}retry一个函数,用来表示当promise加载器拒绝时加载器是否应该重试*@param{*}fail指示的函数测试加载程序完成退出*@param{*}尝试允许的最大重试次数*/onError(error,retry,fail,attempts){if(error.message.match(/fetch/)&&attempts<=3){//请求出错时重试,最多尝试3次retry()}else{//注意retry/fail就像promise的resolve/reject://必须调用其中一个才能继续错误处理。fail()}}})但是在官方的V3迁移指南中,官方指出了如下一段话:VueRouter支持类似的机制来异步加载路由组件,也就是俗称的懒加载。虽然类似,但这个功能与Vue支持的异步组件不同。使用VueRouter配置路由组件时,不应使用defineAsyncComponent。您可以在VueRouter文档的惰性路由章节中阅读更多相关信息。官网说了defineAsyncComponent不应该用于路由的懒加载,但是并没有说不能用,而我们现在需要这个方法,所以还是选择使用(遇到了会分享以后坑)。思路有了上面的方法,我们目前的思路是重写Vue3中的createRouter方法。在createRouter中,我们递归遍历传入的路由,判断当前组件是否为异步加载组件。如果是这样,我们使用defineAsyncComponent方法将其包装起来。下面是我现在封装的代码import{RouteRecordMenu}from'@/components/AdminLayout';importPageLoadingfrom'@/components/AdminLayout/components/PageLoading';importPageResultfrom'@/components/AdminLayout/components/PageResult';import{AsyncComponentLoader,AsyncComponentOptions,defineAsyncComponent,h,}from'vue';import{createRouterasvueCreateRouter,RouterOptions}from'vue-router';/****@paramrouterOptionsvuecreateRouter的参数*@paramasyncComponentOptions异常组件配置参数*@returns*/exportdefaultfunctioncreateRouter(routerOptions:RouterOptions,{loadingComponent=PageLoading,errorComponent=PageResult,delay=200,timeout=3000,suspensible=false,onError,}:Omit={},){consttreedRoutes=(childrenRoutes:RouteRecordMenu[])=>{returnchildrenRoutes.map((childrenRoute:RouteRecordMenu)=>{if(childrenRoute.children){childrenRoute.children=treedRoutes(childrenRoute.children);}else{if(typeofchildrenRoute.component==='function'){childrenRoute.component=defineAsyncComponent({loader:childrenRoute.componentasAsyncComponentLoader,loadingComponent,errorComponent,delay,timeoutonError,suspendable,,});}}返回childrenRoute;});};treedRoutes(routerOptions.routes);returnvueCreateRouter(routerOptions);}上面重写了createRouter方法,提供了可选的配置参数routerOptions,routerOptions中的字段其实就是defineAsyncComponent中的参数,除了loder有当前的createRouter,我们来看同一个场景不同的效果.对于弱网,我们可以看到第二种方案是在弱网的情况下,只要我们切换路由,页面就会立刻切换,过渡方式也是我们指定的。与第一种方案不同的是,页面会停在点击前的页面,然后点击一下就滑过去。切换到菜单时,因为我这里指定的超时时间是3秒,如果3秒内没有加载,就会显示我们指定的errorComponent。现在,打开浏览器,切换到NetWork,把网络改成Offline,也就是网络断开的时候,我们看看效果。当网络断开时,我们可以看到,当我们的网络断开时,当我们切换页面时,会显示我们指定的errorComponent,不像第一种方法,会一直卡在页面加载中。TransformLoading看下面,我的示例路由:}from'@ztjy/antd-vue-admin'importcreateRouterfrom'./createRoute'exportconstroutes:RouteRecordMenu[]=[{path:'/menu',name:'Menu',component:RouterView,redirect:'/menu/list',meta:{icon:'fasfa-ad',title:'MenuOne',},children:[{path:'/menu/list',component:()=>import('@/pages/Menu1'),meta:{title:'List',},},]],},{path:'/menu2',name:'Menu2',component:RouterView,redirect:'/menu2/list',meta:{icon:'fasfa-ad',title:'Menu2',},children:[{path:'/menu2/list',component:()=>import('@/pages/Menu2'),meta:{title:'List',},},],},{路径:'/menu3',名称:'Menu3',组件:RouterView,重定向:'/menu3/list',元:{图标:'fasfa-ad',标题:'Menu3',},孩子:[{路径:'/menu3/list',组件:()=>import('@/pages/Menu3'),meta:{title:'list',},},],},]constrouter=createRouter({history:createWebHistory('/'),routes:[{path:'/login',component:Login,props:{title:'商业前端后台登录',},},{path:'/',redirect:'/menu',component:AdminLayout,props:{title:'商业前端后台模板',routes,},meta:{title:'Home',},children:routesasRouteRecordRaw[],},],})exportdefaultrouter我们现在想把菊花样式替换成下面封装好的冒泡加载方式:很简单,我们只需要将对应的加载组件(BubbleLoading)的名字传给createRouter,为了演示效果,我们把网络切到Slow3G,代码如下:router.ts/***这里省略很多字**/constrouter=createRouter({history:createWebHistory('/'),路线:[/***这里省略了很多词**/]},{loadingComponent:BubbleLoading,//看这里和这里})exportdefaultrouter如果我们只需要点击菜单2就可以使用BubbleLoading,然后点击别人使用chrysanthemumLoading,那怎么办呢?这里,仔细看上面二次封装的createRouter方法,你可能就知道怎么做了。里面有一个判断是typeofchildrenRoute.component==='function'其实我做的是判断从外面传入的路由是否使用我只是用defineAsyncComponent重写了异步加载方法。我不关心其他加载方法。因此,如果我们想要自定义自己的加载方法,只需要用defineAsyncComponent重写即可回到我们的router.ts代码,//这里省略了一些代码exportconstroutes:RouteRecordMenu[]=[//这里省略了一些代码{path:'/menu2',name:'Menu2',component:RouterView,redirect:'/menu2/list',meta:{icon:'fasfa-ad',title:'Menu2',},children:[{path:'/menu2/list',component:defineAsyncComponent({//看这里loader:()=>import('@/pages/Menu2'),//看这里loadingComponent:BubbleLoading,//看这里}),meta:{title:'List',},},],},//这里省略部分代码]//这里省略部分代码,我们使用defineAsyncComponent来定义菜单2的组件加载方法,运行效果如下:从图中我们可以看到,当点击菜单1和菜单3时,我们使用的是菊花加载方式,点击Menu2会显示我们自定义的加载方式。注意这里有个明显的bug,就是下面的代码:component:defineAsyncComponent({loader:()=>import('@/pages/Menu2'),loadingComponent:BubbleLoading,}),不能写成form一个函数的,如下图:component:()=>defineAsyncComponent({loader:()=>import('@/pages/Menu2'),loadingComponent:BubbleLoading,}),这里因为我使用了typeofchildrenRoute.component=在createRouter方法中=='function'来判断,所以上面的代码会再次被defineAsyncComponent包裹,变成一个二层的defineAsyncComponent,所以页面会加载错误。我也想解决这个问题,但是查了很多资料,都没有找到方法中defineAsyncComponent方法的使用方法,也就是下面的形式:component:()=>defineAsyncComponent({loader:()=>import('@/pages/Menu2'),loadingComponent:BubbleLoading,}),有知道的请私信告知。本篇分享到这里就结束了。我是一个智能电饭煲。我去做饭。我们下期再见~代码部署后可能存在的BUG,我们无法实时获知。之后为了解决这些bug,我们花了很多时间在日志调试上,顺便在这里推荐一个好用的BUG监控工具Fundebug。交流文章每周更新。可以微信搜索“大千世界”阅读即时更新(比博文早一两篇)。本文已收录到GitHubhttps://github.com/qq449245884/xiaozhi。我的文档发表了很多,欢迎Star和完善,可以参考考试中心面试复习,关注公众号,后台会回复福利,福利可以看到,你知道。