AEM集成SPA(一)架构及概念梳理
前言
这篇文章主要是对AEM前沿技术文档的研究,主要整理了Adobe Experience Management官方文档对于集成SPA框架的各类文档,并加以略微梳理,本篇文章适合有一定AEM开发经验和SPA框架开发经验的人阅读。
这篇文章内容主要是概念性的内容,在对英文文档翻译的过程中,部分深奥的概念我只能借助图来理解,因此翻译可能会有点小出入。读者只需要略微了解即可,下一篇文章将给出AEM集成React框架的完整教程。
文章目录
1 名词解释
简写 | 完整名称 | 简介 |
---|---|---|
SPA | Single-Page Application | 单页应用程序,AEM官方文档解释 英文版 |
MPA | Mulitiple-Page Application | 多页应用程序,比较参考:Ref |
SPA Editor | SPA Editor |
由AEM提供的一层中间库,沟通SPA框架与AEM Editor的桥梁 |
CMS | Content Management System | 内容管理系统 |
WYSIWYG | What You See Is What You Get | 所见即所得 |
WCM | Web Content Management | 网站内容管理 |
SSR | Server-Side Rendering | 服务端渲染 |
MPA和SPA对比
多页应用 | 单页应用 |
---|---|
一个项目中有多个完整的的HTML文件 | 一个项目中只有一个完整的HTML页面(index.html) |
可以使用超链接、js实现页面间的跳转 | 可以使用改进后的超链接、js实现模板页面间的切换 |
传统的页面跳转是同步请求:在服务器生成响应内容时,客户端是一片空白 | 模板页面间的切换属于经典的异步请求:直到下一个模板页面来到,前一个模板页面到来,前一个模板页面才从当前DOM树上删除 |
页面跳转时,前一个DOM树无用了,需要从浏览器中全部删除,然后等待下一个DOM树的到来 | index.html到来时,浏览器创建一个DOM树。所有的请求都是xhr,到来时只需要作为一个片段挂载在当前DOM树——浏览器从始至终只有一个DOM树 |
当网速比较慢时(尤其是移动端应用中时),用户体验极为不好! | 不会出现一片惨白的情形,浏览体验好。 |
页面切换时,DOM被完整的删除,不可能有过场动画! | 页面切换的本质是DIV的轮换,可以很容易实现漂亮的过场动画。 |
2 AEM混合架构
详细介绍见:aem-hybrid-architecture-wp-1-18-19.pdf
2.1 架构对比
1)传统CMS架构:
2)典型的无中心式架构:
3)AEM的混合式架构:
2.2 Content Fragment
1)AEM中的content fragment模型示例:
2)Author编辑CF:
3)创建CF:
4)CF的具体用例:
2.3 Experience Fragments
1)XF在不同设备上的显示变种:
2.4 多通道内容交付(多设备)
Multichannel content delivery in Experience Manager:简单来说就是应对多种设备的内容交付,如Web端、Mobile端等。
AEM中,主要通过两种形式输出Web Content:HTML和JSON。
1)AEM Sites、Screens、Forms中提供HTMl内容,这里面通过WCM components实现组件化
2)AEM content services framework将内容作为JSON交付,它是在Apache Sling Model Exporter的基础上构建,并为实现其接口的组件提供自己的JSON模式。
Content Services依旧利用了AEM的Templates、Pages、Components,但通过JSON的形式代替了HTML的输出:
- Templates:通过强制哪些组件必须在页面上以及作者可以添加哪些组件来定义下游使用者的*JSON模式和结构。
- Pages:定义传递JSON的HTTP API端点。 开发人员可以使用一个或多个页面来定义HTTP API,每个页面都充当HTTP端点,以传递逻辑上的JSON数据集。
- Components:是允许Author选择和配置要公开的数据的数据单位。 与HTML用例类似,作者可以配置组件以在Experience Manager中聚合和引用其他内容。
2.5 总结
HTML交付 vs. JSON交付:
4 SPA官方文档整理和翻译
4.1 SPA简介和演练
主要内容:
- 什么是SPA?
单页应用程序(SPA)与传统页面不同,它在客户端呈现,主要由Javascript驱动,它依赖Ajax调用加载数据和动态更新页面。 大多数或所有内容在单页加载中检索一次,并根据用户与页面的交互情况按需异步加载其他资源。
这减少了页面刷新的需求,并为用户提供了无缝、快速的体验,更像原生的App体验。
AEM SPA编辑器允许前端开发人员创建可集成到AEM站点的SPA,使内容作者能够像编辑任何其他AEM内容一样轻松地编辑SPA内容。
- 为什么要做SPA?
SPA更快、更流畅,更像本机应用程序,它不仅对网页的访客非常有吸引力,而且对营销人员和开发人员也极具吸引力,因为SPA的工作方式。
- SPA的工作原理
- 通过Github上的We.Retail项目演练示例
- 继续以上面的项目为例,介绍SPA Apps和SPA Editor
- 通过项目的react页面展示SPA页面加载特性:完全JS驱动的页面,并且支持AEM的Authoring
- 通过Chrome控制台查看SPA页面源码发现只有骨架
- 通过Chrome网络监控工具查看页面的加载情况,发现加载的都是xxx.model.json
- 编辑器交互介绍,主要属性:data-cq-data-path,这一段内容还是难以理解
4.2 SPA教程导航
React
- Getting Started with React and AEM SPA Editor
- GitHub: WKND Events SPA Editor Project 每章教程都有对应Github的代码
- AEM中SPA快速入门——React Getting Started with SPAs in AEM - React
其它信息如Angular相关的可以见以下导航:
4.3 AEM SPA React快速入门
文档:
AEM中SPA快速入门——React Getting Started with SPAs in AEM - React
简要总结:
- React工程package.json需要的依赖,当然React相关的依赖不可少,这里没有列出
"dependencies": {
"@adobe/cq-react-editable-components": "~1.0.3",
"@adobe/cq-spa-component-mapping": "~1.0.3",
"@adobe/cq-spa-page-model-manager": "~1.0.4"
}
- aem-clientlib-generator 工具能够自动创建client library,属于开发时依赖(devDependencies),具体配置见文档
- NPM的Script脚本
"build": "webpack && clientlib --verbose"
- 工程结构,其实和传统React差不多,入口index.js有固定的写法,见文档
- 映射AEM的Component
import {MapTo} from '@adobe/cq-react-editable-components';
......
MapTo('my-react-app/components/content/image')(Image, ImageEditConfig);
- 导出 Editable Content
import React, { Component } from 'react';
import { MapTo } from '@cq/cq-react-editable-components';
...
const EditConfig = {...}
class PageClass extends Component {...};
...
export default MapTo('my-react-app/react/components/structure/page')(PageClass, EditConfig);
- 映射Page
import {Page, MapTo, withComponentMappingContext } from "@adobe/cq-react-editable-components";
...
class AppPage extends Page {
...
}
MapTo('my-react-app/components/structure/page')(withComponentMappingContext(AppPage));
- 还有一个withModel的函数,用于App.js,是React的入口js
import {Page, withModel } from '@adobe/cq-react-editable-components';
...
class App extends Page {
...
}
export default withModel(App);
4.4 实现一个React组件
源码下载:Github上的We.Retail项目
- 本小节代码见React子模块:react-app/src/components/Weather.js
Weather API Key,可以在官方获取,需要在注册:https://home.openweathermap.org/api_keys
0fb587df6819cb63c0a2cdf9c3f84933
本节摘要:
1)安装依赖:
npm config set registry https://registry.npm.taobao.org
npm install
2)Weather.js分析和注释,注意文档和项目代码有略微不同
//导入依赖模块,ReactWeather来自第三方的开源组件
import React, {Component} from 'react';
import ReactWeather from 'react-open-weather';
import {MapTo} from '@adobe/cq-react-editable-components';
//导入样式
require('./Weather.css');
//通过EditConfig定义城市,检查城市是否为空,若为空,则定义了空值
const WeatherEditConfig = {
emptyLabel: 'Weather',
isEmpty: function(props) {
return !props.city || props.city.trim().length < 1;
}
};
//Weather组件扩展了类 Component 并提供React Open Weather组件的NPM使用文档中定义的所需数据,并渲染该组件。
class Weather extends Component {
render() {
if (this.props.apiKey) {
return <ReactWeather key={'react-weather' + Date.now()}
forecast="today" apikey={this.props.apiKey} type="city" city={this.props.city} />
} else {
return <div className="rw-box weather-error">No api key defined! Weather data cannot be retrieved</div>
}
}
}
//MapTo函数将React组件与相应的AEM组件关联,以便在SPA编辑器中编辑
MapTo('we-retail-journal/components/weather')(Weather, WeatherEditConfig);
4.5 深入SPA
AEM SPA Editor aem-spa-editor.html SPA Editor概述,建议初学者从这开始
SPA Editor SDK Deep Dive - Part 1 - React 深入SPA Editor - React
4.6 SPA Editor概述*
简要总结:
- SPA Editor是SPA框架(React)和AEM Authoring后端的桥梁
- 整个SPA Editor相关的概念和流程简述
SPA框架(React)开始 -> Page Model -> 从Page Editor获取JSON Messaging -> GET请求从Sling Model Exporter获取JSON数据/POST请求Default POST Servlet进行数据更新 -> 获取JCR的资源/后台JCR持久化
- 介绍SPA Editor的详细工作原理和流程
几个重要的概念:
- Page Model,是SPA Editor的核心部分之一,在SPA框架(React)中进行协调,通过JSON消息和Page Editor进行交互,并负责传递消息给Orchestrator
- Orchestrator,也是SPA Editor的核心部分之一,负责将React组件与AEM组件映射,最后实例化这些组件
- Page Editor,也就是SPA Editor,负责和AEM服务器通信
SPA Editor的工作流程:
- 首先加载出AEM SPA Editor
- 然后将SPA框架(React)加载到一个单独的iFrame中
- SPA框架(React)请求JSON内容并在客户端(前端页面)渲染这些React组件
- SPA Editor能够检测到这些被渲染的React组件,然后为每个React组件生成editable overlays层(cq-react-editable-components)
- Author可以像传统方式一样对这些Editable Overlays包裹的React组件进行各种编辑,如点击将出现edit toolbar
- Author编辑完,SPA Editor向服务器发送POST请求将数据持久化
- SPA Editor向服务器请求更新后的JSON数据,并将这些数据通过DOM Event传递给SPA框架(React)
- SPA框架(React)监听到改变,对页面组件重新渲染,更新DOM
SPA Editor更加详细的流程,具体流程描述参考文档:
Authoring的详细工作流程,具体流程文字描述见文档:
4.7 为AEM开发SPAs
有用内容:
- 在AEM中开发SPA的原则
- SPA前端开发人员的说明以及粗略的步骤,如几个API的介绍
- AEM架构和SPA图,见文档
4.8 SPA Blueprint*
简介:
- AEM SPA 能力目前暂时只支持React和Angular
- 两个AEM提供的NPM的库:
PageModelManger
和ComponentMapping
主要作用就是解释了SPA框架和AEM存储库中的数据的语义的模型结构 - PageModelManager:@adobe/cq-spa-page-model-manager ,AEM为SPA提供的数据模型管理器,抽象了JSON结构的索引和管理使之能对应AEM具体的content结构,并且负责同步SPA框架通知它何时进行页面重新渲染,流程图见文档
- ComponentMapping:同样是AEM的NPM包,主要存储了前端组件并为AEM resource types和SPA的前端组件提供了映射方式,这样,在解析JSON模型时,可以动态的解析组件。关于SPA的Dynamic Model到Component的映射可参考:Dynamic Model to Component Mapping for SPAs
- 每个SPA前端框架必须实现框架特定的第三层,这个第三层的库负责与底层库交互
一般概念:
-
Page Model,页面模型,用于映射和实例化SPA组件
-
Meta Fields,元字段,页面模型通过Sling Model API的JSON Model Exporter导出JSON,下列field是导出的用于和底层库交互的数据原型:
-
:type
:AEM的资源类型(默认等同于resourceType) -
:children
:当前资源的Children子资源(并不是当前资源的inner content,能够在代表Page资源的items中找到) -
:hierarchyType
:资源的层次结构类型,PageModelManager目前仅支持page类型 -
:items
:当前resource的child content,对于嵌套的结构,仅表示表层 -
:itemsOrder
:children的ordered list(排序后的列表),返回的JSON map对象并不能保证所有fields的顺序。通过同时使用Map和当前的array能够使API的消费者具有两者的优点 -
:path
:item的content path,呈现在page resource的items中
-
-
Framework-Specific Module,AEM提供的SPA Editor的NPM特殊包,负责聚合、导出基础modules、services、components,主要包含以下两个NPM libraries
React的实现:npm module: @adobe/cq-react-editable-components
SPA Editor的主要服务和组件(Main Services and Components):
-
The Model Provider,模型提供者,Project components必须有访问Model Provider的权限,模型提供者必须注册到 [PageModelManager],这样才能为被委托的组件接收、传递更新数据;被委托的组件默认会携带一个叫**“cqModel”**的属性模型。
-
The Component HTML Decorator,组件的HTML装饰器,负责装饰每个组件实例的元素的外部HTML,并且包含各种Page Editor所需要的data attributes和class names。主要包含:
- Component Declaration,组件声明,属性如下:
- data-cq-data-path,相对
jcr:content
的Resource路径
- data-cq-data-path,相对
- Editing Capability Declaration and Placeholder,编辑功能声明和占位符:
- cq-placeholder:Class name,为空组件标识占位符
- data-emptytext:当组件实例为空时,将覆盖显示此label
注意: 每个组件必须扩展当它为空时的占位符功能
- Component Declaration,组件声明,属性如下:
-
Container,容器,是一个包含并渲染子组件的的组件。它主要对Model中的以下属性进行迭代遍历:
:itemsOrder
、:items
、:children
。
Container能够动态的获取child components(通过 ComponentMapping 库的store),然后,Container使用The Model Provider功能扩展child components,并最终将其实例化。 -
Page,页面组件,是Container的扩展。文档中的职责描述与Container一样。
-
Responsive Grid,响应式网格组件,也是一种Container。能够根据模型中的具体类名(class name)对外部HTML动态的装饰。响应式网格组件将预先被映射到与AEM对应的组件,因为其复杂且很少会被自定义。Responsive Grid主要包括:
-
Specific Model Fields,特定的Model域
- gridClassNames,网格grid的class name属性
- columnClassNames,响应式column列的class name属性
-
Placeholder of the Reponsive Grid,响应式网格的占位符
SPA组件将映射到图形容器(如响应式网格),并且在创作内容时必须添加虚拟子占位符。 当页面编辑器创作SPA内容时,该内容会使用iframe嵌入到编辑器中,并且该属性会 data-cq-editor 添加到该内容的文档节点。 当属 data-cq-editor 性存在时,容器必须包含HTMLElement,以表示将新组件插入页面时作者与之交互的区域。例如:
<div data-cq-data-path={"path/to/the/responsivegrid/*"} className="new section aem-Grid-newComponent"/> 示例中使用的类名称当前为页面编辑器所必需的。 "new section" : 指示当前元素是容器的占位符 "aem-Grid-newComponent" : 为布局创作规范化组件
-
Component Mapping,组件映射。
底层的 Component Mapping Library及其函数 MapTo 可以被封装和扩展,为当前component class提供编辑配置相关的功能。
const EditConfig = {//通过EditConfig扩展组件为空时的功能 emptyLabel: 'My Component', isEmpty: function() { return !this.props || !this.props.cqModel || this.props.cqModel.isEmpty; } }; class MyComponent extends Component { render() { return <div className={'my-component'}></div>; } } MapTo('component/resource/path')(MyComponent, EditConfig);
-
-
Page Editor的约束配置,project components必须生成如下属性与editor交互:
data-cq-data-path:PageModel提供的组件路径,如:“root/responsivegrid/image”,不应将此属性添加到页面。
要让Page Editor解释组件,project component必须遵守:
- 提供与AEM组件实例关联的需要属性
- 组件为空时的占位符实现
- 提供支持Assets拖放的class names
举例典型的HTML元素结构:
<div data-cq-data-path="/content/page"> <div class="aem-Grid aem-Grid--12 aem-Grid--default--12"> <div class="aem-container aem-GridColumn aem-GridColumn--default--12" data-cq-data-path="/content/page/jcr:content/root/responsivegrid"> <div class="aem-Grid aem-Grid--12 aem-Grid--default--12"> <div class="cmp-image cq-dd-image aem-GridColumn aem-GridColumn--default--12" data-cq-data-path="/root/responsivegrid/image"> <img src="/content/we-retail-spa-sample/react/jcr%3acontent/root/responsivegrid/image.img.jpeg/1512113734019.jpeg"> </div> </div> </div> </div> </div> 响应式网格元素包含前缀为 aem-Grid-- 响应式列元素包含前缀为 aem-GridColumn-- 响应式网格(也是父网格的列)被封装,例如两个以前的前缀不出现在同一元素上 与可编辑资源对应的元素带有 data-cq-data-path 属性。 请参阅 此文档的“与页面编辑器 ”部分签订合同。
-
Navigation and Routing,导航和路由。
前端SPA开发需要实现Navigation组件并和AEM的navigation组件映射
- PageModelManager 中的 ModelRouter模块负责预加载并提供Path路径关联的Model的访问
4.9 SPA Page Component
1)Page Model Management的介绍不多说了,见文档。
- 注意clientlib中添加 cq.authoring.pagemodel.messaging
2)Communication Data Type,通信数据类型,主要通过HTML attribute:data-cq-datatype,指定JSON后,将通过GET请求Sling Model,具体见文档。
3)Meta Properties,元属性
- cq:wcmmode:编辑器的WCM模式(例如,页面、模板)
- cq:pagemodel_root_url:应用程序的根模型的URL
- cq:pagemodel_router:启用或禁用PageModelManager中的ModelRouter路由
- cq:pagemodel_route_filters:路由过滤器,通过逗号分割或者正则匹配
4)Page Editor Overlay Synchronization,编辑器覆盖同步,使用 cq.authoring.page
category
5)Sling Model JSON Exported Structure Configuration
启用路由功能后,假定SPA的JSON导出包含应用程序的不同路由,这要归功于AEM导航组件的JSON导出。 AEM导航组件的JSON输出可以通过以下两个属性在SPA的根页面内容策略中进行配置:
- structureDepth : 与导出的树的深度对应的编号
- structurePatterns : 与要导出的页面对应的正则表达式数组的正则表达式
这可以在中的SPA示例内容中显示 /conf/we-retail-journal/react/settings/wcm/policies/we-retail-journal/react/components/structure/page/root 。
4.10 动态Model - Compoent映射
1)ComponentMapping Module,在SPA Blueprint中介绍过了,一个NPM包,映射SPA和AEM的组件,每个model需要有一个 :type
属性,表示AEM的resourceType。SPA组件装载后,将依赖ComponentMapping接收AEM的模型数据来渲染自己。
2)Model-Driven Single Page Application,模型驱动的单页应用程序
- 前端SPA组件将自己注册到 Component Mapping Store .
- 一旦 Container 获取到来自 Model Provider 提供的model,便会迭代遍历它的model内容(:items)
- 对于一个Page,它的children(:children)将最先从 Component Mapping Store 获取一个component class,然后实例化它
3)App Initialization,程序初始化流程,建议看原文图
- 每个 Model Provider 都初始化自己并监听对与其内部组件相对应的模型片段所做的更改。
- 必须 以初始化流所表示的形式 进行初始化 SPA Blueprint 。
- 存储后,PageModelManager 将返回应用程序的完整模型。
- 然后,此模型将传递给应用程序的前端根 Container 组件。
- 模型段最终传播到每个单独的子组件。
4.11 SPA Model Routing
1)前端SPA框架自己拥有路由的实现
2)PageModelManager.getData() 的使用,没怎么看懂
3)ModelRouter介绍
4)手动路由和自动路由,示例项目 We.Retail Journal 中React方式是自动路由
5)路由的一些配置约定
4.12 SPA与AEM平台集成教程
要了解如何将您的SPA与Adobe Experience Platform Launch集成,请参阅此知识库文章和教程 launch-reference-architecture-SPA-tutorial-implement.html,其中将指导您完成Launch设置并实施使用Angular或React构建的Experience Cloud。
4.13 SPA和SSR
主要就是对AEM中使用SSR的建议和例子,暂不深入。
4.14 SPA JS API*
以下是AEM SPA Editor SDK(NPM modules)API参考:
推荐阅读