基于文件的路由,大大减轻项目结构负担,vite 的妙用

最近因为毕设需要,又重温了一下前端知识,毕设是做个前后端分离的商城类项目,前台我是准备用 SolidJS 写,后台管理界面用 vue3。一开始是觉得前台业务逻辑一般不会太过复杂,忘了这是商城了,用户购物车、订单管理什么的,逻辑其实也挺复杂的。不过无所谓了,冲!!!

博客原文:vite之基于文件目录的路由模式

为什么要基于文件系统路由

其实也是在 vite 找 SolidJS 的社区模板的时候看到了这个说法,第一反应就是之前学 vue3 看的一个后台管理的开源项目的源码,里面用到了 import.meta.glob 来读取目录下的路由文件。但仔细看了一下,区别很大。

这里 SolidJS 的文件路由,主要指的是 solid-start 这个库里的基于文件的路由(详细可见:Routing (solidjs.com)),简单来说,就是我们在创建路由时,只需要在 routes 文件夹下新建 view 模板,系统会根据文件名和路劲来确定路由:

  • hogwarts.com ➜ /routes/index.tsx
  • hogwarts.com/blog ➜ /routes/blog.tsx
  • hogwarts.com/admin/edit-settings ➜ /routes/admin/edit-settings.tsx

并且也提供了通过在 tsx 文件了 export route 对象的方式来对路由进行其他配置,可以看到,这种做法使得项目的路由配置非常清晰高效。solid-start 的源码进去看了一下,没找到具体实现的代码在哪里,于是就尝试用 import.meta.glob 来实现该功能

import.meta.glob 介绍

import.meta.glob 是 vite 提供的一个从文件系统导入多个模块的函数,如果你不设置 {eager: true} 的话,它会自动转译成异步加载代码,我们的路由业务肯定是需要异步加载来进行分包的,所以这里就不设置 eager 了。

ts 复制代码
// 获取 routes 目录下的所有模板对象
const routeViews = import.meta.glob<any>('./routes/**/*.tsx')
console.log(routeViews)
​
// 会生成这样的东西
{
    "./routes/index.tsx": () => import("/src/routes/index.tsx")
    "./routes/shop/index.tsx": () => import("/src/routes/shop/index.tsx")
}
​

预处理获取到的数据

我们得到了 routeViews 之后,就需要对其进行预处理了,主要是将文件路径转化为路由的 path

注意这里我没有考虑 param 的情况,你可以对照 solid-start 的规则,用 id 之类的命名方式来识别路径参数,实现起来也很简单

ts 复制代码
function handleRouteViews() {
  const routes: RouteDefinition[] = []
​
  for (const path in routeViews) {
    // 这里的 component 就是上面的 import 异步函数
    const component = routeViews[path]
​
    // 替换路径
    let pathNew = path.replace('./routes/', '').replace('.tsx', '')
    if (pathNew.endsWith('index')) {
      pathNew = pathNew.replace('index', '')
    }
    const route = {
      path: pathNew,
      // 这里使用 SolidJS 的 lazy 函数来包装我们的异步导入
      component: lazy(component),
    }
    routes.push(route)
  }
​
  return routes
}

这样我们就根据文件路径获取到了路由路径和组件本身,但这显然还不够,因为实际业务中,我们在路由跳转的时候可能还需要进行一些工作,比如设置标题,准备数据之类的,SolidJS 的路由提供了 load 参数来实现跳转的时候进行数据获取和其它操作。

获取组件的路由配置

我们模仿 solid-start ,通过在组件中导出配置对象的方法来获取对应组件的配置(这里统一 export const route),按照以上的代码,不难想到通过 component.route 来获取目标对象,但 component 是一个异步函数,那我们又该如何在保证组件不会预先加载的情况下获取到对象呢?

一开始我想到的使用 import.meta.glob 的 eager 和 import 配置了具名导出该对象,但这种做法会导致 vite 不再将组件分包,也就是失去按需加载的能力,这无疑是致命的。后来又想到 vue 的那些开源的项目多是将路由配置与模板文件分开存放,就也想那么做,但感觉很鸡肋,又回到了老的路由配置模式。

想了半天,突然想到,load 本身就是一个函数,而且是支持异步的,直接将 component 放进 load 里不就好了?

typescript 复制代码
{
    load: () => {
        component().then((mod: any) => {
          const routeConf = mod.route
          if (routeConf?.title) {
            document.title = `${routeConf.title} - 特色农产品团购平台`
          }
          routeConf?.load?.()
        })
      },
}

测试,成功!

总结

其实后面我还是很担心这种写法是不是会导致分包问题,经过测试,正确的做到了分包和懒加载。理论上,只要是 vite 应该就能这么做,无非是导出的路由配置项有差异,当然由于我没找到相关资料,啃源代码的能力也不太行,不清楚我的写法是否规范,有没有隐患。

相关推荐
jktantan2 小时前
每个节点 41 字节:把一棵百万节点树拆进 14 个 typed array
前端·javascript·vue.js·算法·typescript
我命由我123452 小时前
访客系统形式(有用户概念、无用户概念)
java·开发语言·前端·javascript·python·产品运营·产品经理
程序员大阳3 小时前
副队长JavaScript教程(65)-详解JSON:轻量级数据交换格式
javascript·json·序列化·反序列化·stringify·parse
paopaokaka_luck5 小时前
基于springboot+vue的益暖微光公益服务平台(DeepSeek公益助手、善款信息追溯、志愿服务积分闭环、ECharts运营分析、活动状态自动流转)
java·前端·javascript·spring boot·mysql·架构·echarts
栀椩5 小时前
Electron 内嵌页面跨域问题(CORS/PNA)解决全记录
前端·javascript·electron
SEO_juper5 小时前
Chrome 扩展一键查看当前页面 SEO 信息:不用切页面,点一下全出来
前端·javascript·chrome
Highcharts.js5 小时前
CSUN 2026|Highcharts无障碍数据与AI分享
javascript·人工智能·数据分析·highcharts·图表开发
Nymph_Zhu5 小时前
vue3+element-plus 文件上传
前端·typescript·vue3·element-plus
系统集成-鹰翔5 小时前
ERP推单数据库怎么备份:SQLite WAL模式下只复制db文件为什么可能漏任务
javascript·数据库·经验分享·后端
QianhangQianping5 小时前
撤销重做不是「存快照」:一个可视化编辑器的Operation 设计
java·前端·数据库·typescript·编辑器·前端架构·撤销重做