编程模式
引言
本章节讨论使用 GeoScene Maps SDK for JavaScript 开发应用程序的编程模式和最佳实践。
加载类
获取 GeoScene Maps SDK for JavaScript 后,使用 require()
将 GeoScene Maps SDK for JavaScript 类异步加载到应用程序中。
该方法需要两个参数:
- 一个有序的完整的命名空间字符串数组,用来导入 API 类
- 在回调函数中每个 API 类都按顺序作为参数加载
例如,要加载 Map
和 Map
类,请先在文档并查找每个类的完整命名空间。在本例中,Map
命名空间 "geoscene/Map"
,Map
的命名空间为 "geoscene/views/Map
。这些字符串作为数组传递给 require()
,并使用局部变量名称 Map
和 Map
作为回调函数中对应位置的参数:
并非每个模块都需要使用 require()
进行加载,因为许多类可以使用自动转换从构造函数中初始化。
通过 AMD 模块 Dojo 工具包简介,了解有关 AMD 模块格式的更多信息。
构造函数
GeoScene Maps SDK for JavaScript 中的所有类都有单个构造函数,并且所有属性都可通过将参数传递给构造函数来设置。
例如,下面是调用 Map
和 Map
类的构造函数的示例。
或者,可以使用 setters 直接指定类实例的属性。
属性
GeoScene Maps SDK for JavaScript 支持以一种简单、一致的方式来获取、设置和查看类的所有属性。
许多 API 类都是 Accessor
类的子类,该类定义了以下方法。
方法名称 | 返回值类值 | 说明 |
---|---|---|
get(property | 不同类型 | 使用以下名称获取属性的值: property |
set(property | N/A | 对于 property 中的每个 key /value 对,此方法可将名称为 key 的属性值设置为 value |
watch(property | Watch | 当名为 property 的属性值更改时,调用回调函数 callback |
Getters
get
方法返回命名属性的值。
此方法是一种简便的方法,因为在不使用 get()
的情况下,要返回嵌套属性的值 (例如,要返回 Map
对象的属性 basemap
的 title
),需要使用 if
语句来检查 basemap
是 undefined
还是 null
。
get
方法不再需要 if
语句,并返回 map.basemap.title
的值,如果 map.basemap
存在,否则返回 null
。
Setters
可直接设置属性的值。
当需要更改多个属性值时,set()
可以传递具有属性名称和新值的 JavaScript Object
。
查看属性变化
API 提供有两种模式来监视属性值的变化:reactiveUtils 和 Accessor.watch()
。
reactiveUtils
reactive
提供了使用各种不同的数据类型和结构(例如字符串、布尔值、数组、集合和对象)来跟踪 API 属性更改的功能。该模块还允许组合来自多个来源的属性。它包括以下方法:watch()
、on()
、once()
、when()
和 when
。
reactive
提供 TypeScript 类型检查。您可以访问属性、构建对象或执行其他计算,所有这些都经过 TypeScript 编译器的正确检查。回调参数也是从 get
函数中正确推断出来的。
最常见的实现模式之一是跟踪简单属性何时被访问。该模式使用 watch()
方法,如下所示:
watch()
方法返回一个 WatchHandle。要停止监视更改,请在监视句柄上调用 remove()
方法,例如 handle.remove()
。最佳做法是在不再需要监视程序时将其删除。
以下代码段使用 watch()
来跟踪何时访问 view.updating
布尔属性:
此代码段使用 map.all
属性(Collection
)来确定所有图层是否可见:
以下代码段显示了如何使用 watch()
方法跟踪何时访问两个不同的属性 (view.stationary
和 view.zoom
):
使用 reactive
,您可通过使用点符号(例如 object.property
)、括号符号(例如 object["property"]
)和可选链接(例如 object.value?.property
)来跟踪对象。
例如,可以跟踪 3D 应用程序何时启用或禁用环境遮挡着色:
Accessor.watch()
Accessor.watch()
是一个简便的方法,在 Accessor
的子类中可以使用它来查看类实例上的属性更改。构造函数采用两个参数:
- 指定为
String
或String[]
的属性名称,以及 - 每当属性值更改时调用的回调函数
以下代码段查看 Map
,并在比例值发生变化时调用回调函数。
Accessor.watch()
还返回一个 Watch
。要停止监视更改,请在监视句柄上调用 remove()
方法,例如 handle.remove()
。最佳做法是在不再需要监视程序时将其删除。
自动转换
自动转换可将 JavaScript 对象转换为 GeoScene Maps SDK for JavaScript 类类型,而无需应用程序开发者显式导入这些类。
在以下代码示例中,为 FeatureLayer 创建 SimpleRenderer 需要五个 API 类。
通过自动转换,您不必导入渲染器和符号类;您需要导入的唯一模块是 geoscene/layers/Feature
。
要了解类是否可以自动转换,请查看每个类的 GeoScene Maps SDK for JavaScript 参考。如果属性可以自动转换,则将显示下图:
例如,Feature
类的属性 renderer
的文档具有 autocast
标记。
请注意,使用自动转换的代码更简单,并且在功能上与上述代码片段相同,其中所有模块都显式导入。GeoScene Maps SDK for JavaScript 将使用传递给构造函数中属性的值,并在内部实例化类型对象。
请记住,如果模块类型已知或固定,则无需在属性上指定 type
。例如,查看上面代码片段中 SimpleMarkerSymbol 类中的 outline
属性。它没有 type
属性,因为唯一具有 outline
属性的 Symbol
子类是 Simple
。
在 type
更通用的情况下,例如 FeatureLayer.renderer,则必须始终指定 type
以便自动转换正常工作。
所有代码示例均记录了类或属性是否正在自动转换。
异步数据
本部分介绍 GeoScene Maps SDK for JavaScript 中的 JavaScript Promises 和 Loading 模式。
Promises
Promises 在 GeoScene Maps SDK for JavaScript 中扮演着重要角色。使用 promises 允许在使用异步操作时编写更干净的代码。
什么是 Promise?
在最基本层面上,promise 是从异步任务返回的未来值的表示形式。当任务执行时,promise 允许其他进程同时运行,同时等待返回将来的值。这在发出多个网络请求时特别有用,因为时间和下载速度可能无法预测。
Promise 始终处于以下三种状态之一:
- 等待中
- 已完成
- 已拒绝
解析 promise 时,它可以解析为 callback
函数中定义的值或另一 promise。当 promise 被拒绝时,应在 err
函数中处理。
使用 Promises
Promises 通常与 then()
一起使用。这是一个功能强大的方法,它定义在实现 promise 时调用的回调函数,以及在 promise 被拒绝时调用的错误函数。第一个参数始终是成功回调,第二个可选参数是错误回调。
一旦 promise 被解析,即会调用 callback
;如果 promise 被拒绝,则会调用 err
。
catch()
方法也可用于为 promise 指定错误回调函数。
有关错误处理的详细信息,请参阅 GeoScene 错误。
示例:GeometryService
在此示例中,geometryService 用于将多个点几何投影到新的空间参考。在 geometry
文档中,请注意,project()
返回一个解析为投影几何数组的 promise。
使用 async/await
promise 的一个优点是它们可以在异步或异步函数中使用,该函数允许表达式同步运行而不会阻止其他代码的执行。这可通过将 async
关键字放在函数定义之前,然后在返回 promise 的表达式上设置 await
关键字来实现。异步函数外部的代码可以并行运行,直到 await
表达式中的 promise 完成或拒绝。
下面是一个使用 reactive
的示例。此表达式首次等待缩放级别大于 20。然后它返回 promise。解析 promise 后,控制台将写入一条消息:
异步函数可以包含零个或多个await
表达式,这些表达式在解析每个 promise 时按顺序运行。在本例中,compile
函数与 query
函数同时运行。compile
函数运行 view.when
,并等待解析表示已在 View
图上创建 Layer
的 promise。然后,它使用 reactive
来评估图层视图何时完成更新。当该表达式计算 updating
属性为 true
时,代码将继续执行到计算统计信息的第三个表达式,然后函数返回结果。
其他资源
在 MDN Promise 文档以及 async
和 await
中阅读有关 promises 的更多信息,以更深入地了解其结构和用法。
以下是指向博客的其他链接,这些博客通过其他有用示例解释了 promises:
可加载
图层、地图和门户项目等资源通常依赖于远程服务或磁盘上的数据集来初始化其状态。访问此类数据需要资源异步初始化其状态。可加载设计模式统一了此行为,采用这种模式的资源被称为“可加载”。
可加载资源处理并发和重复的请求,以允许在应用程序的各部分之间共享相同的资源实例。此模式允许在服务响应缓慢等情况下取消加载资源。最后,可加载资源通过可以检查和监视的显式状态提供有关其初始化状态的信息。
加载状态
可加载类的 loadStatus 属性返回可加载资源的状态。具有四种可能状态。
状态 | 说明 |
---|---|
not-loaded | 未要求资源加载其元数据,并且其状态未正确初始化。 |
loading | 资源正在异步加载其元数据 |
failed | 资源无法加载其元数据,并且遇到的错误可从 load 属性获得。 |
loaded | 资源已成功加载其元数据,并且其状态已正确初始化。 |
以下状态转换表示可加载资源所经历的阶段。
Loadable 接口包括侦听器,可轻松监控可加载资源的状态、显示进度以及在状态更改时采取的措施。
加载
当调用 load()
时,资源开始异步加载其元数据。
此时,加载状态由 not-loaded
变为 loading
。当异步操作完成时,将调用回调。如果操作遇到错误,则填充回调中的错误参数,并将 load
设置为 failed
。如果操作成功完成,则错误参数为 null
,且加载状态设置为 loaded
,这意味着资源已完成加载其元数据,现在已正确初始化。
很多时候,相同的资源实例由应用程序的不同部分共享。例如,图例组件和 Layer
可能有权访问同一图层,并且它们可能都希望访问图层的属性以填充其 UI。或者,可跨应用程序共享相同的门户实例,以在应用程序的不同部分显示用户的项目和组。load()
支持多个“侦听器”以简化此类应用程序的开发。可并发地重复调用它,但只尝试加载元数据一次。如果在调用 load()
时,加载操作已在进行中 (loading
状态),则它只需借助未完成的操作,并在该操作完成时将回调加入队列以被调用。
如果在调用 load()
时,操作已经完成 (loaded
或 failed
状态),则回调将立即使用传递的操作结果进行调用,无论是成功还是失败,且状态保持不变。这样就可以安全地对可加载的资源自由调用 load()
,而无需检查资源是否已加载,也不必担心每次都会发出不必要的网络请求。
如果资源加载失败,调用 load()
不会更改其状态。回调将立即调用过去的加载错误。
取消加载
当调用 cancel
时,资源将取消任何未完成的异步操作以加载其元数据。这会将状态从 loading
转换为 failed
。load
属性将返回反映操作已取消的信息。
应谨慎使用此方法,因为该资源实例的所有排队回调都将被调用,并显示操作已取消的错误。这样,应用程序中的一个组件在共享同一资源实例时,可以取消其他组件发起的加载。
如果资源未处于 loading
状态,则 cancel
方法不执行任何操作。
级联加载依赖性
可加载资源通常依赖于加载其他可加载资源来正确初始化其状态。例如,门户项目在其主门户完成加载之前,无法完成加载。在首次加载与其关联的要素服务之前,无法加载要素图层。这种情况称为加载依赖性。
在任何资源上调用的可加载操作通过其依赖关系图透明地级联。这有助于简化可加载资源的使用,并将正确建立和管理其加载依赖项的责任放在资源上。
以下代码示例显示了这种级联行为如何产生简洁的代码。加载地图将导致门户项目开始加载,进而开始加载其门户。无需显式加载资源。
依赖项可能无法加载。某些依赖项可能很关键,例如门户项目对其门户的依赖。如果在加载此类依赖项时遇到故障,则该错误将显示在启动加载周期的资源上 (该资源也将无法加载)。其他加载依赖性可能是偶然的,例如地图对其业务图层的依赖,资源可能会成功加载,即使其依赖项之一未能加载。
使用 fromJSON
包括 symbols、geometries、Camera、Viewpoint、Color 和 FeatureSet 在内的许多类都包含一个名为 from
的方法。
此函数可从 GeoScene 产品生成的 JSON 创建给定类的实例。这种格式的 JSON 通常是基于 t
方法或通过 REST API 查询而创建的。请参阅 GeoScene REST API 文档,了解如何在 JSON 中查询和表示几何、符号、web 地图等的信息和示例。
以下示例演示了如何使用之前通过 REST API 从查询中检索到的 JSON 创建 SimpleMarkerSymbol 的方式。
作为输入参数传递给 from
的 JSON 对象看起来可能与同一类中作为构造函数参数传递的对象类似。然而,这两个对象在很多方面都有所不同,不应进行互换。这是因为 REST API 和 GeoScene Maps SDK for JavaScript 之间的值和默认测量单位不同(例如,REST API 中符号大小以磅为单位进行测量,而 GeoScene Maps SDK for JavaScript 使用像素为单位)。
类构造函数中传递的参数是一个简单的 JSON 对象。此模式应始终用于创建类的新实例,除非处理之前由 t
或 REST API 的查询生成的 JSON。如果 JSON 是先前使用 REST API 或其他 GeoScene 产品(例如 GeoScene Server、GeoScene Online、GeoScene Portal 等)生成的,则在从 JSON 对象创建类实例时,则始终使用 from
,而不是构造函数。
使用 jsonUtils
在使用 from
实例化对象时,有几个 json
类作为方便类提供,但对象的类型未知。
geoscene/geometry/support/json
Utils geoscene/renderers/support/json
Utils geoscene/symbols/support/json
Utils
当 JSON 对象表示 REST API 中的几何、渲染器或符号,但对象类型未知时,可以使用这些类。例如,如果图层的渲染器来自于 REST 请求,并且不确定渲染器是否为 UniqueValueRenderer,则 require()
geoscene/renderers/support/jsonUtils 类来帮助确定渲染器的类型。
微件 viewModel 模式
可以使用 viewModel 为许多开箱即用的微件实现其他功能。
使用微件有两个部分:微件和微件的 viewModel。微件(即视图)部分负责处理微件的用户界面(UI),这意味着微件如何通过 DOM 显示和处理用户交互,例如 Sketch 微件。viewModel 部分负责微件的基础功能,或者更确切地说,负责其业务逻辑,例如 SketchViewModel。
为什么要将微件框架分为这两个独立的部分?一个原因是可重用性。viewModel 公开支持视图所需的功能所需的 API 属性和方法,而视图包含 DOM 逻辑。由于 viewModels 从 geoscene/core/Accessor 扩展而来,因此它们利用了 Accessor 的所有功能。这有助于保持 API 各个部分之间的一致性,因为许多其他模块也派生自此类。
那么这两部分是如何协同工作的呢?当微件渲染时,它会渲染其 state
。此状态派生自视图和 viewModel 属性。在微件生命周期中的某个时刻,视图调用 viewModel 的方法/属性,从而导致属性或结果的更改。触发更改后,视图会收到通知,并将在 UI 上更新。
下面是一个使用 Sketch
属性以在主动创建新图形时覆盖默认绘图符号系统: