基于react-routet v7 的配置式 + 约定式路由系统 第二步:一个简单的约定式路由系统

基于第一步,来对路由系统进行初步改造:juejin.cn/post/759366... 若有需要,请点击跳转 ~

实现思路

  1. 基于 react-router-dom v7
  2. 利用 webpack 注入的require 变量,在编译时动态扫描目录,获取匹配的文件列表.
  3. 遍历文件目录,使用 lazy 动态导入组件
  4. 返回 routes

事情开始变得不简单喽 ~

补充知识

什么是 require.context?

Webpack 的 API,用于在编译时动态扫描目录,获取匹配的文件列表。 语法

js 复制代码
  require.context(
    directory,      // 扫描的目录(相对路径)
    useSubdirectories, // 是否递归扫描子目录
    regExp          // 匹配文件的正则表达式
  )

示例

js 复制代码
  // 扫描 ../pages 目录下的所有 index.tsx 文件
  const context = require.context('../pages', true, /index\.tsx$/);

  // 获取所有匹配的文件路径
  context.keys();
  // ["./chat/index.tsx", "./file/index.tsx", "./shiti/index.tsx"]

  // 动态导入某个文件
  context('./chat/index.tsx');

require 是哪里来的?

Webpack 在编译时会注入一些全局变量:

标题 来源 说明
require Webpack 模块导入函数(CommonJS 风格)
require.context Webpack Webpack 特有的 API
import.meta.glob Vite Vite 等价功能
__dirname Node.js 当前文件所在目录

lazy

lazy 的作用

实现代码分割(Code Splitting),把不同页面的代码拆分成独立的 bundle,按需加载:

js 复制代码
 不使用 lazy:
  ├── main.js (2MB)  ← 所有页面代码打包在一起,首次加载很慢

  使用 lazy:
  ├── main.js (200KB)      ← 首屏只加载核心代码
  ├── chat.js (150KB)      ← 访问 /chat 时才加载
  ├── shiti.js (100KB)     ← 访问 /shiti 时才加载
  └── upload.js (80KB)     ← 访问 /upload 时才加载

不是必须。可以不用,用了更好 ~

好啦,知识铺垫结束 ~ ,正文开始~

文件大迁移 ~

首先,一个约定式路由系统,需要先约定 文件目录, 这里我选 src/pages 目录,然后先对目录做一个整理的大迁移 --- 将原来 src 目录下的页面文件统统移到 src/pages 目录下

像这样

扫描 pages 目录生成文件配置

src/utils/routes.tsx

js 复制代码
import { ComponentType, lazy } from "react" 

export interface RouteItem { path: string, component: ComponentType } 
export const RouteWrapper = ({ 
    component: Component 
}: { component: React.ElementType }) => { 
return <Component />; } 

// 路由生成器 
export const generateRoutes = () => { 
    const routes: RouteItem[] = [ 
        { path: '/chat', component: lazy(() => import('../pages/chat')) }, 
        { path: '/shiti', component: lazy(() => import('../pages/shiti')) }, 
        { path: '/file', component: lazy(() => import('../pages/file')) } 
    ] 

    return routes; 
}

替换pages/index.tsx 中的写法

额,好像有点长,不管了,先这样,下一步再优化吧 ~

js 复制代码
import { ConfigProvider } from 'antd';
import zhCN from 'antd/locale/zh_CN';

import { Suspense } from 'react';
import './index.css';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
import Layout from './layouts'
import { generateRoutes, RouteWrapper } from "./utils/routes";

const App = () => {
    const routes = generateRoutes()
    return (
        <ConfigProvider locale={zhCN}>
            <BrowserRouter>
                <Routes>
                    <Route path="/" element={<Layout />} >
                        {
                            routes.map(item => {
                                const { path, component } = item || {}
                                return (
                                    <Route 
                                        key={path}
                                        path={path}
                                        element={
                                            <Suspense fallback={<>loading...</>}>
                                            <RouteWrapper component={component} />
                                            </Suspense>
                                        }
                                    />
                                )
                            })
                        }
                    </Route>
                </Routes>
            </BrowserRouter>
        </ConfigProvider>
    )
}

export default App

加入 webpack 变量,编译时扫描文件目录

上面 generateRoutes 的自动化 ~

ts 复制代码
// 扫描 pages 目录生成路由配置

import { ComponentType, lazy } from "react"

export interface RouteItem {
    path: string,
    component: ComponentType
}

// 由于 React 不支持小写字母开头的组件名称,这里我们需要用 wrapper 包裹一下
export const RouteWrapper = ({ 
    component: Component 
}: { component: React.ElementType }) => {
    return <Component />;
}

/**                                                                                                                                                          
  * 路由生成器                            
  * 约定规则:                                                           
  *    
  * - src/pages/chat/index.tsx  → /chat                                                                                                                       
  * - src/pages/file/index.tsx  → /file                                                                                                                       
  * - src/pages/shiti/index.tsx → /shiti                                                                                                                      
  */    
export const generateRoutes = (): RouteItem[] => {

    const pagesContext = require.context('../pages', true, /index\.tsx$/);


    // 遍历所有匹配的文件   
    // 注意: 这里没有体现递归遍历,不过这个不重要,项目目前不需要        
    pagesContext.keys().forEach((filePath: string) => {                               
        // filePath 示例: "./chat/index.tsx"                   
        const pathParts = filePath.split('/');  

        const length = pathParts.length;
        const lastSplitIndex =  filePath.lastIndexOf('/'); // 找到最后一个 / 的位置
        const path = filePath.slice(2, lastSplitIndex); // 去掉 ./   

        // 生成路径
        const routePath = `/${path}`;

        // 使用lazy 动态导入组件
        routes.push({
            path: routePath,
            component: lazy(() => import (`../pages/${path}`))
        })

        // 添加首页,默认跳转到第一个页面
        if (routes.length > 0) {
            routes.unshift({
                path: '/',
                component: routes[0].component
            })
        }
    })

    return routes;
}

OK 今天就到这啦 ~ 明天见,本专栏正在持续更新哦 ~

下一步:配置式路由和约定式路由共存 ~ 敬请期待 ~

相关推荐
攀登的牵牛花2 小时前
前端向架构突围系列 - 框架设计(七):反应式编程框架Flower的设计
前端·架构
佛系打工仔2 小时前
K线绘制前言
前端
遇见~未来2 小时前
JavaScript数组全解析:从本质到高级技巧
开发语言·前端·javascript
石像鬼₧魂石2 小时前
80 端口(Web 服务)渗透测试完整总结(含踩坑 + 绕过 + 实战流程)
linux·运维·服务器·前端·网络·阿里云
C_心欲无痕3 小时前
nginx - 核心概念
运维·前端·nginx
开开心心_Every3 小时前
安卓做菜APP:家常菜谱详细步骤无广简洁
服务器·前端·python·学习·edge·django·powerpoint
前端_Danny3 小时前
用 ECharts markLine 实现节假日标注
前端·信息可视化·echarts
古城小栈3 小时前
Rust 丰富&好用的 格式化语法
前端·算法·rust
丢,捞仔3 小时前
uni-app上架应用添加权限提示框
前端·javascript·uni-app