mcpField
A
aem-component-development

aem-component-development

github.com/headwirecom开发341 次安装32 周前更新
代码仓库

关于 aem-component-development

面向开发者和编码 Agent 的 Adobe Experience Manager 组件开发技能,涵盖组件结构、实现流程、继承方式与内容编辑规范。

主要功能

  1. 01组件文件结构与定义
  2. 02Sling Model 和 HTL 实现
  3. 03Touch UI 对话框指南
  4. 04Core Component 继承模式
  5. 05编辑占位符与创作行为

适用场景

  1. 01创建新的 AEM 组件
  2. 02扩展现有 Core Component
  3. 03配置对话框和编辑行为

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/headwirecom/aem-agent-skills --skill aem-component-development

安装后在 Claude Code 中输入/aem-component-development确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
clientlibs.md
dialogs.md
sling-models.md

详细介绍

AEM 组件开发

AEM 组件是页面内容的构建块。每个组件都是 JCR 中的一个 cq:Component 节点,将以下内容关联起来:

  1. HTL 标记 — 渲染模板(.html 文件)
  2. Sling Model — 服务端业务逻辑(Java)
  3. 对话框 — 用于编辑内容的作者界面(cq:dialog)
  4. 客户端库 — 前端行为所需的 CSS/JS
  5. 编辑配置 — 作者工具栏行为(cq:editConfig)

前置技能: 本技能假设你了解 HTL。加载 htl-scripting 技能,了解表达式语法、块语句、XSS 上下文和全局对象。

组件文件结构

标准 Maven 原型布局(Touch UI,现代 AEM):

ui.apps/src/main/content/jcr_root/apps/<project>/components/<component-name>/
├── .content.xml          ← cq:Component definition (title, group, supertype)
├── <component-name>.html ← HTL rendering script
├── _cq_dialog/
│   └── .content.xml      ← Touch UI dialog (Granite UI)
├── _cq_editConfig/
│   └── .content.xml      ← Edit behavior config
├── _cq_template/
│   └── .content.xml      ← Default content on drag-and-drop
├── _cq_design_dialog/
│   └── .content.xml      ← Policy/design dialog
└── clientlib/             ← Optional co-located clientlib
    ├── .content.xml
    ├── css.txt
    ├── js.txt
    ├── css/
    └── js/

core/src/main/java/<package>/models/
└── <ComponentName>Model.java  ← Sling Model

注意:带下划线的目录(_cq_dialog)是 JCR 节点(带冒号,如 cq:dialog)在文件系统中的表示形式。FileVault 序列化要求使用此命名方式。

组件定义(.content.xml)

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:Component"
    jcr:title="My Component"
    jcr:description="A description for authors"
    componentGroup="My Project - Content"
    sling:resourceSuperType="core/wcm/components/text/v2/text"/>
关键属性
属性用途
jcr:title组件浏览器中的显示名称
jcr:description组件浏览器中的工具提示
componentGroup组件浏览器中的分组。使用 .hidden 可隐藏
sling:resourceSuperType继承其他组件(渲染、对话框、编辑配置)
cq:isContainer若组件可包含子组件(例如 parsys),则设为 true
cq:icon组件浏览器中使用的 Coral UI 图标名称
abbreviation未设置图标时使用的 2 字符缩写

Agent 工作流:创建新组件

第 1 步:定义组件节点

创建 .content.xml,设置 jcr:primaryType="cq:Component"、标题、分组,并可选地设置 sling:resourceSuperType 以扩展现有组件。

第 2 步:创建 Sling Model

在 core/ 模块中编写 Java Model 类。参见 references/sling-models.md。

@Model(adaptables = SlingHttpServletRequest.class,
       adapters = MyComponent.class,
       resourceType = "myproject/components/mycomponent",
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
public class MyComponentImpl implements MyComponent {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    @Override
    public String getTitle() { return title; }

    @Override
    public String getDescription() { return description; }
}
第 3 步:创建 HTL 脚本
<sly data-sly-use.model="com.myproject.core.models.MyComponent"/>
<div class="cmp-mycomponent"
     data-sly-test="${model.title || model.description}">
    <h2 class="cmp-mycomponent__title"
        data-sly-test="${model.title}">${model.title}</h2>
    <div class="cmp-mycomponent__description"
         data-sly-test="${model.description}">
        ${model.description @ context='html'}
    </div>
</div>
<sly data-sly-test="${!model.title && !model.description && (wcmmode.edit || wcmmode.preview)}">
    <div class="cq-placeholder" data-emptytext="My Component"></div>
</sly>
第 4 步:创建对话框

完整的 Granite UI 字段参考,请参见 references/dialogs.md。

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
          xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="My Component"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content jcr:primaryType="nt:unstructured"
             sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs jcr:primaryType="nt:unstructured"
                  sling:resourceType="granite/ui/components/coral/foundation/tabs"
                  maximized="{Boolean}true">
                <items jcr:primaryType="nt:unstructured">
                    <properties jcr:primaryType="nt:unstructured"
                                jcr:title="Properties"
                                sling:resourceType="granite/ui/components/coral/foundation/container"
                                margin="{Boolean}true">
                        <items jcr:primaryType="nt:unstructured">
                            <columns jcr:primaryType="nt:unstructured"
                                     sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
                                     margin="{Boolean}true">
                                <items jcr:primaryType="nt:unstructured">
                                    <column jcr:primaryType="nt:unstructured"
                                            sling:resourceType="granite/ui/components/coral/foundation/container">
                                        <items jcr:primaryType="nt:unstructured">
                                            <title jcr:primaryType="nt:unstructured"
                                                   sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                   fieldLabel="Title"
                                                   name="./title"/>
                                            <description jcr:primaryType="nt:unstructured"
                                                         sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                                         fieldLabel="Description"
                                                         name="./description"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </properties>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>
第 5 步:添加客户端库(如需要)

参见 references/clientlibs.md。

第 6 步:配置编辑行为(如需要)
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="cq:EditConfig"
    cq:actions="[edit,delete,insert,copymove]"
    cq:layout="editbar"
    cq:dialogMode="floating">
    <cq:listeners jcr:primaryType="cq:EditListenersConfig"
                  afteredit="REFRESH_PAGE"
                  afterinsert="REFRESH_PAGE"/>
</jcr:root>

扩展现有组件

使用 sling:resourceSuperType 继承父组件的所有内容:

<jcr:root ...
    jcr:primaryType="cq:Component"
    jcr:title="Custom Text"
    sling:resourceSuperType="core/wcm/components/text/v2/text"
    componentGroup="My Project"/>

自动继承的内容:

  • HTL 渲染脚本(通过 Sling 资源类型层级解析)
  • 对话框(可通过 Sling Resource Merger 覆盖单个字段)
  • 编辑配置、描述和图标

要覆盖对话框字段,只需在 _cq_dialog/ 下创建需要修改的节点,并在对话框根节点上设置 sling:resourceSuperType。Sling Resource Merger 会将你的修改与父组件合并。

Core Components

始终优先扩展 AEM Core Components,而不是从头构建。它们提供:

  • 经过生产环境测试、无障碍且对 SEO 友好的标记
  • BEM CSS 类命名(cmp-<name>__<element>--<modifier>)
  • 用于 JSON/SPA 的 Sling Model 导出器
  • Style System 支持
  • Adobe Client Data Layer 集成

常见的可扩展 Core Components:

  • core/wcm/components/text/v2/text
  • core/wcm/components/image/v3/image
  • core/wcm/components/title/v3/title
  • core/wcm/components/teaser/v2/teaser
  • core/wcm/components/list/v4/list
  • core/wcm/components/container/v1/container
  • core/wcm/components/page/v3/page

编辑占位符模式

即使组件为空,也必须在编辑模式下渲染可见内容:


<sly data-sly-use.template="core/wcm/components/commons/v1/templates.html"
     data-sly-call="${template.placeholder @ isEmpty=!model.hasContent}"/>

或者使用手动回退方案:

<div class="cq-placeholder" data-emptytext="${component.properties.jcr:title}"
     data-sly-test="${(wcmmode.edit || wcmmode.preview) && isEmpty}"></div>

内容模板(_cq_template/)

将组件拖到页面上时,预先填充组件的内容节点:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="nt:unstructured"
    title="Default Title"
    description="Edit this component"/>

资源类型解析

Sling 通过内容节点上的 sling:resourceType 解析组件:

  1. 内容节点的 sling:resourceType = "myproject/components/hero"
  2. Sling 会查找 /apps/myproject/components/hero/hero.html
  3. 如果未找到,则检查 sling:resourceSuperType 链
  4. 最后回退到 /libs/ 搜索路径

资源类型为 myproject/components/hero 时的脚本解析顺序:

  1. hero.html(与组件名称匹配)
  2. html.html(与扩展名匹配)
  3. GET.html(与方法和扩展名匹配)
  4. 然后沿 sling:resourceSuperType 链向上查找

关键规则

  1. 始终使用 Touch UI(cq:dialog)——Classic UI 对话框已弃用。
  2. 优先使用 Sling Models,而非 WCMUsePojo——Sling Models 可测试、可缓存,是 AEM as a Cloud Service 的标准。
  3. 切勿修改 /libs——始终在 /apps 中创建覆盖。使用 Sling Resource Merger 进行部分覆盖。
  4. 组件没有内容时,始终渲染编辑占位符。
  5. CSS 类使用 BEM 命名:cmp-<component>__<element>--<modifier>。
  6. 设置 componentGroup,否则组件不会显示在创作 UI 中。
  7. 对话框字段值的 name 属性需使用 ./ 前缀(例如,name="./title"),以写入组件的内容节点。
  8. 从头构建之前,先扩展 Core Components。
  9. /apps 下的客户端库需要设置 allowProxy=true,才能通过 /etc.clientlibs/ 提供。

参考文件

评论

支持 Markdown