常用能力
上下文对象提供了 NocoBase 的各项内置能力。不过有些能力只在 Plugin 里可用,有些只在组件里可用,有些两边都有但写法不同。先看一下总览:
下面按 namespace 逐项介绍。
API 请求(ctx.api)
通过 ctx.api.request() 调用后端接口,用法和 Axios 一致。
在 Plugin 中使用
import { Plugin } from '@nocobase/client-v2';
class MyPlugin extends Plugin {
async load() {
// 在 load() 里直接发请求
const response = await this.context.api.request({
url: 'app:getInfo',
method: 'get',
});
console.log('应用信息', response.data);
}
}
在组件中使用
import { useFlowContext } from '@nocobase/flow-engine';
export default function MyPage() {
const ctx = useFlowContext();
const handleLoad = async () => {
// GET 请求
const response = await ctx.api.request({
url: 'users:list',
method: 'get',
});
console.log(response.data);
// POST 请求
await ctx.api.request({
url: 'users:create',
method: 'post',
data: { name: 'Tao Tao' },
});
};
return <button onClick={handleLoad}>加载数据</button>;
}
搭配 ahooks useRequest
在组件中,可以用 ahooks 的 useRequest 来简化请求的状态管理:
import { useFlowContext } from '@nocobase/flow-engine';
import { useRequest } from 'ahooks';
export default function PostList() {
const ctx = useFlowContext();
const { data, loading, error, refresh } = useRequest(() =>
ctx.api.request({
url: 'posts:list',
method: 'get',
}),
);
if (loading) return <div>加载中...</div>;
if (error) return <div>请求出错: {error.message}</div>;
return (
<div>
<button onClick={refresh}>刷新</button>
<pre>{JSON.stringify(data?.data, null, 2)}</pre>
</div>
);
}
请求拦截器
通过 ctx.api.axios 可以添加请求/响应拦截器,通常在 Plugin 的 load() 中设置:
async load() {
// 请求拦截器:添加自定义请求头
this.context.api.axios.interceptors.request.use((config) => {
config.headers['X-Custom-Header'] = 'my-value';
return config;
});
// 响应拦截器:统一错误处理
this.context.api.axios.interceptors.response.use(
(response) => response,
(error) => {
console.error('请求出错', error);
return Promise.reject(error);
},
);
}
NocoBase 自定义请求头
NocoBase Server 支持以下自定义请求头,通常由拦截器自动注入,不需要手动设置:
国际化(ctx.t / ctx.i18n)
NocoBase 插件通过 src/locale/ 目录管理多语言文件,通过 ctx.t() 在代码中使用翻译。
多语言文件
在插件的 src/locale/ 下按语言创建 JSON 文件:
plugin-hello/
└── src/
└── locale/
├── zh-CN.json
└── en-US.json
// zh-CN.json
{
"Hello": "你好",
"Your name is {{name}}": "你的名字是 {{name}}"
}
// en-US.json
{
"Hello": "Hello",
"Your name is {{name}}": "Your name is {{name}}"
}
ctx.t()
在组件中通过 ctx.t() 获取翻译文本:
const ctx = useFlowContext();
// 基本用法
ctx.t('Hello');
// 带变量
ctx.t('Your name is {{name}}', { name: 'NocoBase' });
// 指定命名空间(默认命名空间是插件的包名)
ctx.t('Hello', { ns: '@my-project/plugin-hello' });
this.t()
在 Plugin 里用 this.t() 更方便——它会自动把插件的包名注入为 namespace,不需要手动传 ns:
class MyPlugin extends Plugin {
async load() {
// 自动使用当前插件的包名作为 ns
console.log(this.t('Hello'));
// 等同于
console.log(this.context.t('Hello', { ns: '@my-project/plugin-hello' }));
}
}
ctx.i18n
ctx.i18n 是底层的 i18next 实例,通常来说直接用 ctx.t() 就够了。不过如果你需要动态切换语言、监听语言变化等,可以用 ctx.i18n:
// 获取当前语言
const currentLang = ctx.i18n.language; // 'zh-CN'
// 监听语言变化
ctx.i18n.on('languageChanged', (lng) => {
console.log('语言切换为', lng);
});
tExpr()
tExpr() 用于生成延迟翻译的表达式字符串,通常在 FlowModel.define() 里使用——因为 define 是在模块加载时执行的,此时还没有 i18n 实例:
import { tExpr } from '@nocobase/flow-engine';
HelloBlockModel.define({
label: tExpr('Hello block'), // 生成 '{{t("Hello block")}}',运行时再翻译
});
更完整的国际化用法(翻译文件写法、useT hook、tExpr 等)见 i18n 国际化。NocoBase 支持的语言代码完整列表见 语言列表。
日志(ctx.logger)
通过 ctx.logger 输出结构化日志,基于 pino。
在 Plugin 中使用
import { Plugin } from '@nocobase/client-v2';
class MyPlugin extends Plugin {
async load() {
this.context.logger.info('插件加载完成', { plugin: 'my-plugin' });
this.context.logger.error('初始化失败', { error });
}
}
在组件中使用
import { useFlowContext } from '@nocobase/flow-engine';
export default function MyPage() {
const ctx = useFlowContext();
const handleLoad = async () => {
ctx.logger.info('页面加载完成', { page: 'UserList' });
ctx.logger.debug('当前用户状态', { user });
};
// ...
}
日志级别从高到低:fatal > error > warn > info > debug > trace。只有大于或等于当前配置级别的日志才会输出。
路由(ctx.router / ctx.route / ctx.location)
路由相关能力分为三部分:注册(仅 Plugin)、导航和信息获取(仅组件)。
路由注册(this.router / this.pluginSettingsManager)
在 Plugin 的 load() 中通过 this.router.add() 注册页面路由,通过 this.pluginSettingsManager 注册插件设置页:
async load() {
// 注册普通页 面路由
this.router.add('hello', {
path: '/hello',
componentLoader: () => import('./pages/HelloPage'),
});
// 注册插件设置页(会出现在「插件配置」菜单里)
this.pluginSettingsManager.addMenuItem({
key: 'my-settings',
title: this.t('My Settings'),
icon: 'SettingOutlined', // Ant Design 图标,参考 https://5x.ant.design/components/icon
});
this.pluginSettingsManager.addPageTabItem({
menuKey: 'my-settings',
key: 'index',
title: this.t('My Settings'),
componentLoader: () => import('./pages/MySettingsPage'),
});
}
详细用法见 Router 路由。完整的设置页示例见 做一个插件设置页。
注意
this.router 是 RouterManager,用于注册路由。this.pluginSettingsManager 是 PluginSettingsManager,用于注册设置页。两者跟组件里的 ctx.router(React Router,用于页面导航)不是同一个东西。
页面导航(ctx.router)
在组件中通过 ctx.router.navigate() 进行页面跳转:
const ctx = useFlowContext();
ctx.router.navigate('/hello'); // -> /v/hello
路由信息(ctx.route)
在组件中通过 ctx.route 获取当前路由信息:
const ctx = useFlowContext();
// 获取动态参数(比如路由定义为 /users/:id)
const { id } = ctx.route.params;
// 获取路由名字
const { name } = ctx.route;
ctx.route 的完整类型:
interface RouteOptions {
name?: string; // 路由唯一标识
path?: string; // 路由模板
pathname?: string; // 路由的完整路径
params?: Record<string, any>; // 路由参数
}
当前 URL(ctx.location)
ctx.location 提供当前 URL 的详细信息,类似浏览器的 window.location:
const ctx = useFlowContext();
console.log(ctx.location.pathname); // '/v/hello'
console.log(ctx.location.search); // '?page=1'
console.log(ctx.location.hash); // '#section'
ctx.route 和 ctx.location 虽然在 Plugin 里也能通过 this.context 访问到,不过插件加载时的 URL 是不确定的,拿到的值没有意义。建议在组件中使用。
视图管理(ctx.viewer / ctx.view)
ctx.viewer 提供了命令式打开弹窗、抽屉等视图的能力。在 Plugin 和组件中都可以用。
在 Plugin 中使用
import { Plugin } from '@nocobase/client-v2';
class MyPlugin extends Plugin {
async load() {
// 比如在某个初始化逻辑中打开一个弹窗
this.context.viewer.dialog({
title: '欢迎',
content: () => <div>插件初始化完成</div>,
});
}
}
在组件中使用
import { Button } from 'antd';
import { useFlowContext } from '@nocobase/flow-engine';
export default function MyPage() {
const ctx = useFlowContext();
const openDetail = () => {
// 打开弹窗
ctx.viewer.dialog({
title: '编辑用户',
content: () => <UserEditForm />,
});
};
const openDrawer = () => {
// 打开抽屉
ctx.viewer.drawer({
title: '详情',
content: () => <UserDetail />,
});
};
return (
<div>
<Button onClick={openDetail}>编辑</Button>
<Button onClick={openDrawer}>查看详情</Button>
</div>
);
}
通用方法
// 通过 type 指定视图类型
ctx.viewer.open({
type: 'dialog', // 'dialog' | 'drawer' | 'popover' | 'embed'
title: '标题',
content: () => <SomeComponent />,
});
在视图内部操作(ctx.view)
在弹窗/抽屉内部的组件中,可以通过 ctx.view 来操作当前视图(比如关闭):
import { Button } from 'antd';
import { useFlowContext } from '@nocobase/flow-engine';
function DialogContent() {
const ctx = useFlowContext();
return (
<div>
<p>弹窗内容</p>
<Button onClick={() => ctx.view.close()}>关闭</Button>
</div>
);
}
FlowEngine(this.flowEngine)
this.flowEngine 是 FlowEngine 实例,仅在 Plugin 中可用。通常用它来注册 FlowModel:
import { Plugin } from '@nocobase/client-v2';
class MyPlugin extends Plugin {
async load() {
// 注册 FlowModel(推荐按需加载写法)
this.flowEngine.registerModelLoaders({
HelloBlockModel: {
loader: () => import('./models/HelloBlockModel'),
},
});
}
}
FlowModel 是 NocoBase 可视化配置体系的核心——如果你的组件需要出现在「添加区块 / 字段 / 操作」菜单里,就需要通过 FlowModel 来包装。详细用法见 FlowEngine。
更多能力
以下能力在更高级的场景中可能用到,这里简要列出:
这些能力的详细用法可以参考 FlowEngine 完整文档。
相关链接