"onBeforeRendering" 或 "onAfterRendering" 不会在每次打开视图时调用

"onBeforeRendering" or "onAfterRendering" is not called every time the view is opened

在我的 UI5 应用程序中,我有一个带有 table (sap.m.Table) 的视图,在 onInit 挂钩处填充了来自后端的数据。问题是 onInit 每个视图实例只执行一次:

It is only called once per View instance, unlike the onBeforeRendering and onAfterRendering hooks.

如果用户决定离开此视图(例如,返回导航)并稍后重新打开它,onInit 将不会被召回,因此不会再次检索数据,并且 table 内容将不会反映可能的更改。

为了保证每次打开视图都能获取到数据,我尝试在onBeforeRendering获取数据,但是这个hook也只调用了一次。我发现,每次打开视图时强制调用 onBeforeRendering 的唯一方法是将以下代码添加到 onInit 方法中:

onInit: function () {
  this.getView().addEventDelegate({
    onBeforeShow: this.onBeforeShow,
  }, this);
}

我的问题:

  1. 为什么没有上面onInit中的代码片段,不是每次显示视图都会触发onBeforeRendering

  2. 上面的代码片段到底做了什么?

  3. 替代技术:使用 and routeMatched。但是这三种方法中哪一种更常见?

问题 #3:

当您的路由器在 URL 或 router.navTo 方法上匹配时,

patternMatched 将被击中。

routeMatched 将在与 patternMatched 相同的场合以及其子路由被导航到时受到攻击。

想象一下,您在路线 A 上有主视图,在路线 B 上有详细信息。如果用户直接导航到路由 B,渲染与路由 B 关联的目标以及与路由 [=31] 关联的目标是有意义的=]A.

总结:

  • patternMatched:直接路由匹配

  • routeMatched:

    1. 此路由器中的路由模式。
    2. 其子路由的模式。
    3. 其嵌套路线的模式。发生这种情况时,nestedRoute 参数设置为嵌套路由实例。

你应该使用

onInit: function() {
    let route = this.getOwnerComponent().getRouter().getRoute("yourroutename");
    route.attachPatternMatched(this.onRoutePatternMatched, this);
    // ...
},

每次在您的路由配置中匹配路由模式时都会触发此路由事件。在上面的例子中,每次通过导航加载路由时都会调用函数onRoutePatternMatched

onRoutePatternMatched: function(event) {
    // this logic will repeat itself every time the route pattern is matched
},
  1. Why (...) is the onBeforeRendering not triggered every time the view is displayed?

我认为对“渲染”的含义存在误解。在 UI5 中,当控件正在“呈现”时,其对应的 HTML 元素正在被 RenderManager 在 DOM 中修改或创建。 IE。 onBeforeRendering 的字面意思是“在调用控件(此处:View)的 render 函数之前”。

onBeforeRendering not 意味着它在浏览器的 paint 事件之前被调用(为此,现代浏览器提供高级 APIs 例如 Intersection Observer).
呈现的控件可以在 DOM 中但不能同时在视口中可见。

回到问题; on*Rendering 没有被触发的原因是控件之前已经渲染过了。这可以在用户通过 navTo 导航然后再次返回时看到。对应的视图元素已经在DOM中,所以不需要再调用render,即不会触发on*Rendering


  1. What does the code snippet exactly do?
    this.getView().addEventDelegate({
      onBeforeShow: this.onBeforeShow,
    }, this);
    

addEventDelegate 控件上触发的事件添加侦听器( 不是 被控制).

例如:view contains eventsafterInitbeforeExit、...
执行 addEventDelegate({onAfterInit}) 将无法工作,因为 afterInit 此控件(视图) 触发。
执行 addEventDelegate({onmouseover}) 有效,因为它在 此控件被触发。

onBeforeShow也是如此。该视图不包含 beforeShowafterShow 等任何事件。这些事件由 NavContainer 视图触发(例如<App> 在它的子视图上)。有关这些事件的文档可以在以下位置找到:

另见类似问题


  1. The alternative technique: to use patternMatched (...). But which one of these three approaches is more common?

“三种方法”我假设你的意思是:

  • on*Rendering(第一种方法),
  • on*Show(第二种方法),
  • 和上面提到的路由事件,如 patternMatched(第三种方法)。

一如既往,答案是这取决于您想要实现的目标。但是通常,我们:

  • 如果应用程序没有路由概念(manifest.json 中没有 sap.ui5/routing),请使用第二种方法(NavContainerChild 事件)。

  • 如果打算在显示视图后设置初始焦点,请将第二种方法与 onAfterShow 结合使用。参见 How to Set Initial Focus in a View?

  • 使用第 3 种方法获取有关匹配的路由模式的通知。这种方式通常用于在每次显示视图(NavContainerChild)时做一些事情,例如,在导航到详细信息页面后做Context Binding。工作原理:

    1. 调用router.navTo()时,创建下一个对应的view和controller。
    2. 在新建控制器的onInit中,你分配一个patternMatched.
    3. 导航时,URL 散列值会发生变化。路由器(内部 HashChanger) notices the URL change, leading to Route firing patternMatched 将调用您的处理程序。请参阅下面的 TL;DR
  • ⚠️ 个人意见:作为 应用程序 开发人员,避免使用第一种方法。避免在 onBeforeRenderingonAfterRendering 中做任何事情,因为从应用程序的角度来看,无法预测 render 函数被调用的频率。对于 control 开发人员来说,这些钩子是绝对必要的。但对于应用程序开发人员来说,通常有更好的选择。


TL;DR

忘记 on(Before|After)Rendering。使用路由中的 (pattern)Matched event 代替:

{ // Controller
  onInit: function() {
    const myRoute = this.getOwnerComponent().getRouter().getRoute("routeName");
    myRoute.attachPatternMatched(this.onMyRoutePatternMatched, this);
  },
  onMyRoutePatternMatched: function(event) {
    // your code when the view is about to be displayed ..
  },
}

Boghyon Hoffmann 和 Bernard 的答案是目前解决页面导航到时需要事件执行某些操作的问题的最佳解决方案。

有一个选项可以配置路由的目标以清除控件聚合并强制它在每次导航到时重新呈现。使用此配置,导航到页面时将调用事件 onBeforeRenderingonAfterRendering但是,这不是推荐的解决方案,因为它会导致不断重新渲染的性能问题,并且在使用 sap.m.NavContainer 时应设置为 false,否则会导致过渡停止发生等问题。如果您正在处理使用目前已弃用的 sap.ui.ux3.Shell 的遗留项目,这是一个可能的解决方案。

所以要配置这个选项我们需要在manifest.json中将选项clearControlAggregation设置为true,使用[=时默认为false 17=].

在此 link 中,您可以检查路由目标的所有选项: https://sapui5.hana.ondemand.com/#/api/sap.ui.core.routing.Targets

"config": {
    "routerClass": "sap.ui.core.routing.Router",
    "viewPath": "TestApp.view",
    "viewType": "XML",
    "clearControlAggregation": true,
    "controlId": "Main",
    "controlAggregation": "pages",
    "transition": "slide"
}

也可以仅为特定目标添加此选项,如下所示:

"targets": {
    "Test1": {
        "viewType": "XML",
        "viewName": "Test1",
        "clearControlAggregation": true,
        "title": "Test1"
    },
    "Test2": {
        "viewType": "XML",
        "viewName": "Test2",
        "title": "Test2"
    }
}