Vue3加载条(LoadingBar)

发布时间:2026/8/29 12:55:14
Vue3加载条(LoadingBar) 效果如下图在线预览APIsLoadingBar参数说明类型默认值containerClass加载条容器的类名stringundefinedcontainerStyle加载条容器的样式CSSProperties{}loadingBarSize加载条大小单位pxnumber2colorLoading加载中颜色stringundefinedcolorFinish加载完成颜色stringundefinedcolorError加载错误颜色string‘#ff4d4f’to加载条的挂载位置可选元素标签名例如body或者元素本身false 会待在原地string | HTMLElement | false‘body’Methods名称说明类型start开始加载的回调函数(from 0, to 80) voidfinish结束加载的回调函数() voiderror出现错误的回调函数() void创建加载条组件LoadingBar.vue其中引入使用了以下工具函数获取依赖注入 useInjectscript setup langtsimport{ref,computed,watch,nextTick}fromvueimporttype{CSSProperties}fromvueimport{useInject}fromcomponents/utilsexportinterfaceProps{containerClass?:string// 加载条容器的类名containerStyle?:CSSProperties// 加载条容器的样式loadingBarSize?:number// 加载条大小单位 pxcolorLoading?:string// 加载中颜色colorFinish?:string// 加载完成颜色colorError?:string// 加载错误颜色to?:string|HTMLElement|false// 加载条的挂载位置可选元素标签名例如 body或者元素本身false 会待在原地}constpropswithDefaults(definePropsProps(),{containerClass:undefined,containerStyle:()({}),loadingBarSize:2,colorLoading:undefined,colorFinish:undefined,colorError:#ff4d4f,to:body})constinitialDisplayrefboolean(false)// 性能优化使用 v-if 避免初始时不必要的渲染展示之后使用 v-show 来控制显示隐藏constshowLoadingBarrefboolean(false)// 加载条是否显示constloadingBarRefref()// 加载条元素引用consttransitionDisabledref(false)// 是否禁用过渡表示使用仅由 JavaScript 执行的动画constloadingStartedrefboolean(false)// 加载条是否开始constloadingFinishingrefboolean(false)// 加载条是否完成constloadingErroringrefboolean(false)// 加载条是否报错const{colorPalettes}useInject(LoadingBar)// 主题色注入constcolorLoadingComputedcomputed((){if(props.colorLoadingundefined){returncolorPalettes.value[5]}else{returnprops.colorLoading}})constcolorFinishComputedcomputed((){if(props.colorFinishundefined){returncolorPalettes.value[5]}else{returnprops.colorFinish}})watch(showLoadingBar,(to){if(to!initialDisplay.value){initialDisplay.valuetrue}},{immediate:true})asyncfunctioninit():Promisevoid{showLoadingBar.valuefalseloadingFinishing.valuefalseloadingErroring.valuefalsetransitionDisabled.valuetrueawaitnextTick()transitionDisabled.valuefalse}// 加载条开始加载的回调函数asyncfunctionstart(from0,to80,status:starting|errorstarting):Promisevoid{loadingStarted.valuetrueawaitinit()if(loadingFinishing.value){return}showLoadingBar.valuetrueawaitnextTick()if(!loadingBarRef.value){return}loadingBarRef.value.style.transitionnone// 禁用过渡loadingBarRef.value.style.maxWidth${from}%voidloadingBarRef.value.offsetWidth// 触发浏览器回流(重排)loadingBarRef.value.classNameloading-bar loading-bar-${status}loadingBarRef.value.style.transitionloadingBarRef.value.style.maxWidth${to}%}// 加载条结束加载的回调函数asyncfunctionfinish():Promisevoid{if(loadingFinishing.value||loadingErroring.value){return}if(loadingStarted.value){awaitnextTick()}loadingFinishing.valuetrueif(!loadingBarRef.value){return}loadingBarRef.value.classNameloading-bar loading-bar-finishingloadingBarRef.value.style.maxWidth100%voidloadingBarRef.value.offsetWidth// 触发浏览器回流(重排)showLoadingBar.valuefalse}// 加载条出现错误的回调函数functionerror():void{if(loadingFinishing.value||loadingErroring.value){return}if(!showLoadingBar.value){voidstart(100,100,error).then((){loadingErroring.valuetrue})}else{loadingErroring.valuetrueif(!loadingBarRef.value){return}loadingBarRef.value.classNameloading-bar loading-bar-errorloadingBarRef.value.style.maxWidth100%voidloadingBarRef.value.offsetWidth showLoadingBar.valuefalse}}functiononAfterEnter():void{if(loadingErroring.value){showLoadingBar.valuefalse}}asyncfunctiononAfterLeave():Promisevoid{awaitinit()}defineExpose({start,finish,error})/scripttemplateTeleport:disabledto false:toto false ? null : toTransition namefade-inappearafter-enteronAfterEnterafter-leaveonAfterLeave:css!transitionDisableddiv v-ifinitialDisplayv-showshowLoadingBarclassloading-bar-wrap:classcontainerClass:style[--loading-bar-size:${loadingBarSize}px; --loading-bar-color-loading:${colorLoadingComputed}; --loading-bar-color-finish:${colorFinishComputed}; --loading-bar-color-error:${colorError};,containerStyle]div refloadingBarRefclassloading-barstylemax-width: 100%/div/div/Transition/Teleport/templatestyle langlessscoped.fade-in-enter-active{transition:all0.3s cubic-bezier(0.4,0,0.2,1);}.fade-in-leave-active{transition:all0.8s cubic-bezier(0.4,0,0.2,1);}.fade-in-enter-from,.fade-in-leave-to{opacity:0;}.loading-bar-wrap{position:fixed;top:0;left:0;right:0;z-index:9999;height:var(--loading-bar-size);.loading-bar{width:100%;transition:max-width 4s linear,background0.2s linear;height:var(--loading-bar-size);border-radius:var(--loading-bar-size);}.loading-bar-starting{background:var(--loading-bar-color-loading);}.loading-bar-finishing{background:var(--loading-bar-color-finish);transition:max-width0.2s linear,background0.2s linear;}.loading-bar-error{background:var(--loading-bar-color-error);transition:max-width0.2s linear,background0.2s linear;}}/style在要使用的页面引入其中引入使用了以下组件Vue3间距SpaceVue3按钮Buttonscript setup langtsimportLoadingBarfrom./LoadingBar.vueimport{ref}fromvueconstloadingBarref()constdisabledref(true)constlocalCardRefref()constlocalLoadingBarref()constcustomLoadingBarref()functionhandleStart(){loadingBar.value.start()disabled.valuefalse}functionhandleFinish(){loadingBar.value.finish()disabled.valuetrue}functionhandleError(){disabled.valuetrueloadingBar.value.error()}/scripttemplatedivh1{{$route.name}}{{$route.meta.title}}/h1h2classmt30 mb10基本使用/h2SpaceButton typeprimaryclickhandleStart开始/ButtonButton:disableddisabledclickhandleFinish结束/ButtonButton typedangerclickhandleError报个错/Button/SpaceLoadingBar refloadingBar/h2classmt30 mb10局部加载条/h2divclasscard-containerreflocalCardRefSpaceButton typeprimaryclicklocalLoadingBar.start()Start/ButtonButtonclicklocalLoadingBar.finish()Finish/ButtonButton typedangerclicklocalLoadingBar.error()Error/Button/Space/divLoadingBar reflocalLoadingBar:container-style{ position: absolute }:tolocalCardRef/h2classmt30 mb10自定义加载条样式/h2SpaceButton typeprimaryclickcustomLoadingBar.start()Start/ButtonButtonclickcustomLoadingBar.finish()Finish/ButtonButton typedangerclickcustomLoadingBar.error()Error/Button/SpaceLoadingBar refcustomLoadingBar:loading-bar-size5color-loading#2db7f5color-finish#52c41acolor-errormagenta//div/templatestyle langlessscoped.card-container{position:relative;display:flex;align-items:center;height:200px;padding:16px 24px;border:1px solid #d9d9d9;}/style