基于文件的路由,大大减轻项目结构负担,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.globeagerimport 配置了具名导出该对象,但这种做法会导致 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 应该就能这么做,无非是导出的路由配置项有差异,当然由于我没找到相关资料,啃源代码的能力也不太行,不清楚我的写法是否规范,有没有隐患。

相关推荐
San30.1 小时前
JavaScript 深度解析:从 map 陷阱到字符串奥秘
开发语言·javascript·ecmascript
初遇你时动了情1 小时前
前端使用TensorFlow.js reactjs调用本地模型 实现图像、文本、音频/声音、视频相关识别
前端·javascript·tensorflow
十一.3661 小时前
66-69 原型对象,toString(),垃圾回收
开发语言·javascript·原型模式
AiXed4 小时前
PC微信WDA算法
前端·javascript·macos
博客zhu虎康5 小时前
React+Ant design
javascript·react.js·ecmascript
一只小阿乐9 小时前
react 封装弹框组件 传递数据
前端·javascript·react.js
533_10 小时前
[element-plus] el-tree 动态增加节点,删除节点
前端·javascript·vue.js
禁止摆烂_才浅10 小时前
前端开发小技巧-【JavaScript】- 获取元素距离 document 顶部的距离
前端·javascript·react.js
wshzd10 小时前
LLM之Agent(二十九)|LangChain 1.0核心组件介绍
前端·javascript·langchain
程序猿_极客10 小时前
Vue 2脚手架从入门到实战核心知识点全解析(day6):从工程结构到高级通信(附代码讲解)
前端·javascript·vue.js·vue2学习笔记