背景
都2202年了,前端工程师们都纷纷开始卷起来了,最近在学了vue2和vue3后还想再卷一点,就继续学习react,边学边尝试搭建一个通用的后台管理的模板框架。其中涉及到细粒度的按钮权限设计,于是就有了下文。
传统的权限校验
在vue中,对于按钮级别的权限校验,可以简单的封装一个v-permission的指令实现按钮的权限控制,但是在react中好像没有指令只一个概念,所以指令是行不通的。传统react的权限校验大多都是在jsx中判断:
{hasAuth ? (<button>权限按钮</button>) : null}
这样的写法简介明了,但是这样的话对于每一个按钮,都要写一遍这一长串的东西,工作量增加不说,看起来也比较冗余。所以衍生出了高阶组件。
基于高阶组件的权限校验
使用高阶组件将原来的组件包裹起来达到权限校验的目的:
// AuthBtn
import React from 'react';
import { Button } from 'antd';
export interface AuthBtnProps {
auth?: string
}
const AuthBtn: React.FC<AuthBtnProps> = (props) => {
let { auth, children } = props;
// btnIds 应该有后台接口返回,告诉前端用户有哪些按钮权限
let btnIds = ['read', 'edit'];
let hasAuth = btnIds.includes(auth);
// 这里可以根据实际需求封装
return <>{hasAuth ? (<Button>{children}</Button>) : null}</>
};
export default AuthBtn;
这样就可以实现按钮的权限控制,但是新的问题又出现了,这里只是对于Button组件的封装,如果是其他展示组件呢。每一个组件都有一层包裹,那就要分别对多个组件封装。远远达不到我们预期的要求,那么能不能实现一次逻辑,对所有组件都适用呢。
基于多态的高阶组件权限校验
基于上一个问题的基础上,我们烤炉,能不能将我们要渲染的组件直接通过props传递过去,所以就有了下面的实现:
import React from 'react'
export interface AuthProps<E> {
as?: E
fallback?: React.ReactNode
auth?: string | boolean | (() => boolean),
children?: React.ReactNode
}
const Auth = <E extends React.ElementType>(props: AuthProps<E>) => {
const { as, fallback, auth, ...rest } = props
const Component = as ?? 'span'
let hasAuth = false
if (auth === undefined) {
return <Component {...rest} />
} else if (typeof auth === 'function') {
hasAuth = auth()
} else if (typeof auth === 'boolean') {
hasAuth = auth
} else {
// todo 自定义权限逻辑校验
hasAuth = [''].includes(auth)
}
if (hasAuth) {
return <Component {...rest} />
}
return <>{fallback ? fallback : null}</>
}
export default Auth
将要渲染的组件作为as参数传递过去,这样能实现我们要的功能
但是既然使用了typescript,那我们希望在边写组件的时候,能够根据我们as传入的值,有具体的属性提示
改造前:
改造后:
下面直接贴实现代码:
import React from 'react'
type AuthComponentProps<E extends React.ElementType> = {
as?: E
fallback?: React.ReactNode
auth?: string | (() => boolean) | boolean
}
type AuthProps<E extends React.ElementType> = AuthComponentProps<E> &
Omit<React.ComponentProps<E>, keyof AuthComponentProps<E>>
const Auth = <E extends React.ElementType>(props: AuthProps<E>) => {
const { as, fallback, auth, ...rest } = props
const Component = as ?? 'span'
let hasAuth = false
if (auth === undefined) {
return <Component {...rest} />
} else if (typeof auth === 'function') {
hasAuth = auth()
} else if (typeof auth === 'boolean') {
hasAuth = auth
} else {
// todo 自定义权限逻辑校验
hasAuth = [''].includes(auth)
}
if (hasAuth) {
return <Component {...rest} />
}
return <>{fallback ? fallback : null}</>
}
export default Auth
实现提示功能主要是ts类型的定义,这个部分细节讲起来比较晦涩。
推荐两个学习资料:
一、www.bilibili.com/video/BV1cS…
以上是我自己学习的时候看的视频和项目,觉得收益很大,一下是我边学习边搭建的框架(小生BB.jpg)