开发代码组件
代码组件是 Zion 的前端代码扩展能力。你可以使用 React 和 TypeScript 开发平台内置组件无法直接实现的界面与交互,再将组件库发布并导入 Zion 项目。
代码组件适合数据可视化、地图、复杂表单、动画和第三方前端组件集成。需要访问项目数据库或当前运行环境时,可以通过 zvm-code-context 使用宿主项目提供的 API。
开始开发
开始前需要具备 React、TypeScript 和 npm 的基础知识,并准备可用的 Node.js 开发环境。
安装 CLI
npm install --global functorz安装后可以查看当前 CLI 提供的命令:
functorz --help登录 Zion
functorz signin <用户名或邮箱> <密码>登录信息保存在本地,供创建和发布代码组件项目时使用。
创建项目
在工作目录中运行:
functorz create my-components
cd my-components
npm installcreate 会在本地创建模板项目,并在当前 Zion 账号下注册对应的代码组件项目。
了解项目结构
开发时主要关注 src/components、src/graphql、resources 和 package.json:
- index.ts
- Main.tsx
- style.module.scss
- index.ts
- package.json
- README.md
- vite.config.ts
src/components:保存组件实现并统一导出需要发布的组件。src/graphql:保存组件使用的 GraphQL Operation。resources:保存组件库和单个组件的图标、封面或画布预览图。package.json:配置组件库名称、版本、描述、分类和支持平台。README.md:说明组件库的用途和使用方式。
开发组件
每个组件使用独立目录,目录名称和组件名称均以大写字母开头。以 Counter 为例:
src/components/Counter/
├── Counter.tsx
├── index.ts
└── style.module.scssCLI 在发布时读取以下三个接口,并将字段转换为 Zion 中可配置的属性、状态和事件:
| 接口 | 用途 | 声明方式 |
|---|---|---|
${ComponentName}PropData | 从 Zion 绑定到组件的输入数据。 | 使用受支持的基础类型或数组;字段可声明为可选。 |
${ComponentName}StateData | 组件可以读写并暴露给 Zion 的状态。 | 使用 State<T>。 |
${ComponentName}Event | 组件可以触发的前端事件。 | 使用 EventHandler。 |
Props 接口用于约束 React 组件自身的参数,不是 CLI 单独生成的配置类型。
完整示例
import { EventHandler, State } from 'zvm-code-context';
export interface CounterPropData {
label: string;
step?: number;
}
export interface CounterStateData {
value?: State<number>;
}
export interface CounterEvent {
onChange?: EventHandler;
}
export interface CounterProps {
propData: CounterPropData;
propState: CounterStateData;
event: CounterEvent;
}
export function Counter({ propData, propState, event }: CounterProps) {
const value = propState.value?.get() ?? 0;
const increase = async () => {
await propState.value?.set(value + (propData.step ?? 1));
event.onChange?.();
};
return (
<button type="button" onClick={increase}>
{propData.label}: {value}
</button>
);
}在组件目录的 index.ts 中导出组件:
export * from './Counter';再在 src/components/index.ts 中注册需要发布的组件:
import { Counter } from './Counter';
export default {
Counter,
};发布解析器支持 string、number、boolean 及其数组。状态字段使用对应的 State<T>,事件字段使用 EventHandler。模板还会导出日期、时间和 JSON 等专用类型;实际可发布的类型以当前 CLI 的解析结果为准。
使用宿主 API
在组件中调用 useAppContext(),可以访问 Zion 当前页面、用户身份、GraphQL 客户端和运行时组件树。
import { useAppContext } from 'zvm-code-context';
const ctx = useAppContext();navigate
跳转到宿主应用中的路径。
ctx.navigate('/orders/10001');当前接口签名为 navigate(path)。如果目标页面使用路径参数或查询参数,应先生成完整路径再传入,不要向 navigate 传第二个配置对象。
query
使用当前宿主身份向项目后端发送 GraphQL Query 或 Mutation。
const response = await ctx.query(
`query GetPosts($limit: Int!) {
post(limit: $limit) {
id
title
}
}`,
{ limit: 10 }
);
const posts = response.data?.post;query 返回标准 GraphQL JSON 响应。调用方应同时检查 data 和 errors。
subscribe
使用当前宿主身份建立 GraphQL Subscription,并在数据更新时执行回调。
const subscription = ctx.subscribe(
`subscription ListenPost($id: bigint!) {
post_by_pk(id: $id) {
id
status
}
}`,
{ id: 100000000000001 },
(data) => {
console.log(data.post_by_pk);
}
);
// 组件卸载时停止订阅
subscription.unsubscribe();回调接收 GraphQL 响应中的 data。组件卸载或不再需要更新时,应调用 unsubscribe()。
globalData
读取宿主应用当前的全局变量对象。
const locale = ctx.globalData?.locale;globalData 是当前值,不提供 State 接口。需要通过运行时状态修改全局变量时,可以配合 discover('App') 使用。
pageData
读取或修改当前页面的页面变量对象。
const pageData = ctx.pageData.get();
await ctx.pageData.set({
...pageData,
selectedOrderId: 10001,
});pageData 是 State 对象,应使用 get() 和 set(),而不是直接修改其属性。
apolloClient
获取宿主应用正在使用的 Apollo Client。需要缓存策略、手动刷新或其他 Apollo 能力时,可以直接使用该实例。
import GetPosts from '@/graphql/getPosts.gql';
const result = await ctx.apolloClient.query({
query: GetPosts,
variables: { limit: 10 },
fetchPolicy: 'network-only',
});一般 GraphQL 请求优先使用 query 和 subscribe;只有需要 Apollo Client 特有能力时再使用 apolloClient。
getLoggedInUserAuth
异步获取当前登录用户的认证信息。
const auth = await ctx.getLoggedInUserAuth();
console.log(auth.userId);
console.log(auth.userRoles);返回对象包含 userId、userToken、userRoles,第三方登录时还可能包含 thirdPartyInfo。
userToken 是当前用户的认证凭证。不要记录到日志、发送给不受信任的第三方或持久化到公开存储中。
component
返回当前代码组件对应的运行时组件实例。可以通过它访问当前实例的状态、变量、运行时引擎和组件树能力。
const currentComponent = ctx.component;
const engine = currentComponent.engine;运行时组件对象包含平台内部状态。修改前应先确认目标字段提供的 get()、set() 或其他公开方法,不要直接覆盖对象属性。
discover
根据运行时组件 ID 查找宿主应用中的组件实例。
const app = ctx.discover('App');
const appState = (app.state || app.variables).App_CustomState;
const current = appState.get();
await appState.set({
...current,
locale: 'zh-CN',
});discover 返回运行时组件实例,因此可用字段由目标组件类型决定。调用前应确保组件 ID 存在,并对缺失状态做好处理。
配置组件库信息
发布前检查 package.json 中的以下字段:
| 字段 | 说明 |
|---|---|
name | 组件库名称。 |
description | 组件库说明。 |
version | 本次发布版本,需遵循 npm 版本格式且不能与已发布版本重复。 |
homepage | 组件库的演示或介绍地址,可为空。 |
category | 组件分类,如 container、display、input 或 others。 |
platforms | 支持的运行平台,可配置 WEB、WECHAT。 |
资源目录支持以下文件:
resources/icon.webp或resources/icon.svg:组件库图标。resources/{ComponentName}/icon.webp或.svg:组件图标。resources/{ComponentName}/cover.webp或.svg:组件封面。resources/{ComponentName}/canvas.webp或.svg:画布预览图。- 根目录
README.md:组件库说明。 src/components/{ComponentName}/README.md:单个组件说明。
调试组件
独立预览
npm run build
npm run previewpreview 默认在 6326 端口提供构建产物。
在 Zion 实时预览中调试
代码组件库至少需要成功发布一次,才能在项目中选择并替换为本地调试版本。
- 运行
npm run preview。 - 另开终端运行
npm run watch:build,监听代码变化并重新构建。 - 在 Zion 实时预览中打开调试入口,选择
6326端口。 - 修改代码后刷新实时预览。
调试微信小程序组件
Zion 的网页端和微信小程序端使用不同的运行环境。需要支持小程序时,在 platforms 中加入 WECHAT,并注意以下限制:
- 只使用微信小程序运行环境支持的浏览器和平台 API。
- 引入 Taro 相关包时,需要将对应依赖加入
vite.config.ts的共享依赖配置。 - 实时预览不能完整模拟 Taro 和微信原生能力;涉及这些能力时,应在微信开发者工具中验证。
- 控制组件库体积,避免超过微信小程序的包体积限制。
发布并导入
检查项目
functorz dryrun该命令会检查项目结构、组件声明和构建过程是否满足发布要求。
发布组件库
functorz publish发布前更新 package.json 中的 version。需要查看详细执行信息时,使用:
functorz publish --verbose在 Zion 中使用
在组件面板中选择 导入组件库,导入已发布的组件库。导入后即可将代码组件拖入画布,并配置属性、状态和事件。
发布新版本后,需要在使用该组件库的 Zion 项目中更新对应版本。
CLI 命令
| 命令 | 用途 |
|---|---|
functorz signin / signout | 登录或退出 Zion。 |
functorz create <项目名> | 创建并注册代码组件项目。 |
functorz dryrun | 检查当前项目是否可以发布。 |
functorz publish | 构建并发布新版本。 |
functorz list <项目名> | 查看已发布版本。 |
functorz update | 将旧项目迁移到当前模板结构;执行前提交或暂存本地修改。 |
functorz reinit | 将当前本地项目重新注册为新项目。 |
functorz unpublish <版本号> | 撤回指定版本。 |
functorz delete [项目名] | 删除指定项目;不传项目名时删除当前目录关联的项目。 |
reset、reinit、unpublish 和 delete 会改变项目或远端版本状态,执行前应先确认目标项目并保存本地代码。
常见问题
| 问题 | 处理方法 |
|---|---|
| CLI 提示未登录 | 重新运行 functorz signin。 |
| CLI 无法识别组件 | 检查组件目录、文件名、三个配置接口及 src/components/index.ts 的导出。 |
| 属性、状态或事件没有生成 | 检查字段是否使用 CLI 支持的类型,并运行 functorz dryrun --verbose 查看解析错误。 |
| 发布版本重复 | 更新 package.json 中的 version 后重新发布。 |
| 本地修改没有出现在实时预览中 | 确认 watch:build 与 preview 都在运行,并刷新实时预览。 |
| GraphQL 请求失败 | 同时检查响应中的 data 和 errors;接口结构参阅 Runtime API 参考。 |