React应用程序 react-app
[AEM Headlessas a Cloud Service]{class="badge informative"}
示例应用程序是探索Adobe Experience Manager (AEM)的Headless功能的绝佳方法。 此React应用程序演示了如何通过AEM GraphQL API,使用持久化查询来查询内容。 适用于JavaScript的AEM Headless Client用于执行为应用程序提供支持的GraphQL持久查询。
查看 GitHub上的源代码
A 完整的分步教程 描述此React应用程序的构建方式。
前提条件 prerequisites
应在本地安装以下工具:
AEM要求
React应用程序可与以下AEM部署选项配合使用。 所有部署都需要 WKND站点v3.0.0+ 即将安装。
- AEMas a Cloud Service
- 使用进行本地设置 AEM CLOUD SERVICE SDK
- 需要 JDK 11
React应用程序旨在连接到 AEM发布 但是,如果在React应用程序的配置中提供身份验证,则它可以从AEM Author源内容。
使用方法
-
克隆
adobe/aem-guides-wknd-graphql
存储库:code language-shell $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
-
编辑
aem-guides-wknd-graphql/react-app/.env.development
文件和设置REACT_APP_HOST_URI
指向目标AEM。如果连接到作者实例,请更新身份验证方法。
code language-plain # Server namespace REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com #AUTH (Choose one method) # Authentication methods: 'service-token', 'dev-token', 'basic' or leave blank to use no authentication REACT_APP_AUTH_METHOD=basic # For Bearer auth, use DEV token (dev-token) from Cloud console REACT_APP_DEV_TOKEN=dev-token # For Service toke auth, provide path to service token file (download file from Cloud console) REACT_APP_SERVICE_TOKEN=auth/service-token.json # For Basic auth, use AEM ['user','pass'] pair (eg for Local AEM Author instance) REACT_APP_BASIC_AUTH_USER=admin REACT_APP_BASIC_AUTH_PASS=admin
-
打开终端并运行以下命令:
code language-shell $ cd aem-guides-wknd-graphql/react-app $ npm install $ npm start
-
新浏览器窗口应加载到 http://localhost:3000
-
应用程序上应显示WKND引用站点中的冒险列表。
代码
以下摘要介绍了React应用程序的构建方式、它如何连接到AEM Headless以使用GraphQL持久查询检索内容,以及这些数据的呈现方式。 完整代码可在上找到 GitHub.
持久查询
遵循AEM Headless最佳实践,React应用程序使用AEM GraphQL持久查询来查询冒险数据。 该应用程序使用两个持久查询:
wknd/adventures-all
持久查询,该查询返回在AEM中使用一组删节的属性进行的所有冒险。 此持久查询驱动初始视图的冒险列表。
# Retrieves a list of all Adventures
#
# Optional query variables:
# - { "offset": 10 }
# - { "limit": 5 }
# - {
# "imageFormat": "JPG",
# "imageWidth": 1600,
# "imageQuality": 90
# }
query ($offset: Int, $limit: Int, $sort: String, $imageFormat: AssetTransformFormat=JPG, $imageWidth: Int=1200, $imageQuality: Int=80) {
adventureList(
offset: $offset
limit: $limit
sort: $sort
_assetTransform: {
format: $imageFormat
width: $imageWidth
quality: $imageQuality
preferWebp: true
}) {
items {
_path
slug
title
activity
price
tripLength
primaryImage {
... on ImageRef {
_path
_dynamicUrl
}
}
}
}
}
wknd/adventure-by-slug
持久查询,返回一次冒险slug
(唯一标识冒险的自定义属性)和一组完整的属性。 此持久查询为冒险详细信息视图提供支持。
# Retrieves an Adventure Fragment based on it's unique slug.
#
# Required query variables:
# - {"slug": "bali-surf-camp"}
#
# Optional query variables:
# - {
# "imageFormat": "JPG",
# "imageSeoName": "my-adventure",
# "imageWidth": 1600,
# "imageQuality": 90
# }
#
# This query returns an adventure list but since the the slug property is set to be unique in the Content Fragment Model, only a single Content Fragment is expected.
query ($slug: String!, $imageFormat:AssetTransformFormat=JPG, $imageSeoName: String, $imageWidth: Int=1200, $imageQuality: Int=80) {
adventureList(
filter: {slug: {_expressions: [{value: $slug}]}}
_assetTransform: {
format: $imageFormat
seoName: $imageSeoName
width: $imageWidth
quality: $imageQuality
preferWebp: true
}) {
items {
_path
title
slug
activity
adventureType
price
tripLength
groupSize
difficulty
price
primaryImage {
... on ImageRef {
_path
_dynamicUrl
}
}
description {
json
plaintext
html
}
itinerary {
json
plaintext
html
}
}
_references {
... on AdventureModel {
_path
slug
title
price
__typename
}
}
}
}
执行GraphQL持久查询
AEM持久查询通过HTTPGET执行,因此, 适用于JavaScript的AEM Headless客户端 用于 执行持久的GraphQL查询 针对AEM将冒险内容加载到应用程序中。
每个持久查询都有一个对应的React useEffect 挂入 src/api/usePersistedQueries.js
,异步调用AEM HTTPGET持久查询端点并返回冒险数据。
每个函数依次调用 aemHeadlessClient.runPersistedQuery(...)
,执行持久的GraphQL查询。
// src/api/usePersistedQueries.js
/**
* React custom hook that returns a list of adevntures by activity. If no activity is provided, all adventures are returned.
*
* Custom hook that calls the 'wknd-shared/adventures-all' or 'wknd-shared/adventures-by-activity' persisted query.
*
* @returns an array of Adventure JSON objects, and array of errors
*/
export function useAdventuresByActivity(adventureActivity, params = {}) {
...
let queryVariables = params;
// If an activity is provided (i.e "Camping", "Hiking"...) call wknd-shared/adventures-by-activity query
if (adventureActivity) {
// The key is 'activity' as defined in the persisted query
queryVariables = { ...queryVariables, activity: adventureActivity };
// Call the AEM GraphQL persisted query named "wknd-shared/adventures-by-activity" with parameters
response = await fetchPersistedQuery("wknd-shared/adventures-by-activity", queryVariables);
} else {
// Else call the AEM GraphQL persisted query named "wknd-shared/adventures-all" to get all adventures
response = await fetchPersistedQuery("wknd-shared/adventures-all", queryVariables);
}
...
}
...
/**
* Private function that invokes the AEM Headless client.
*
* @param {String} persistedQueryName the fully qualified name of the persisted query
* @param {*} queryParameters an optional JavaScript object containing query parameters
* @returns the GraphQL data or an error message
*/
async function fetchPersistedQuery(persistedQueryName, queryParameters) {
let data;
let err;
try {
// AEM GraphQL queries are asynchronous, either await their return or use Promise-based .then(..) { ... } syntax
const response = await aemHeadlessClient.runPersistedQuery(
persistedQueryName,
queryParameters
);
// The GraphQL data is stored on the response's data field
data = response?.data;
} catch (e) {
// An error occurred, return the error messages
err = e
.toJSON()
?.map((error) => error.message)
?.join(", ");
console.error(e.toJSON());
}
return { data, err };
}
视图
React应用程序使用两个视图在Web体验中展示冒险数据。
-
src/components/Adventures.js
调用
getAdventuresByActivity(..)
从src/api/usePersistedQueries.js
并在列表中显示返回的冒险。 -
src/components/AdventureDetail.js
调用
getAdventureBySlug(..)
使用slug
param通过Adventures
组件,并显示单次冒险的详细信息。
环境变量
多个 环境变量 用于连接到AEM环境。 默认连接到运行于的AEM发布 http://localhost:4503
. 更新 .env.development
文件,更改AEM连接:
-
REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
:设置为AEM目标主机 -
REACT_APP_GRAPHQL_ENDPOINT=/content/graphql/global/endpoint.json
:设置GraphQL端点路径。 此React应用程序不使用此选项,因为此应用程序仅使用持久化查询。 -
REACT_APP_AUTH_METHOD=
:首选身份验证方法。 可选,根据默认情况,不使用身份验证。service-token
:使用服务凭据获取AEMas a Cloud Service上的访问令牌dev-token
:使用开发令牌进行AEMas a Cloud Service上的本地开发basic
:使用用户/通行证进行本地AEM创作实例上的本地开发- 留空以在不进行身份验证的情况下连接到AEM
-
REACT_APP_AUTHORIZATION=admin:admin
:设置连接到AEM创作环境时使用的基本身份验证凭据(仅用于开发)。 如果连接到“发布”环境,则不需要此设置。 -
REACT_APP_DEV_TOKEN
:开发令牌字符串。 要连接到远程实例,在基本身份验证(user:pass)旁边,您可以从云控制台将持有者身份验证与开发令牌结合使用 -
REACT_APP_SERVICE_TOKEN
:服务凭据文件的路径。 要连接到远程实例,还可以使用服务令牌进行身份验证(从开发人员控制台下载文件)。
代理AEM请求
使用webpack开发服务器时(npm start
)项目依赖于 代理设置 使用 http-proxy-middleware
. 文件配置于 src/setupProxy.js 并且依赖于在设置的多个自定义环境变量 .env
和 .env.development
.
如果连接到AEM创作环境,则相应的 需要配置身份验证方法.
跨源资源共享(CORS)
此React应用程序依赖于在目标AEM环境中运行的基于AEM的CORS配置,并假定React应用程序运行于 http://localhost:3000
处于开发模式时。 查看AEM Headless部署文档 有关如何设置和配置CORS的详细信息。