Amis 渲染器自定义组件
解读
国内中高级 PHP 面试里,前端低代码方案 Amis 被越来越多的公司(尤其是 ToB SaaS、内部运营后台)采用。面试官问“Amis 渲染器自定义组件”,并不是想让你背文档,而是考察三条线:
- 你是否理解 Amis 的渲染管线(JSON → Schema → React 组件 → DOM)。
- 你是否能把 PHP 后端业务逻辑(字段、权限、数据格式)无缝对接到前端自定义组件里。
- 你是否具备“造轮子”能力:当官方组件无法满足业务(例如:SKU 多维矩阵、电子签名、低代码地图选点)时,能否用官方推荐的 Custom 或 SDK 方式扩展,并保证后续可维护、可复用、可单元测试。
一句话:面官想看你在 PHP 主导的项目里,如何“优雅地”把后端数据 + 前端自定义组件打通,而不是简单写个 React 组件了事。
知识点
- Amis 渲染器架构
- 三层概念:JSONSchema → Renderer(React 高阶组件) → 真实 DOM。
- 内置渲染器注册表:RendererStore,通过 addSchemaFilter、registerRenderer 注入。
- 自定义组件三种官方姿势
- Custom 微件:在 JSON 里直接写
type:"custom",内嵌 React 组件,适合一次性场景。 - SDK 注册:使用 amis.embed、amis.registerRenderer,适合多项目复用。
- 源码级扩展:fork amis,在 /src/renderers 新增 Renderer,重新 build,适合深度 UI 统一。
- Custom 微件:在 JSON 里直接写
- 与 PHP 后端的数据闭环
- 初始数据:PHP 在
data属性里注入api返回的rows、permissions。 - 事件回调:自定义组件通过
onAction触发 amis 动作,再调用 PHP 提供的ajax接口。 - 字段校验:PHP 返回
validations规则,自定义组件内部用amis.FormItemControl统一校验。
- 初始数据:PHP 在
- 性能与安全
- 使用 React.memo、useDeepCompareMemo 避免重复渲染。
- 自定义组件内部禁止直接操作 DOM,统一走 amis 提供的
env.fetcher做请求,方便接入 PHP 的 CSRF Token、JWT。
- 工程化
- 前端用 Vite/Rollup 把自定义组件打成
custom-renderer.esm.js,PHP 通过asset()引用。 - 单元测试:@testing-library/react + jest,mock amis 上下文。
- 文档:Storybook 自动生成示例,PHP 注释用 swagger-php 同步给前端。
- 前端用 Vite/Rollup 把自定义组件打成
答案
下面给出一个“国内面试现场”最常用、最可落地的回答模板,可直接口述或白板手写。
场景:电商后台商品模块,官方 Select 无法完成“SKU 多维矩阵”交互,需要自定义组件。
步骤 1:PHP 后端先给出接口约定
// ProductController.php 返回属性与属性值
public function skuMeta(int $categoryId): JsonResponse
{
$attrs = Attr::where('category_id', $categoryId)
->with('values:id,attr_id,name')
->get(['id','name']);
return response()->json([
'status' => 0,
'data' => $attrs
]);
}
步骤 2:前端注册自定义渲染器(SDK 方式,不改动 amis 源码)
// sku-matrix.jsx
import React, {useEffect, useState} from 'react';
import {registerRenderer} from 'amis';
function SkuMatrix({value, onChange, data, env}) {
const [matrix, setMatrix] = useState(value || []);
useEffect(() => {
// 初始拉取 PHP 给的属性元数据
env.fetcher(data.skuMetaApi).then(res => {
// 构造矩阵
setMatrix(buildMatrix(res.data));
});
}, []);
const handleCellChange = (cell) => {
const newVal = matrix.map(m => m.key === cell.key ? cell : m);
setMatrix(newVal);
onChange(newVal); // 把最新矩阵回传给 amis 表单
};
return (
<div className="sku-matrix">
{matrix.map(r => (
<div key={r.key}>
<span>{r.label}</span>
<input value={r.price} onChange={e => handleCellChange({...r, price: e.target.value})}/>
</div>
))}
</div>
);
}
// 关键:注册到 amis
registerRenderer({
type: 'sku-matrix',
name: 'sku-matrix',
autoVar: true, // 让 amis 自动注入 data、onChange、env
component: SkuMatrix
});
步骤 3:PHP 返回的 JSON 里直接使用
$pageSchema = [
'type' => 'page',
'data' => [
'skuMetaApi' => route('product.sku.meta', ['categoryId' => $categoryId])
],
'body' => [
'type' => 'form',
'api' => route('product.save'),
'controls' => [
[
'type' => 'sku-matrix',
'name' => 'skuList', // 表单字段名
'label' => 'SKU矩阵'
]
]
]
];
return response()->json($pageSchema);
步骤 4:性能与可维护
- 用 React.memo 包裹 SkuMatrix,避免父级表单任何改动都重渲染。
- 所有请求走
env.fetcher,自动携带 PHP 注入的X-CSRF-Token。 - 把 sku-matrix 打成一个独立包,通过 Composer 的
composer-merge-plugin把前端资源版本锁在composer.lock,实现 PHP 项目一键升级。
这样回答,面官能清晰听到:
- 你理解 amis 渲染管线。
- 你知道 PHP 如何给前端“喂数据”。
- 你考虑了性能、安全和工程化,符合企业级落地标准。
拓展思考
- 动态权限:PHP 在后端把字段级权限
visible= false注入 JSON,自定义组件内部通过amis.util.filter做二次过滤,避免前端硬编码权限。 - 微前端场景:当后台用 Laravel + amis,前台用 Next.js,可以把自定义组件打成 Web Component,通过
amis.registerRenderer({component: 'sku-matrix-webc'})实现技术栈隔离。 - 低代码平台化:把 SkuMatrix 抽象成 JSON 配置,例如
{"type":"sku-matrix","attrsApi":"xxx","priceUnit":"CNY"},让运营人员通过 PHP 管理的元数据直接生成新组件,无需发版。 - 单元测试覆盖率:PHP 用 Codeception 测接口,前端用 React Testing Library 测组件,合并到 GitLab CI,MR 阶段必须 ≥ 90%,保证自定义组件不会成为“祖传代码”。
- 服务端渲染:如果后台对 SEO 有要求,可在 PHP 里用
v8js扩展或 Node 子进程做同构渲染,把 amis JSON 直出 HTML,自定义组件需要兼容ssr: true模式。
掌握以上思路,无论面官如何追问“性能优化”“权限安全”“跨框架复用”,你都能把话题拉回 PHP 主导的工程实践,体现全栈掌控力。