0.0.3 • Published 7 months ago

@sagaroute/react v0.0.3

Weekly downloads
-
License
MIT
Repository
github
Last release
7 months ago

@sagaroute/react

NPM NPM codecov npm type definitions

一款根据约定式路由规则生成路由列表的插件,生成的路由列表可直接用于react-router@6+

快速上手

1. 安装

npm install @sagaroute/react

2. 在路由模板文件中用注释做标记注入

路由模板文件是指要被注入路由列表的文件,我们需要通过注释来指明路由模板文件中哪个位置被注入路由列表依赖

例如存在路由模板文件,其内容如下:

import React from 'react';

const routes = [];
const router = createBrowserRouter(routes);
export default router;

我们需要对上述文件用注释进行标记,标记后如下所示:

import React from 'react';
import { createBrowserRouter } from 'react-router-dom';
/* sagaroute-inject:imports */

/* sagaroute-inject:routes */
const routes = [];
const router = createBrowserRouter(routes);
export default router;

其中/* sagaroute-inject:imports */用于标记依赖注入的位置,/* sagaroute-inject:routes */用于标记路由列表注入的位置。关于这些注释的含义和路由模板文件的更多说明可看此处

3. 生成且注入路由

在项目目录中运行以下代码:

import SagaRoute from '@sagaroute/react';

const sagaRoute = new SagaRoute({
  // 指定页面文件目录,默认为src/pages
  dirpath: 'src/views',
  // 指定路由模板文件,默认为src/routes.tsx
  routeFilePath: 'src/router/index.jsx',
});

// 调用routing后,sagaroute会扫描页面文件目录且生成路由列表,把路由列表注入到路由文件中
sagaRoute.routing();

运行成功后,"src/router/index.js"文件的内容如下所示:

import React from 'react';
import { createBrowserRouter } from 'react-router-dom';
/* sagaroute-inject:imports */
/* injected by sagaroute: start */
import PagesIndex from '../pages/index.jsx';
/* injected by sagaroute: end */

/* sagaroute-inject:routes */
const routes = [
  {
    path: '/',
    element: <PagesIndex />,
  },
];
const router = createBrowserRouter(routes);
export default router;

约定式路由规则

本插件的约定式路由规则的设计主要参考uminext,关于路由规则的详细可看约定式路由规则

配置介绍

可以在初始化@sagaroute/react实例时,传入配置项,如下所示:

import SagaRoute from '@sagaroute/react';

const sagaroute = new Sagaroute({
  dirpath: 'src/views',
});

配置项类型如下所示:

interface RoutingOption {
  rootPath?: string;
  dirpath?: string;
  layoutDirPath?: string;
  routeFilePath?: string;
  lazy?: boolean | LazyFn;
  pathRewrite?: Record<string, string>;
  onWarning?: (message: string) => void;
  hooks?: {
    build?: {
      before?: Handler<HookBeforeBuild>;
      after?: Handler<HookAfterBuild>;
    };
    gather?: {
      before?: Handler<GatherHookBefore>;
      beforeEach?: Handler<GatherHookBeforeEach>;
      afterEach?: Handler<GatherHookAfterEach>;
      after?: Handler<GatherHookAfter>;
    };
    weave?: {
      before?: Handler<WeaveHookBefore>;
      beforeEach?: Handler<WeaveHookBeforeEach>;
      afterEach?: Handler<WeaveHookAfterEach>;
      after?: Handler<WeaveHookAfter>;
    };
    print?: {
      parse?: {
        before?: Handler<PrintHookBeforeParse>;
        after?: Handler<PrintHookAfterParse>;
      };
      inject?: {
        before?: Handler<PrintHookBeforeInject>;
        after?: Handler<PrintHookAfterInject>;
      };
      write?: {
        before?: Handler<PrintHookBeforeWrite>;
        after?: Handler<PrintHookAfterWrite>;
      };
    };
  };
}

配置项中所有参数的简要说明如下所示:

名称说明类型默认值
dirpath页面文件目录路径string'src/pages'
layoutDirPath全局路由目录路径string'src/layouts'
routeFilePath指定路由模板文件的路径string'src/route.tsx'
lazy路由是否懒加载boolean/Function(string): booleanfalse
hooks执行周期的钩子函数object--
pathRewrite用于对 import 语句的路径进行替换Object{string: string}--
rootPath项目路径stringprocess.cwd()
onWarning触发警告时的回调函数function(message: string): void--

对上述配置参数中更详细的说明可看API

配置文件

支持往项目中添加sagaroute.config.jssagaroute.config.cjs作为配置文件,在文件中以CommonJS的格式编写和导出部分上述配置项,例如:

/** @type {import('@sagaroute/react').RoutingOption} */
module.exports = {
  lazy: function (filepath) {
    return filepath.includes('charts');
  },
};

深入原理: 三个阶段

@sagaroute/react从生成路由列表到将其插入到路由模板文件的整个过程中需要经历三个的执行阶段,分别是gatherweaveprint,其分布图如下所示:

gather

gather阶段会遍历路由文件目录下的所有文件,根据约定式路由规则过滤出符合条件的文件,并把每个符合条件的 文件对象(即包括文件和文件夹) 转换成FileNode节点,最后组成FileNodes列表

FileNode的类型如下所示:

interface FileNode {
  name: string;
  children?: FileNode[];
  type: 'dir' | 'file';
  layoutNode?: boolean;
  props?: {
    routeProps?: Record<string, any>;
    routeOptions?: {
      layout?: boolean;
      [key: string]: any;
    };
  };
  path: string;
  dependencies?: Dependency[];
}

参数解释:

  • name: 节点对应的文件对象名称
  • children: 如果节点对应文件夹,则children为该文件夹下的文件对象转换后的节点列表
  • type: dir代表文件对象为文件夹,file代表文件对象为文件
  • layoutNode: 代表文件对象是否为全局路由文件
  • props: 包含文件中导出的与路由相关的两个属性routePropsrouteOptions
  • path: 文件对象的绝对路径
  • dependencies: props中包含的属性所附带的依赖

weave

weave阶段会遍历gather生成的FileNode数组,根据react-router的规则Routes生成路由列表和Routes的依赖集合Imports

Routes的类型等同于react-routerRouteObject类型,如下所示:

interface RouteObject {
  path?: string;
  index?: boolean;
  children?: React.ReactNode;
  caseSensitive?: boolean;
  id?: string;
  loader?: LoaderFunction;
  action?: ActionFunction;
  element?: React.ReactNode | null;
  Component?: React.ComponentType | null;
  errorElement?: React.ReactNode | null;
  ErrorBoundary?: React.ComponentType | null;
  handle?: RouteObject['handle'];
  shouldRevalidate?: ShouldRevalidateFunction;
  lazy?: LazyRouteFunction<RouteObject>;
}

print

print阶段会把weave生成的RoutesImports作为模板变量插入到路由模板文件对应的位置上,从而完成注册路由从扫描、生成到覆写的过程。关于路由模板文件的解释可看此处

钩子函数

gatherweaveprint三个阶段都可以注册钩子函数去调整整个路由列表生成的过程,利用这些钩子函数,可以实现以下场景需求:

更多详细可看钩子函数