umi3
内置了@umijs/plugin-access
可以快速实现精细的权限控制功能,本文将会实现页面级的权限功能,并给出实现代码。
本文使用的的umi版本为3.x。
关于 @umijs/plugin-access
有src/access.ts
时启用。我们约定了
src/access.ts
为我们的权限定义文件,该文件需要默认导出一个方法,导出的方法会在项目初始化时被执行。该方法需要返回一个对象,对象的每一个值就对应定义了一条权限。
按照文档,我们将实现一个可读权限,没有权限时显示404页面。
// src/access.ts
export default function (initialState) {
const { userInfo } = initialState;
return {
read: userInfo.userName === 'abc',
};
}
关于 @umijs/plugin-initial-state
上面代码中的initialState
来源于另一个插件@umijs/plugin-initial-state
:
有src/app.ts
并且导出getInitialState
方法时启用。
按文档说明,添加如下配置:
export function getInitialState() {
return new Promise((resolve) => {
// 模拟 api请求获取用户信息
setTimeout(
() => {
resolve({
userInfo: { userName: 'abc' },
});
}, 500);
});
}
正常情况下,应该可以在src/.umi
目录下看到生成的plugin-initial-state
和plugin-access
目录,表示我们成功启用了这两个插件。
代码中使用
全局设置
一般umi项目中会有一个全局的布局文件 src/layouts/index.ts
,里面组件的props.children
为当前路由需要渲染的页面,我们可以在这里对权限进行判断,没有权限时显示404页面:
import NotFound from '@/pages/404';
import { useAccess } from 'umi';
const { children } = props;
const access = useAccess();
const authChildren = access.read ? children : <NotFound />;
把要渲染的组件换成authChildren即可。
其他页面设置
@umijs/plugin-access
还提供了Access组件:
import { useAccess, Access } from 'umi';
import NotFound from '@/pages/404';
const EditPage = props => {
const access = useAccess();
return (
<div>
<Access
accessible={access.write}
fallback={<NotFound />}
>
Edit page
</Access>
</div>
);
};
其他
这部分主要讲讲实践中遇到的问题以及解决方法。
与dva状态同步
我们一般会把用户信息存放在dva model中,如果我们在getInitialState
中调用了获取了用户信息的方法,我们可以同步到dva model中,省去了在dva中触发effect再调用一次相同的接口:
// src/access.ts
import { getDvaApp } from 'umi';
export default function (initialState) {
const { userInfo } = initialState;
// 触发reducer
getDvaApp()._store.dispatch({ type: 'common/update', userInfo });
return {
read: userInfo.userName === 'abc',
};
}
如何动态修改access中的值?
翻一下插件生成的代码找找思路:
// src/.umi/plugin-access/AccessProvider.ts
const { initialState } = useModel('@@initialState');
const access: AccessInstance = useMemo(() => accessFactory(initialState as any), [initialState]);
return React.createElement(
AccessContext.Provider,
{ value: access },
React.cloneElement(children, {
...children.props,
routes:traverseModifyRoutes(props.routes, access)
}),
);
可以看到,只是在根元素外面套了一层context,而我们要动态修改的,是这个access的值。它在一个useMemo中返回, 因此我们要修改access,只需修改initalState即可。
而修改initalState的方法很简单:
import { useModel } from 'umi';
const { initialState, setInitialState } = useModel('@@initialState');
setInitialState({ ...initialState, userInfo: { userName: 'newUser' } });
即可触发access的修改, 重新执行src/access.ts
中的逻辑。