
文章目录参考资料代码解析index.htmlapps\web-antd\src\main.tspackages\utils\src\helpers\unmount-global-loading.tsvite.config.tsinternal\vite-config\src\config\index.tsinternal\vite-config\src\config\application.tsinternal\vite-config\src\utils\env.tsinternal\vite-config\src\plugins\index.tsinternal\vite-config\src\plugins\inject-app-loading\index.tsinternal\vite-config\src\plugins\inject-app-loading\index.tsinternal\vite-config\src\plugins\inject-app-loading\default-loading.html参考资料https://doc.vben.pro/https://doc.vben.pro/guide/in-depth/loading.html代码解析index.htmlbodyscriptdata-app-loadinginject-jsvarthemelocalStorage.getItem(vben-web-antd-5.7.0-prod-preferences-theme);document.documentElement.classList.toggle(dark,/dark/.test(theme));/scriptstyledata-app-loadinginject-csshtml{line-height:1.15;}.loading{position:fixed;top:0;left:0;z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;overflow:hidden;background-color:#f4f7f9;}.loading.hidden{visibility:hidden;pointer-events:none;opacity:0;transition:all 0.8s ease-out;}.dark .loading{background:#0d0d10;}.title{margin-top:66px;font-size:28px;font-weight:600;color:rgb(0 0 0 / 85%);}.dark .title{color:#fff;}.loader{position:relative;width:48px;height:48px;}.loader::before{position:absolute;top:60px;left:0;width:48px;height:5px;content:;background:hsl(var(--primary,210 100% 50%)/ 50%);border-radius:50%;animation:shadow-ani 0.5s linear infinite;}.loader::after{position:absolute;top:0;left:0;width:100%;height:100%;content:;background:hsl(var(--primary,210 100% 50%));border-radius:4px;animation:jump-ani 0.5s linear infinite;}keyframesjump-ani{15%{border-bottom-right-radius:3px;}25%{transform:translateY(9px)rotate(22.5deg);}50%{border-bottom-right-radius:40px;transform:translateY(18px)scale(1,0.9)rotate(45deg);}75%{transform:translateY(9px)rotate(67.5deg);}100%{transform:translateY(0)rotate(90deg);}}keyframesshadow-ani{0%, 100%{transform:scale(1,1);}50%{transform:scale(1.2,1);}}/styledivclassloadingid__app-loading__divclassloader/divdivclasstitleVben Admin Antd/div/divdividapp/div/bodyapps\web-antd\src\main.ts// 移除并销毁loadingunmountGlobalLoading();packages\utils\src\helpers\unmount-global-loading.ts/** * 移除并销毁loading * 放在这里是而不是放在 index.html 的app标签内是因为这样比较不会生硬渲染过快可能会有闪烁 * 通过先添加css动画隐藏在动画结束后在移除loading节点来改善体验 * 不好的地方是会增加一些代码量 * 自定义loading可以见https://doc.vben.pro/guide/in-depth/loading.html */exportfunctionunmountGlobalLoading(){// 查找全局 loading 元素constloadingElementdocument.querySelector(#__app-loading__);if(loadingElement){// 添加隐藏类触发过渡动画loadingElement.classList.add(hidden);// 查找所有需要移除的注入 loading 元素constinjectLoadingElementsdocument.querySelectorAll([data-app-loading^inject],);// 当过渡动画结束时移除 loading 元素和所有注入的 loading 元素loadingElement.addEventListener(transitionend,(){loadingElement.remove();// 移除 loading 元素injectLoadingElements.forEach((el)el.remove());// 移除所有注入的 loading 元素},{once:true},);// 确保事件只触发一次}}vite.config.tsimport{defineConfig}fromvben/vite-config;internal\vite-config\src\config\index.tsfunctiondefineConfig(userConfigPromise?:DefineConfig,type:application|auto|libraryauto,):VbenViteConfig{letprojectTypetype;// 根据包是否存在 index.html,自动判断类型if(projectTypeauto){consthtmlPathjoin(process.cwd(),index.html);projectTypeexistsSync(htmlPath)?application:library;}switch(projectType){caseapplication:{returndefineApplicationConfig(userConfigPromise);}caselibrary:{returndefineLibraryConfig(userConfigPromise);}default:{thrownewError(Unsupported project type:${projectType});}}}internal\vite-config\src\config\application.tsfunctiondefineApplicationConfig(userConfigPromise?:DefineApplicationOptions){returndefineConfig(async(config){constoptionsawaituserConfigPromise?.(config);const{appTitle,base,port,...envConfig}awaitloadAndConvertEnv();const{command,mode}config;const{application{},vite{}}options||{};constrootprocess.cwd();constisBuildcommandbuild;constenvloadEnv(mode,root);constpluginsawaitloadApplicationPlugins({archiver:true,archiverPluginOptions:{},compress:false,compressTypes:[brotli,gzip],devtools:true,env,extraAppConfig:true,html:true,i18n:true,importmapOptions:defaultImportmapOptions,injectAppLoading:true,injectMetadata:true,isBuild,license:true,mode,nitroMock:!isBuild,nitroMockOptions:{},print:!isBuild,printInfoMap:{Vben Admin Docs:https://doc.vben.pro,},pwa:true,pwaOptions:getDefaultPwaOptions(appTitle),vxeTableLazyImport:true,...envConfig,...application,});internal\vite-config\src\utils\env.tsasyncfunctionloadAndConvertEnv(matchVITE_,confFilesgetConfFiles(),):PromisePartialApplicationPluginOptions{appTitle:string;base:string;port:number;}{constenvConfigawaitloadEnv(match,confFiles);const{VITE_APP_TITLE,VITE_ARCHIVER,VITE_BASE,VITE_COMPRESS,VITE_DEVTOOLS,VITE_INJECT_APP_LOADING,VITE_NITRO_MOCK,VITE_PORT,VITE_PWA,VITE_VISUALIZER,}envConfig;constcompressTypes(VITE_COMPRESS??).split(,).filter((item)itembrotli||itemgzip);return{appTitle:getString(VITE_APP_TITLE,Vben Admin),archiver:getBoolean(VITE_ARCHIVER),base:getString(VITE_BASE,/),compress:compressTypes.length0,compressTypes,devtools:getBoolean(VITE_DEVTOOLS),injectAppLoading:getBoolean(VITE_INJECT_APP_LOADING),nitroMock:getBoolean(VITE_NITRO_MOCK),port:getNumber(VITE_PORT,5173),pwa:getBoolean(VITE_PWA),visualizer:getBoolean(VITE_VISUALIZER),};}internal\vite-config\src\plugins\index.ts/** * 根据条件获取应用类型的vite插件 */asyncfunctionloadApplicationPlugins(options:ApplicationPluginOptions,):PromisePluginOption[]{// 单独取否则commonOptions拿不到constisBuildoptions.isBuild;constenvoptions.env;const{archiver,archiverPluginOptions,compress,compressTypes,extraAppConfig,html,dayjs,i18n,importmap,importmapOptions,injectAppLoading,license,nitroMock,nitroMockOptions,print,printInfoMap,pwa,pwaOptions,vxeTableLazyImport,...commonOptions}options;constcommonPluginsawaitloadCommonPlugins(commonOptions);returnawaitloadConditionPlugins([...commonPlugins,{condition:i18n,plugins:async(){return[viteVueI18nPlugin({compositionOnly:true,fullInstall:true,runtimeOnly:true,}),];},},{condition:print,plugins:async(){return[awaitvitePrintPlugin({infoMap:printInfoMap})];},},{condition:vxeTableLazyImport,plugins:async(){return[awaitviteVxeTableImportsPlugin()];},},{condition:nitroMock,plugins:async(){return[awaitviteNitroMockPlugin(nitroMockOptions)];},},{condition:injectAppLoading,plugins:async()[awaitviteInjectAppLoadingPlugin(!!isBuild,env)],},internal\vite-config\src\plugins\inject-app-loading\index.ts/** * 用于生成将loading样式注入到项目中 * 为多app提供loading样式无需在每个 app - index.html单独引入 */asyncfunctionviteInjectAppLoadingPlugin(isBuild:boolean,env:Recordstring,any{},loadingTemplateloading.html,):PromisePluginOption|undefined{constloadingHtmlawaitgetLoadingRawByHtmlTemplate(loadingTemplate);const{version}awaitreadPackageJSON(process.cwd());constenvRawisBuild?prod:dev;constcacheName${env.VITE_APP_NAMESPACE}-${version}-${envRaw}-preferences-theme;// 获取缓存的主题// 保证黑暗主题下刷新页面时loading也是黑暗主题constinjectScriptscript>${cacheName}); document.documentElement.classList.toggle(dark, /dark/.test(theme)); /script;if(!loadingHtml){return;}return{enforce:pre,name:vite:inject-app-loading,transformIndexHtml:{handler(html){constre/body\s*/;htmlhtml.replace(re,body${injectScript}${loadingHtml});returnhtml;},order:pre,},};}internal\vite-config\src\plugins\inject-app-loading\index.ts/** * 用于获取loading的html模板 */asyncfunctiongetLoadingRawByHtmlTemplate(loadingTemplate:string){// 支持在app内自定义loading模板模版参考default-loading.html即可letappLoadingPathjoin(process.cwd(),loadingTemplate);if(!fs.existsSync(appLoadingPath)){const__dirnamefileURLToPath(newURL(.,import.meta.url));appLoadingPathjoin(__dirname,./default-loading.html);}returnawaitfsp.readFile(appLoadingPath,utf8);}internal\vite-config\src\plugins\inject-app-loading\default-loading.htmlstyle data-app-loadinginject-csshtml{/* same as ant-design-vue/dist/reset.css setting, avoid the title line-height changed */line-height:1.15;}.loading{position:fixed;top:0;left:0;z-index:9999;display:flex;flex-direction:column;align-items:center;justify-content:center;width:100%;height:100%;overflow:hidden;background-color:#f4f7f9;/* transition: all 0.8s ease-out; */}.loading.hidden{visibility:hidden;pointer-events:none;opacity:0;transition:all0.8s ease-out;}.dark.loading{background:#0d0d10;}.title{margin-top:66px;font-size:28px;font-weight:600;color:rgb(000/85%);}.dark.title{color:#fff;}.loader{position:relative;width:48px;height:48px;}.loader::before{position:absolute;top:60px;left:0;width:48px;height:5px;content:;background:hsl(var(--primary,210100%50%)/50%);border-radius:50%;animation:shadow-ani0.5s linear infinite;}.loader::after{position:absolute;top:0;left:0;width:100%;height:100%;content:;background:hsl(var(--primary,210100%50%));border-radius:4px;animation:jump-ani0.5s linear infinite;}keyframesjump-ani{15%{border-bottom-right-radius:3px;}25%{transform:translateY(9px)rotate(22.5deg);}50%{border-bottom-right-radius:40px;transform:translateY(18px)scale(1,0.9)rotate(45deg);}75%{transform:translateY(9px)rotate(67.5deg);}100%{transform:translateY(0)rotate(90deg);}}keyframesshadow-ani{0%,100%{transform:scale(1,1);}50%{transform:scale(1.2,1);}}/styledivclassloadingid__app-loading__divclassloader/divdivclasstitle%VITE_APP_TITLE%/div/div