admin管理员组

文章数量:1028460

介绍企业级前端开发领域,Web 应用的扩展点预埋最佳实践

在企业级前端迭代越来越频繁的今天,开发者希望既能快速交付 MVP,又能在后续迭代里无痛插拔功能。ThoughtWorks 技术雷达长期倡导的 evolutionary architecture 思想,鼓励在核心代码里为未来变化预埋“可变点”。SAP UI5 社区多年来实践出两条主干:在 XML 视图里放置 extension point,在控制器里暴露 extension hook。这两种机制让应用像乐高一样可插可拔,同时保持与 SAP 官方升级兼容。本文结合 Radar 的“Adopt / Trial / Assess / Hold” 四象限视角,拆解如何在项目伊始就种下可扩展的“钩子”,并提供可直接运行的代码片段供读者上手。

技术雷达视角下的可扩展设计

ThoughtWorks 在 Radar Vol 32 所列出的 Design for Extensibility 归于 Tools — Adopt 象限,强调在框架层面预留插槽以减轻分叉带来的维护成本 (Tools | Technology Radar - Thoughtworks)。SAP UI5 的 extension point / hook 正是落地这一理念的典型实现:视图层用 declarative 的标记告诉框架“此处可替换”,控制器层用约定命名的函数让消费者选择性覆写 (Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal, View Extension)。当后续业务需要插入按钮、表单或整段逻辑时,二次开发者只需在 manifest 里声明替换关系,无须改写源码,这与 Radar 所推崇的“可演进架构”不谋而合 (Build Your Own Technology Radar | Thoughtworks United States)。

XML 视图里的 extension point 实践

基本语义

在 XML 视图文件中,只要用 <core:ExtensionPoint name='...'/>(也可用 JS API sap.ui.extensionpoint)标记一个位置,就等于告诉 UI5“这里是一块可被替换的占位符” (View Extension, View Extension - SAP Help Portal)。运行时框架会在加载组件时查找 manifest 里的 sap.ui.viewExtensions,决定是否把自定义片段或子视图注入该位置。若无匹配配置,占位符可显示默认内容或保持为空 (Implementing View Extension, Modification, and Replacement)。

可运行示例

下面示例基于最低 UI5 版本 1.108,在本地 ui5 serve 即可启动。代码里刻意使用单引号或反引号,避免违背本文格式约束。

Main.view.xml

代码语言:xml复制
<mvc:View
    xmlns='sap.m'
    xmlns:mvc='sap.ui.core.mvc'
    xmlns:core='sap.ui.core'>
    <VBox>
        <Text text='Product List'/>
        <core:ExtensionPoint name='ProductActions'/>
        <List items='{path: `/Products`}'>
            <items>
                <StandardListItem title='{ProductName}'/>
            </items>
        </List>
    </VBox>
</mvc:View>

manifest.json(片段插入)

代码语言:json复制
{
  'sap.ui5': {
    'componentName': 'demo.ext',
    'contentDensities': { 'compact': true, 'cozy': true },
    'viewExtensions': {
      'demo.ext.view.Main': {
        'ProductActions': {
          'className': 'sap.ui.core.Fragment',
          'fragmentName': 'demo.ext.fragment.ProductToolbar',
          'type': 'XML'
        }
      }
    }
  }
}

ProductToolbar.fragment.xml

代码语言:xml复制
<OverflowToolbar
    xmlns='sap.m'>
    <Button text='Add' type='Emphasized' press='.onAdd'/>
    <ToolbarSpacer/>
    <Button text='Download' icon='sap-icon://download' press='.onExport'/>
</OverflowToolbar>

把片段文件放到 /webapp/fragment 目录,再启动应用,就能看到按钮无缝注入,原视图无需改一行代码。这种做法正切合 Radar 推荐的“在设计阶段就暴露插槽”模式,让后续团队可以 Trial 或 Adopt 新功能而不破坏主干。

深入细节:默认内容与多聚合

ExtensionPoint 允许包裹默认控件,当无自定义实现时展示默认 UI,以提升开箱可用性 (View Extension)。对 sap.m.Table 这类二维聚合控件,需要同时在 columns 与行模板 cells 里各放一个占位符,以支持列级与行级注入 (View Extension)。UI5 1.38 以后甚至支持在占位符级别替换整视图,以应对大型功能替换 (1.38.63 - Demo Kit - SAPUI5 SDK)。

控制器里的 extension hook 策略

概念与执行顺序

Controller 扩展以 sap.ui.controllerExtensions 节点为中心。框架在运行期把自定义 controller 与标准 controller 做对象级合并,而生命周期方法 onInit / onAfterRendering 等保持链式调用,确保标准逻辑先执行,再执行扩展逻辑 (Controller Extension - SAP Help Portal)。若想在特定步骤插入自定义算法,可在原 controller 暴露一个前缀为 extHook 的空函数;二次开发者只需实现同名方法并在扩展 controller 中返回,框架便会在合适时机调用 (Using Controller Extension - Documentation - Demo Kit - SAPUI5 SDK, How to invoke hook extension in controller extensi... - SAP Community)。

可运行示例

Main.controller.js(标准逻辑)

代码语言:javascript代码运行次数:0运行复制
sap.ui.define([
  'sap/ui/core/mvc/Controller'
], function (Controller) {
  'use strict';
  return Controller.extend('demo.std.controller.Main', {
    onInit: function () {
      // 标准初始化
      this.byId('msg').setText('Standard init done');
      // 钩子:留给扩展方追加逻辑
      if (this.extHookAfterInit) {
        this.extHookAfterInit();
      }
    }
  });
});

Ext.controller.js(扩展逻辑)

代码语言:javascript代码运行次数:0运行复制
sap.ui.define([], function () {
  'use strict';
  return {
    extHookAfterInit: function () {
      // 二次开发者逻辑
      this.byId('msg').setText('Extension logic executed');
    }
  };
});

manifest.json(声明合并)

代码语言:json复制
{
  'sap.ui5': {
    'controllerExtensions': {
      'demo.std.controller.Main': {
        'controllerName': 'demo.ext.controller.Ext'
      }
    }
  }
}

启动应用即可看到文本由扩展逻辑覆盖。整个过程零侵入,恰好对应 Radar 对“插件化架构”在大型前端中的 Adopt 建议 (UI5 extension of controller and lifecycle methods - Stack Overflow)。

注意事项

  • 对 onInit / onExit 等生命周期方法,扩展文件若覆写同名函数,框架会自动串联执行,顺序与文档描述一致 (Controller Extension - SAP Help Portal)。
  • 对自定义 hook 必须先在标准 controller 暴露空实现,否则框架无法识别 (How to invoke hook extension in controller extensi... - SAP Community)。
  • 若同一 controller 有多级扩展,应避免循环依赖;官方不建议多级链式 controller 继承,可改用 BaseController + mixin 组合模式 (Issue extending a controller that extends another controller)。

manifest 中的 customizing 速查表

节点

目标

示例

参考文档

sap.ui.viewExtensions

XML / JS 视图插槽

ProductActions

(View Extension)

sap.ui.controllerExtensions

控制器逻辑钩子

extHookAfterInit

(Using Controller Extension - Documentation - Demo Kit - SAPUI5 SDK)

sap.ui.viewModifications

细粒度重排视图属性

隐藏字段

(Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal)

与 SAPUI5 flexibility 协同

UI5 Flexibility 让业务用户在 Runtime 自行拖拽修改,可保存到 Layer 中并随版本迁移 (SAPUI5 Flexibility: Adapting UIs Made Easy - SAP Help Portal, SAPUI5 Flexibility Services: Adapting UIs Made Easy)。然而 Flex 修改主要面向最终用户,而 extension point / hook 则面向开发阶段,用于注入整块功能代码。二者可以并存:先在视图预埋 ExtensionPoint,再允许业务用户对注入的 Fragment 做 Flex 调整,兼顾开发到运营全链路的可演进性 (Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal)。

流程建议

  1. 规划阶段:在每个业务模块草图里标记潜在变动区,转化为 ExtensionPoint / hook;同时把默认实现与预期替换者写入 ADR(Architecture Decision Record)。
  2. 编码阶段:以单元测试验证占位符存在;利用 UI5 QUnit 模拟 manifest 注入,确保默认内容与替换内容均可渲染。
  3. 持续集成:在 pipeline 中引入 open-ui5-verifier,检测视图文件里 ExtensionPoint 是否被错误删除,防止回归。
  4. 运维阶段:结合 UI5 Flexibility Layer 做小范围 A/B Test,再根据数据逐步推广自定义插件。

省流版

通过在 XML 视图安放 extension point、在控制器公开 extension hook,SAP UI5 应用自然具备了 ThoughtWorks 技术雷达倡导的“可演进”特质:核心代码稳定,外围能力灵活替换。只要在项目初期养成“先留钩子再写功能”的习惯,就能让未来的业务需求像拼积木一样按需组合,而不必在升级窗口里痛苦比对冲突。希望本文示例能帮助大家在下一个 UI5 项目里迈出 Extension First 的第一步。

介绍企业级前端开发领域,Web 应用的扩展点预埋最佳实践

在企业级前端迭代越来越频繁的今天,开发者希望既能快速交付 MVP,又能在后续迭代里无痛插拔功能。ThoughtWorks 技术雷达长期倡导的 evolutionary architecture 思想,鼓励在核心代码里为未来变化预埋“可变点”。SAP UI5 社区多年来实践出两条主干:在 XML 视图里放置 extension point,在控制器里暴露 extension hook。这两种机制让应用像乐高一样可插可拔,同时保持与 SAP 官方升级兼容。本文结合 Radar 的“Adopt / Trial / Assess / Hold” 四象限视角,拆解如何在项目伊始就种下可扩展的“钩子”,并提供可直接运行的代码片段供读者上手。

技术雷达视角下的可扩展设计

ThoughtWorks 在 Radar Vol 32 所列出的 Design for Extensibility 归于 Tools — Adopt 象限,强调在框架层面预留插槽以减轻分叉带来的维护成本 (Tools | Technology Radar - Thoughtworks)。SAP UI5 的 extension point / hook 正是落地这一理念的典型实现:视图层用 declarative 的标记告诉框架“此处可替换”,控制器层用约定命名的函数让消费者选择性覆写 (Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal, View Extension)。当后续业务需要插入按钮、表单或整段逻辑时,二次开发者只需在 manifest 里声明替换关系,无须改写源码,这与 Radar 所推崇的“可演进架构”不谋而合 (Build Your Own Technology Radar | Thoughtworks United States)。

XML 视图里的 extension point 实践

基本语义

在 XML 视图文件中,只要用 <core:ExtensionPoint name='...'/>(也可用 JS API sap.ui.extensionpoint)标记一个位置,就等于告诉 UI5“这里是一块可被替换的占位符” (View Extension, View Extension - SAP Help Portal)。运行时框架会在加载组件时查找 manifest 里的 sap.ui.viewExtensions,决定是否把自定义片段或子视图注入该位置。若无匹配配置,占位符可显示默认内容或保持为空 (Implementing View Extension, Modification, and Replacement)。

可运行示例

下面示例基于最低 UI5 版本 1.108,在本地 ui5 serve 即可启动。代码里刻意使用单引号或反引号,避免违背本文格式约束。

Main.view.xml

代码语言:xml复制
<mvc:View
    xmlns='sap.m'
    xmlns:mvc='sap.ui.core.mvc'
    xmlns:core='sap.ui.core'>
    <VBox>
        <Text text='Product List'/>
        <core:ExtensionPoint name='ProductActions'/>
        <List items='{path: `/Products`}'>
            <items>
                <StandardListItem title='{ProductName}'/>
            </items>
        </List>
    </VBox>
</mvc:View>

manifest.json(片段插入)

代码语言:json复制
{
  'sap.ui5': {
    'componentName': 'demo.ext',
    'contentDensities': { 'compact': true, 'cozy': true },
    'viewExtensions': {
      'demo.ext.view.Main': {
        'ProductActions': {
          'className': 'sap.ui.core.Fragment',
          'fragmentName': 'demo.ext.fragment.ProductToolbar',
          'type': 'XML'
        }
      }
    }
  }
}

ProductToolbar.fragment.xml

代码语言:xml复制
<OverflowToolbar
    xmlns='sap.m'>
    <Button text='Add' type='Emphasized' press='.onAdd'/>
    <ToolbarSpacer/>
    <Button text='Download' icon='sap-icon://download' press='.onExport'/>
</OverflowToolbar>

把片段文件放到 /webapp/fragment 目录,再启动应用,就能看到按钮无缝注入,原视图无需改一行代码。这种做法正切合 Radar 推荐的“在设计阶段就暴露插槽”模式,让后续团队可以 Trial 或 Adopt 新功能而不破坏主干。

深入细节:默认内容与多聚合

ExtensionPoint 允许包裹默认控件,当无自定义实现时展示默认 UI,以提升开箱可用性 (View Extension)。对 sap.m.Table 这类二维聚合控件,需要同时在 columns 与行模板 cells 里各放一个占位符,以支持列级与行级注入 (View Extension)。UI5 1.38 以后甚至支持在占位符级别替换整视图,以应对大型功能替换 (1.38.63 - Demo Kit - SAPUI5 SDK)。

控制器里的 extension hook 策略

概念与执行顺序

Controller 扩展以 sap.ui.controllerExtensions 节点为中心。框架在运行期把自定义 controller 与标准 controller 做对象级合并,而生命周期方法 onInit / onAfterRendering 等保持链式调用,确保标准逻辑先执行,再执行扩展逻辑 (Controller Extension - SAP Help Portal)。若想在特定步骤插入自定义算法,可在原 controller 暴露一个前缀为 extHook 的空函数;二次开发者只需实现同名方法并在扩展 controller 中返回,框架便会在合适时机调用 (Using Controller Extension - Documentation - Demo Kit - SAPUI5 SDK, How to invoke hook extension in controller extensi... - SAP Community)。

可运行示例

Main.controller.js(标准逻辑)

代码语言:javascript代码运行次数:0运行复制
sap.ui.define([
  'sap/ui/core/mvc/Controller'
], function (Controller) {
  'use strict';
  return Controller.extend('demo.std.controller.Main', {
    onInit: function () {
      // 标准初始化
      this.byId('msg').setText('Standard init done');
      // 钩子:留给扩展方追加逻辑
      if (this.extHookAfterInit) {
        this.extHookAfterInit();
      }
    }
  });
});

Ext.controller.js(扩展逻辑)

代码语言:javascript代码运行次数:0运行复制
sap.ui.define([], function () {
  'use strict';
  return {
    extHookAfterInit: function () {
      // 二次开发者逻辑
      this.byId('msg').setText('Extension logic executed');
    }
  };
});

manifest.json(声明合并)

代码语言:json复制
{
  'sap.ui5': {
    'controllerExtensions': {
      'demo.std.controller.Main': {
        'controllerName': 'demo.ext.controller.Ext'
      }
    }
  }
}

启动应用即可看到文本由扩展逻辑覆盖。整个过程零侵入,恰好对应 Radar 对“插件化架构”在大型前端中的 Adopt 建议 (UI5 extension of controller and lifecycle methods - Stack Overflow)。

注意事项

  • 对 onInit / onExit 等生命周期方法,扩展文件若覆写同名函数,框架会自动串联执行,顺序与文档描述一致 (Controller Extension - SAP Help Portal)。
  • 对自定义 hook 必须先在标准 controller 暴露空实现,否则框架无法识别 (How to invoke hook extension in controller extensi... - SAP Community)。
  • 若同一 controller 有多级扩展,应避免循环依赖;官方不建议多级链式 controller 继承,可改用 BaseController + mixin 组合模式 (Issue extending a controller that extends another controller)。

manifest 中的 customizing 速查表

节点

目标

示例

参考文档

sap.ui.viewExtensions

XML / JS 视图插槽

ProductActions

(View Extension)

sap.ui.controllerExtensions

控制器逻辑钩子

extHookAfterInit

(Using Controller Extension - Documentation - Demo Kit - SAPUI5 SDK)

sap.ui.viewModifications

细粒度重排视图属性

隐藏字段

(Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal)

与 SAPUI5 flexibility 协同

UI5 Flexibility 让业务用户在 Runtime 自行拖拽修改,可保存到 Layer 中并随版本迁移 (SAPUI5 Flexibility: Adapting UIs Made Easy - SAP Help Portal, SAPUI5 Flexibility Services: Adapting UIs Made Easy)。然而 Flex 修改主要面向最终用户,而 extension point / hook 则面向开发阶段,用于注入整块功能代码。二者可以并存:先在视图预埋 ExtensionPoint,再允许业务用户对注入的 Fragment 做 Flex 调整,兼顾开发到运营全链路的可演进性 (Developer Adaptation - SAPUI5 Flexibility - SAP Help Portal)。

流程建议

  1. 规划阶段:在每个业务模块草图里标记潜在变动区,转化为 ExtensionPoint / hook;同时把默认实现与预期替换者写入 ADR(Architecture Decision Record)。
  2. 编码阶段:以单元测试验证占位符存在;利用 UI5 QUnit 模拟 manifest 注入,确保默认内容与替换内容均可渲染。
  3. 持续集成:在 pipeline 中引入 open-ui5-verifier,检测视图文件里 ExtensionPoint 是否被错误删除,防止回归。
  4. 运维阶段:结合 UI5 Flexibility Layer 做小范围 A/B Test,再根据数据逐步推广自定义插件。

省流版

通过在 XML 视图安放 extension point、在控制器公开 extension hook,SAP UI5 应用自然具备了 ThoughtWorks 技术雷达倡导的“可演进”特质:核心代码稳定,外围能力灵活替换。只要在项目初期养成“先留钩子再写功能”的习惯,就能让未来的业务需求像拼积木一样按需组合,而不必在升级窗口里痛苦比对冲突。希望本文示例能帮助大家在下一个 UI5 项目里迈出 Extension First 的第一步。

本文标签: 介绍企业级前端开发领域,Web 应用的扩展点预埋最佳实践