docs/docs/cn/plugin-development/client/component/index.md
在 NocoBase 中,路由挂载的页面组件就是普通的 React 组件。你可以直接用 React + Antd 来写,跟普通前端开发没有区别。
NocoBase 额外提供了:
observable + observer — 推荐的状态管理方式,比 useState 更适合 NocoBase 生态useFlowContext() — 获取 NocoBase 的上下文能力(发请求、国际化、路由导航等)一个最简单的页面组件:
// pages/HelloPage.tsx
export default function HelloPage() {
return <h1>Hello, NocoBase!</h1>;
}
写好之后,在插件的 load() 里用 this.router.add() 注册即可,详见 Router 路由。
NocoBase 推荐使用 observable + observer 来管理组件状态,而不是 React 的 useState。它的好处是:
setState基本用法:用 observable.deep() 创建响应式对象,用 observer() 包裹组件。observable 和 observer 都从 @nocobase/flow-engine 导入:
import React from 'react';
import { Input } from 'antd';
import { observable, observer } from '@nocobase/flow-engine';
// 创建一个响应式状态对象
const state = observable.deep({
text: '',
});
// 用 observer 包裹组件,状态变化时自动更新
const DemoPage = observer(() => {
return (
<div>
<Input
placeholder="输入点什么..."
value={state.text}
onChange={(e) => {
state.text = e.target.value;
}}
/>
{state.text && <div style={{ marginTop: 8 }}>你输入了:{state.text}</div>}
</div>
);
});
export default DemoPage;
效果预览:
更多用法见 Observable 响应式机制。
useFlowContext() 是连接 NocoBase 能力的入口。从 @nocobase/flow-engine 导入,返回一个 ctx 对象:
import { useFlowContext } from '@nocobase/flow-engine';
export default function MyPage() {
const ctx = useFlowContext();
// ctx.api — 发请求
// ctx.t — 国际化
// ctx.router — 路由导航
// ctx.logger — 日志
// ...
}
以下是常用能力的示例。
通过 ctx.api.request() 调用后端接口,用法跟 Axios 一致:
const response = await ctx.api.request({
url: 'users:list',
method: 'get',
});
console.log(response.data);
通过 ctx.t() 获取翻译文本:
const label = ctx.t('Hello');
// 指定命名空间
const msg = ctx.t('Save success', { ns: '@my-project/plugin-hello' });
通过 ctx.router.navigate() 进行页面跳转:
ctx.router.navigate('/some-page'); // -> /v/some-page
获取当前路由参数:
// 比如路由定义为 /users/:id
const { id } = ctx.route.params; // 获取动态参数
获取当前路由名字:
const { name } = ctx.route; // 获取路由名字
更多日志级别和用法见 Context → 常用能力。
把 observable、useFlowContext 和 Antd 组合起来,一个从后端获取数据并展示的页面组件:
// pages/PostListPage.tsx
import React, { useEffect } from 'react';
import { Button, Card, List, Spin } from 'antd';
import { observable, observer, FlowContext, useFlowContext } from '@nocobase/flow-engine';
interface Post {
id: number;
title: string;
}
// 用 observable 管理页面状态
const state = observable.deep({
posts: [] as Post[],
loading: true,
});
const PostListPage = observer(() => {
const ctx = useFlowContext();
useEffect(() => {
loadPosts(ctx);
}, []);
return (
<Card title={ctx.t('Post list')}>
<Spin spinning={state.loading}>
<List
dataSource={state.posts}
renderItem={(post: Post) => (
<List.Item
actions={[
<Button danger onClick={() => handleDelete(ctx, post.id)}>
{ctx.t('Delete')}
</Button>,
]}
>
{post.title}
</List.Item>
)}
/>
</Spin>
</Card>
);
});
async function loadPosts(ctx: FlowContext) {
state.loading = true;
try {
const response = await ctx.api.request({
url: 'posts:list',
method: 'get',
});
state.posts = response.data?.data || [];
} catch (error) {
ctx.logger.error('加载文章列表失败', { error });
} finally {
state.loading = false;
}
}
async function handleDelete(ctx: FlowContext, id: number) {
await ctx.api.request({
url: `posts:destroy/${id}`,
method: 'post',
});
loadPosts(ctx); // 刷新列表
}
export default PostListPage;
useFlowContext 提供的完整能力——见 Context 上下文