前言
前两天做需求遇到个页面跳转,需要填写路径 url,大概如下:
这里是 demo,随便取的路径
强迫症的我搜了一下项目,发现之前很多都是硬编码字符串 url,看起来相当的丑,且不知道跳过去应该传啥参数,因为页面不一定是我写的,且就算是我写的,过了几天也不一定还记得页面需要接受什么传参,哪些参数必填等等。
刚好我们的路由是根据文件目录自动生成路径,所以萌生了实现一个根据文件目录自动编译生成路由方法的 webpack 插件,也就是这个 👉 genetate-history-method-webpack-plugin。
当我实现之后,刚好点点点同事过来我这边,我就演示给他看,没想到他一下子来个三声卧槽。但我想,这个卧槽不能只有他独享,我也想赢得你的一声卧槽,故决定将该插件开源。如果客官您觉得适合的,觉得思路不错的,或者觉得好用的,还请给个 star,小弟万分感谢~
以下是用了该插件后的效果:
gif 有点不清,还请凑合看吧
目前插件支持功能如下 ✨:
- react-router 支持 v5、 v6
- 支持 ts、js
- 支持 hash 和 browser 模式
- 支持自动生成路由方法,比如/order/detail,那就是 history.TO_ORDER_DETAIL
- 路由方法支持 TO、REPLACE、OPEN 三种方式
- 支持自动读取页面参数类型,也就是在页面文件同目录下新增 index.params.ts,这样就会自动读取类型,调用方法就有参数类型提示(只需写一遍,项目其他地方都可用到!)
⚠️ 当然,以上插件能起作用的条件是项目刚好也是根据文件系统路径生成路由,这是前提!这是前提!这是前提!
教你如何使用
如果本插件正合你的心意,那我来手摸手教教你如何使用。
先确保你项目有以下依赖
- qs,用于处理路径传参的
- history。react-router 能实现跳转,其底层依赖的正是 history,如果你项目没有,记得安装 react-router 相对于的 history 版本,我 playgrounds 下 v5 和 v6 对应的 history 版本如下,可以参考下(是在你项目没有 history 依赖的情况下)
- v5:history@4.9.0
- v6:history@5.3.0
在你 src 目录下导出 history 方法
如果你用的是 hash 模式,那可以在创建 src/history.ts 并填写以下代码,具体可以参考react-router-6/src/hash_history.ts:
import { createBrowserHistory } from 'history'
export default createBrowserHistory()
如果你用的是 browser 模式,那可以在创建 src/history.ts 并填写以下代码,具体可以参考react-router-6/src/browser_history.ts:
import { createBrowserHistory } from 'history'
export default createBrowserHistory()
安装 genetate-history-method-webpack-plugin 并配置 webpack
安装依赖:
pnpm add genetate-history-method-webpack-plugin -D
# or
yarn add genetate-history-method-webpack-plugin -D
# or
npm i genetate-history-method-webpack-plugin -D
然后配置 webpack,比如你用的是 react-router v6,那么可以参考以下配置:
// webpack.config.js
const GenerateHistoryMethodWebpackPlugin = require('genetate-history-method-webpack-plugin')
module.exports = {
plugins: [
new GenerateHistoryMethodWebpackPlugin({
pagesRootPath: path.resolve(__dirname, 'src/pages'),
originHistoryModuleName: '@/browser_history',
reactRouterVersion: 6,
}),
]
}
具体可以参考 playgrounds 下不同 router 版本,js、ts 的 webpack 配置的:
扔掉原先的 HashRouter 和 BrowserRouter
插件编译后会根据不同版本的 react-router 导出一个 Router,用这个替换掉你的 HashRouter 和 BrowserRouter
利用 require.context 生成约定式路由
通过 require.context 扫描 pages 下 index.page 的文件,然后处理文件路径并生成每一个 Route,就大功告成了:
const context = require.context('@/pages', true, /index\.page\./)
// 这里目的是拿到对应路径和组件,这里用的是同步模式,当然你也可以改造为异步路由模式
const routePaths = context.keys().map((path: string) => {
const Component = context<{ default: () => JSX.Element }>(path)
path = path.replace(/\.(.*?)\/index\.page\.tsx$/, '$1')
return {
path,
Component,
}
})
然后根据不同的 react-router 版本生成对应的 route 即可,比如上面截图是 react-router v6:
{routePaths.map(({ path, Component }) => {
return <Route path={path} element={<Component.default />} key={path} />
})}
当然,其他例子也可以看下 playgrounds 下的 app.tsx 如何处理的:
配置完成,看下效果
这样,只要你新增页面文件,改动 index.params 类型,对应的路由方法都会同步生成,避免了页面中成百上千个字符串硬编码的 url!!
如果手输或复制漏一个字符,就很容易页面 404,然后找了好久都找不到问题,但是现在直接输入路由首字母,很快就找到对应的路由跳转
一个路由有类型参数提示,是多么的舒服呀~
了解插件每个参数的作用
当然,如果你还有兴趣了解下其他参数的,但请听我娓娓道来~
pagesRootPath(必填)
也就是你页面所存放的根目录,比如我 demo 中 order,my, order/detail 等等都在 src/pages 下
那么 pagesRootPath 就可以填写为:
{
pagesRootPath: path.resolve(__dirname, 'src/pages'),
}
reactRouterVersion
上面说了,目前支持 router 5 和 6 的版本,所以可以根据你项目对应的 router 版本填写
{
reactRouterVersion: 5 | 6
}
mode
一般我们路由不是 hash 就是 browser 模式,这里默认值是 browser
{
mode: 'hash' | 'browser'
}
pageName
因为是约定式路由,所以需要一个文件名来识别哪个文件可以作为页面,这里模式值是 index.page,也就是说,如果你创建了 index.page.js 或 index.page.tsx,那么默认会将其作为路由页面
{
pageName: 'index.page' // 默认值
}
paramsName
这个需要跟 pageName 同目录,当插件扫描到对应的 pageName 下有 paramsName 的话,会将其作为页面的参数类型
{
paramsName: 'index.params' // 默认值
}
这样有个好处,就是无论这个页面是不是你写的,定义的入参后,其他人调用就不必去代码中搜索需要传啥参数了,且有类型提示, 写起来舒服到尖叫~
originHistoryModuleName
上面有说你需要在 src/history(不一定是这个目录,src 下即可)下导出你的 history,那么这里就要用到了
{
originHistoryModuleName: '@/history' // 这里的@是因为通过webpack定义了@ => src
}
historyModuleName
插件会自动在 node_modules 下为你声明一个文件,这里你可以自定义其文件名,比如你是京东的,那么可以叫 jd-history,当然,如果不填,就都默认是从~history
下导入
{
historyModuleName: 'jd-history' // 默认值是 ~history
}
exportHistoryName
historyModuleName 默认会 export 一个叫 history 的对象,如果跟你代码冲突,可以将其重命名,比如叫做 enhanceHistory
{
exportHistoryName: 'enhanceHistory' // 默认值是 history
}
这样,编译后就变成:
import { enhanceHistory } from '~history'
总结
我个人是相当不喜欢路径硬编码的,特别是我们项目有 300+页面,如果一个页面都充斥这几个字符串 url,那看起来是相当的难受,且每个人都负责不用的模块,如果我需要跳到你的页面,我还得去代码中翻找页面所需的传参,按我同事说,我们有个页面有 n 个参数,简直传了个爹在地址栏上!!
所以现在有这个插件,能更智能地根据路径提示,且调用对应方法也有参数提示,我想,这就是我想要的 history。
你呢?
原文链接:https://juejin.cn/post/7246010218412376120 作者:暴走老七