当前位置: 首页 > news >正文

从 PHP 到 AI + Golang,程序员自救转型手记(十三):前端路由初始化

这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-mall(github | gitee)开源项目的制作,全程记录分享。

在上一期,我们已经完成 “前端状态商店、多语言初始化”,本期将完成:前端路由初始化

前端路由初始化

vue 里边实现路由,使用 vue-router,核心部分大概只需要以下三行代码:

constrouter=createRouter({history:createWebHashHistory(),routes:staticRoutes,})

如上,路由本身很简单,麻烦的地方在于建立第一个路由时,一般需要同时配置好:静态路由的自动发现、首屏 loading、路由切换进度条

静态路由的自动发现

路由功能规划:

  1. 有一些预设好的静态路由,比如首页、登录页、404页,全部定义于/@/router/static.ts,对应的组件,在合理位置建立空白 vue 组件即可
  2. 系统能自动加载/@/router/static文件夹内的所有 ts 路由定义文件,自动合并到预设静态路由数组中,最终完成所有静态路由注册
  3. 创建路由实例的逻辑写在/@/router/index.ts文件中,并于mian.ts完成路由插件注册

直接将规划发给 cc,基本一次性实现了所有需求,但还是人工整理了一下,因为写提示词还不如自己改的小细节,最终核心代码如下:

// src\router\static.ts/* * 静态路由(支持自动扩展) * 系统会自动加载 ./static 目录及其子目录中的所有 .ts 文件 * 每个模块的 default 导出可以是 RouteRecordRaw 或 RouteRecordRaw[],自动 push 到以下 staticRoutes 数组 */conststaticRoutes:Array<RouteRecordRaw>=[{path:'/',name:'/',component:()=>import('/@/views/index.vue'),meta:{title:`pageTitles.Home`,},},// ...登录页,404 等其他静态路由]// 静态路由自动扩展逻辑conststaticFiles=import.meta.glob('./static/**/*.ts',{eager:true})for(constpathinstaticFiles){constmodule=staticFiles[path]asanyif(!module.default){console.warn(`[Router] Static route module${path}does not export default, skipped`)continue}constroute=module.defaultasRouteRecordRaw|RouteRecordRaw[]if(Array.isArray(route)){staticRoutes.push(...route)}else{staticRoutes.push(route)}}exportdefaultstaticRoutes

创建src\router\static\adminBase.ts测试静态路由自动扩展功能:

// src\router\static\adminBase.tsimporttype{RouteRecordRaw}from'vue-router'/** * 后台基础路由路径 */exportconstadminBaseRoutePath='/admin'/* * 后台基础静态路由 */constadminBaseRoute:RouteRecordRaw={path:adminBaseRoutePath,name:'admin',component:()=>import('/@/layouts/admin/index.vue'),// 直接重定向到 loading 路由redirect:adminBaseRoutePath+'/loading',meta:{title:`pageTitles.Loading`,},children:[{path:'loading/:to?',name:'adminMainLoading',component:()=>import('/@/layouts/common/loading.vue'),meta:{title:`pageTitles.Loading`,},},],}exportdefaultadminBaseRoute

src\router\static\adminBase.tsdefault导出adminBaseRoute会自动 push 到staticRoutes数组,然后通过src\router\index.tscreateRouter创建路由实例时完成注册。

静态路由涉及的 vue 文件,如/@/layouts/admin/index.vue/@/views/index.vue等都已经由 cc 创建完毕,里边填充了空白 vue 组件的代码,等待后续完善。

路由切换进度条

单页应用加载完毕之后,后续切换页面就不会刷新了,没有加载页面的过程,浏览器就不会显示自带的进度条(或其他 loading 态);

所以需要开发者自己写一个进度条,此进度条不代表真实的网页资源下载进度且路由切换往往很快,所以只需要在路由加载前显示,路由加载后隐藏即可;

我们选择使用nprogress实现路由切换进度条,它会在网页顶端显示一个从左到右的蓝色加载条

// src\router\index.ts 文件,即 createRouter 的文件importNProgressfrom'nprogress'import'nprogress/nprogress.css'import{createRouter,createWebHashHistory}from'vue-router'importstaticRoutesfrom'/@/router/static'constrouter=createRouter({history:createWebHashHistory(),routes:staticRoutes,})// 路由加载前router.beforeEach(()=>{// 显示进度条NProgress.configure({showSpinner:false})NProgress.start()})// 路由加载后router.afterEach(()=>{// 隐藏进度条NProgress.done()})exportdefaultrouter

首屏 loading

vue 单页应用,首次加载可能会很慢,我们需要尽快显示一个 loading 动画,避免长时间白屏,而且系统首次加载完毕后,再不显示此 loading 动画(路由切换不显示)。

loading 的显示只需要放到 vue-router 的路由加载前钩子中即可,整个系统的第一个路由加载前,就能触发到 loading 的显示,loading 的实现则直接使用原生 js 创建 div 即可(就不要加载 vue 组件什么的了)。

另外一个核心点在于首次加载完毕,loading 不会重复触发,所以需要一个标记,此标记的最佳实践是挂载到 window 对象上,先声明全局类型定义:

// common.d.ts 文件interfaceWindow{loading:boolean}

更新src\router\index.ts触发首屏 loading 显示:

importNProgressfrom'nprogress'import'nprogress/nprogress.css'import{createRouter,createWebHashHistory}from'vue-router'importstaticRoutesfrom'/@/router/static'import{loading}from'/@/utils/loading'constrouter=createRouter({history:createWebHashHistory(),routes:staticRoutes,})// 路由加载前router.beforeEach(()=>{NProgress.configure({showSpinner:false})NProgress.start()// 显示首屏 loadingif(!window.loading){loading.show()window.loading=true}})// 路由加载后router.afterEach(()=>{// 隐藏首屏 loading,只要不标记 window.loading 为 false,它就永远不会再触发了if(window.loading){loading.hide()}NProgress.done()})exportdefaultrouter

直接使用原生 js 创建 loading 的 div,挂载到 body 节点下:

// src\utils\loading.ts 文件import{nextTick}from'vue'import'/@/styles/loading.scss'exportconstloading={show:()=>{constdiv=document.createElement('div')div.className='ai-go-page-loading'div.innerHTML=`<div class="container"> <div class="main"> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> <div class="item"></div> </div> </div>`document.body.insertBefore(div,document.body.childNodes[0])},hide:()=>{nextTick(()=>{setTimeout(()=>{constel=document.querySelector('.ai-go-page-loading')el&&el.parentNode?.removeChild(el)},1000)})},}
// src\styles\loading.scss 文件 .ai-go-page-loading { width: 100%; height: 100%; position: fixed; z-index: 2147483600; background-color: #f5f5f5; .container { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); .main { width: 80px; height: 80px; .item { width: 33.333333%; height: 33.333333%; background: #409eff; float: left; animation: ai-go-page-loading-animation 1.2s infinite ease; border-radius: 1px; } .item:nth-child(7) { animation-delay: 0s; } .item:nth-child(4), .item:nth-child(8) { animation-delay: 0.1s; } .item:nth-child(1), .item:nth-child(5), .item:nth-child(9) { animation-delay: 0.2s; } .item:nth-child(2), .item:nth-child(6) { animation-delay: 0.3s; } .item:nth-child(3) { animation-delay: 0.4s; } } } } @keyframes ai-go-page-loading-animation { 0%, 70%, 100% { transform: scale3D(1, 1, 1); } 35% { transform: scale3D(0, 0, 1); } }
http://www.jsqmd.com/news/1101436/

相关文章:

  • RAG 知识库污染实战:从隐藏指令到敏感输出的间接提示注入复现与防护
  • PySide6实战:从登录到主界面,如何优雅地传递用户数据(附完整代码)
  • 当 Agent 有了身体:我用魔珐星云做了一个沉浸式互动叙事具身 Agent
  • 从纯文本政务 Agent 到具身交互智能:我用魔珐星云搭建大厅咨询数字人。
  • 基于超构透镜的像差控制
  • 蜂群图核心特点
  • 用Python玩转量子退火:手把手教你实现subQUBO算法解决TSP问题
  • 速率管理化技术中的速率计划速率实施速率验证
  • OriginOS 6状态栏交互与视觉自定义技术解析:Android 15适配指南
  • 矿山安全监测数据采集物联网方案
  • 手把手教你用Matlab和Argo数据复现海平面变化研究(附完整代码与避坑指南)
  • 手把手教你用杰理AC695x的I2C驱动ACM8625S数字功放(附完整代码)
  • WaveTools鸣潮工具箱终极指南:3步快速安装,解锁帧率优化与抽卡分析
  • Minecraft服务器包生成技术指南:ServerPackCreator架构解析与性能优化
  • 快手Agent开发一面:什么是 A2A 协议?它和 MCP 协议的区别是什么?
  • 目标检测多尺度特征融合:原理、演进与YOLO实战指南
  • 告别DHT11!用ESP32-S3和AHT20搭建高精度温湿度监测站(附完整代码与避坑指南)
  • 当 Agent 有了情绪和身体:我用魔珐星云做了一个会共情的具身 Agent
  • VMware OVF导出效率提升300%的黄金配置(附实测对比数据与vSphere 8.0兼容性验证)
  • 如何写出对单元测试“友好”的代码?
  • 别再手动插图片了!用EasyExcel 3.0.5 + POI 3.17,一键生成带产品图的Excel报告
  • 数据库安全管理策略
  • 一高科技集团AI+教育战略的核心理念与落地路径
  • EDA 签核高峰总是撞车,企业该怎么安排许可证时段
  • Rust Trait 对象的动态派发机制
  • 5分钟掌握ServerPackCreator:Minecraft服务器包自动化生成终极指南
  • Illustrator对象排序终极指南:用Harmonizer脚本告别手动排列噩梦
  • “监、管、控”一体化网管运维方案
  • 告别模拟器:5步在Windows电脑上直接运行安卓应用
  • 别怕传递函数!用MATLAB和Mathcad手把手教你搞定开关电源环路分析