6

前置条件

  1. react,这里使用 umiJs脚手架
  2. 使用redux数据流,这里使用 dvaJS数据流
  3. 创建页面 「TestPage」,作为例子页面
  4. 创建按钮组件「MyInput」,作为例子按钮

部分代码目录

Demo/
    node_modules/
    dist/
    src/
        components/
            MyInput/
                index.js // 公共组件,input
            HOCAuth/
                index.js // 核心权限校验组件
        pages/
            test/
                index.js // 我们的测试页面
                model.js // test页面的状态
            home/
                index.js // home 页面
                model.js // home 页面的状态
        models/
            global.js // 最顶层的全局公共状态
        services/ // api等等
        utils/
        app.js // 入口函数
    router.js // 路由配置
    package.json
    ...等等

思路

如何校验权限?
我们将需要校验的(按钮或页面等)目标组件,通过一个公共组件包裹(即hoc组件),在这个hoc组件中判断目标组件的权限编码是否存在于权限表里,「若存在」则当前有权限访问,渲染。「不存在」则返回 null

那么,我们首先要储存一份权限表,供所有组件使用。再为每个组件设置对于的权限编码。

这里我们对页面的权限编码配置规则为 '数字-数字',如:'1-2'、'1-3'等等。页面级组件使用'-'连接。
按钮级的权限编码为:'1-1_1'、'1-1_2'等等。 按钮级组件使用'_'连接。
如我们当前 test 页面编码为 '1-1',且该下面也有两个按钮级组件,那么该组件编码分别为 '1-1_1'、'1-1_2'。

例子:
const auth = {
    '1-1': true,
    '1-1_1': true
} // 权限表

const testPageAuthKey = '1-1' // test页面的权限编码
const inputAuthKey = '1-1_1' // test页面下的input组件权限编码

若 auth[authKey] === true 即有权限,渲染对应组件

接下来定义状态:

每个页面有单独的 model.js(即redux中的store),用于存放该页面的状态。
global.js 存放项目最顶层的状态,为公共状态。

global 中储存一份权限表 auth,为所有按钮或页面的权限。渲染按钮或页面前,校验当前是否有权限,无权限则返回 null

例子
// global.js
const Model = {
    namespace: 'Global',
    
    state: {
        auth: {
            '1-1': true, // 表示拥有test页面权限
            '1-1_1': true, // 表示拥有test页面下的input组件权限
        } // 当前所有的权限
    },
    effects: {
        * fetchAuth({ payload, callback }, { call, put }) {
            // 登录后调用api获取当前用户所拥有的权限配置,更新掉auth
            const { auth } = yield call(apiFetchAuthFromServe, payload)
            yield put({
                type: 'save',
                payload: {
                    auth
                },
            }); 
            if (callback) callback();
        }
    },
    reducers: {
        save (state, { payload }) {
            return {
                ...state,
                ...payload,
            }
        }; 
    },
}

components/MyInput/index.js,input组件

import React, { useRef, useMemo, memo } from 'react';
import HOCAuth from '../HOCAuth';
const Input = props => {
    const {
        onChange,
        value,
        defaultValue,
    } = props;
    
    const input = useRef(null);
    
    const cur = useMemo(() => {
        if (value) return value;
        if (defaultValue) return defaultValue;
    }, [defaultValue, value]);
    
    function onInputChange (e) {
        if (onChange) onChange(e.target.value);
    }
    return (
        <input type="text" ref={ input } defaultValue={ cur } onChange={ onInputChange }/>
    );
};

export default memo(HOCAuth(Input)); // 这里使用HOCAUth包裹 Input

再看看使用:

// test/index.js test页面
import React from 'react';
import { connect } from 'dva';
import HOCAuth from '@/components/HOCAuth'
import MyInput from '@/components/MyInput'

const Test = props => {
    console.log(props)
    return (
        <div>
            <MyInput defaultValue={ 'default' } auth={ props.Global.auth } authKey={ '1-1_1' } /> // 传递authKey,表示该组件对应的权限编码
        </div>
    );
};

function mapStateToProps ({ Global, Test }) {
    return {
        auth: Global.auth, // 订阅global.js中的auth
        authKey: Test.authKey, // 当前test页面的权限编码,从model中获取
        Test, // test/model.js
        Global
    };
}

export default connect(mapStateToProps)(HOCAuth(Test))
// 这是页面级的权限校验,使用 HOCAuth 包裹 Test

// test/model.js
const Model = {
    namespace: 'Test',
    
    state: {
        authKey: '1-1', // 当前test页面的权限编码
    },
    ...略
}

接下来是我们的核心组件HOCAuth,components/HOCAuth/index.js

import React from 'react';

const HOCAuth = BaseComponent => (props) => {
    const { auth, authKey, ...others } = props;
    if (!auth || !authKey) return null;
    return (
        auth[authKey] ?
        <BaseComponent { ...others }/> : null // 过滤掉 auth与authKey
    );
};

export default HOCAuth;
// 简单不?
以上为react hooks + hoc实现的权限校验系统简单例子。欢迎方案沟通、指正

结束。


liaoxinyu
35 声望5 粉丝

前端开发.