- 我们在开发一个项目的时候,对于路由系统一般都会在原来的基础上进行二次封装。以此达到路由拦截、定制化功能、ts 类型限制 等等相关功能的目的。这篇文章分享一下在项目中路由封装的奇淫技巧。
Taro 信息
1、版本:taro3.x
2、应用端:小程序
3、技术栈:React hooks
定义一个跳转路由的方式
- $.router 是在 app.tsx 里面全局注入的,注入后,在所有文件里面都可以调用
- push 是一个跳转的方法,也可以叫其他名字
- 参数1 是跳转的路径,参数2 是带给下一个页面的参数
$.router.push('/pages/index/index', {
name: 'ajin',
age: 30,
isMarry: false,
hobbys: {
sports: ['run']
},
other: undefined,
})
push 方法的代码
class Router {
push = (url, params) => {
// 处理 params,下一步会介绍这个函数的作用
const newParams = formatRouterParams(params)
// navigateToBefore()
Taro.navigateTo(url, newParams)
}
}
- 我们知道参数传给下一个页面的时候,是以字符串的形式传过去的?name=ajin&age=30 …
- 很多情况下,我们传过去一个数字或者boolean,接受到的也是一个字符串。例如:isMarry: “false”,
- 这样有两个问题
- 1、不利于类型约束;2、判断boolean 和 undefined 不方便
- 我们这样来,在传入的每个类型前面加一个前缀。然后到解析路由参数的时候就根据前缀的类型解析成 对应的 类型。
1、number 类型 #num#111
2、boolean 类型 #bool#true
3、object 类型 #obj#{a: 1}
4、undefined 类型 #und#undefined
const formatRouterParams = (params?: Record<string, any>) => {
if(!params) return params
let newPrams = {}
Object.entries(params).forEach(([key, value]) => {
const keyType = typeof value
switch(keyType) {
case 'number' :
newPrams[key] = `#num#${value}`
break;
case 'boolean' :
newPrams[key] = `#bool#${value}`
break;
case 'object' :
newPrams[key] = `#obj#${JSON.stringify(value)}`
break;
case 'undefined' :
newPrams[key] = `#und#${value}`
break;
default :
newPrams[key] = value
}
})
return newPrams
}
- 格式化后的参数就是这样,前面都带了当前的类型
编写解析路由的 hook
- taro useRouter 会自动解析 你路径上的路由,并且以键值对的形式输出。如上图打印的数据格式一致
import { useRouter } from "@tarojs/taro"
/**
* @description 获取 格式化后的 路由参数
*/
const useRouterParams = () => {
const { params } = useRouter()
let newPrams = {}
Object.entries(params).forEach(([key, value]) => {
// taro 自带的参数
if(key === '$taroTimestamp') return
if(!value) return newPrams[key] = value
// 数字类型
if(value.startsWith("#num#")) {
newPrams[key] = parseFloat(value.replace('#num#', ''))
// boolean 类型
} else if(value.startsWith("#bool#")) {
newPrams[key] = value.replace('#bool#', '') === "true"
// 对象类型
} else if(value.startsWith("#obj#")) {
newPrams[key] = JSON.parse(value.replace('#obj#', ''))
// undefined 类型
} else if (value.startsWith('#und#')) {
newPrams[key] = undefined
}else {
newPrams[key] = value
}
})
return newPrams
}
export default useRouterParams
- 这个hook 跟据 前面定制的规则,把参数解析成对应的类型。解析出来和我们传入进去的对象是一致的。
参数问题解决了,接下来我们来解决ts 问题。
- taro-export-route-type 这个插件,会根据你app.config.ts 的路由配置生成一个路由 的 联合类型。根据配置可以生成对应路径的文件
- 声明每个页面,到下一个页面需要传递 参数的类型
export type PageParams = {
/** 首页 */
'/pages/index/index': {
/** 名字 */
name: string,
age: number,
isMarry: boolean,
hobbys: Record<string, any>
other?: string,
},
}
- push 函数的类型声明
export type Push = <T extends Pages>(url: T, params?: PageParams[T]) => viod
- 这样在选择跳转页面的时候就会有页面提示
- 后面的参数也有对应的类型
- useRouterParams 的 ts 类型
const useRouterParams = <T>(): T extends Params ? PageParams[T] : unknown => {}
- 泛型传入后,对应的类型就出来了