通用角度组件 - 可选绑定

Generic Angular Components - Optional Bindings

本文关键字:绑定 组件      更新时间:2023-09-26

我想创建一堆通用组件(angular 1.5),其中包含多个可选绑定,这些绑定将在多个应用程序中使用。

恐怕它会为不使用大多数可选绑定的应用程序创建许多不必要的观察程序。

例:

组件声明:

let dateRangeComponent = {
    bindings: {
        label: '@',
        name1: '@',
        name2: '@',
        model1: '>?',
        model2: '>?',
        extra1: '>?'
    },
    template: `<div ng-if="$ctrl.model1>stuff</div>
               <div ng-if="$ctrl.model2>stuff</div>
               <div ng-if="$ctrl.extra1>stuff</div>`
};

组件使用示例:

<date-rage-component label="Pretty Date" name1="Start" name2="end"/>

我的问题是是否可以自动取消监视与未使用的可选绑定相关的所有内容,知道它们在编译时未定义。

例如,假设我想在我的应用程序中使用一个不需要任何可选 Binding 的组件,angular 会创建很多不必要的观察程序来保持 ng-if 更新,当我们知道它们将永远是假的时。

我是否在不需要时进行了早期性能优化或误解了任何概念?

我想创建一个自定义包装器指令来利用 angular 1.5 中的惰性排除编译

像这样的东西(伪代码,未经测试):

<optional-binding-once ng-if="::attrs.model1">
  <div ng-if="attrs.model1">
      stuff
  </div>
</optional-binding-once>

通过这种方式,我认为 optional-binding-once 中的代码只有在 ng-if 为 true 时才会被编译,从而在未定义绑定的情况下减少一个观察程序。

(编辑)经过一些测试和研究后的一些结论

好吧,我想当可选绑定未填充时,没有一个简单的解决方案可以减少组件内的观察者数量。

我在角度的$digest阶段进行了一些测试,以检查这种观察者数量的增加是否真的是一个问题。

以下是我的结果:

这些测试针对的是具有 888 个组件和 4 个可选绑定的最坏情况。

Chrome - 不带可选绑定(888 个组件,观察者总数 889)

  • 观看者总数: 889
  • 上次摘要周期时间:0.9950000000026193
  • 最近 1004 个摘要周期的平均时间:1.0544920318724353 ms
  • 起始 DOM 加载 (400ms)

Chrome - 可选绑定(888 个组件,4 个可选绑定,观察者总数 4441)

  • 观察者总数:4441
  • 最后摘要周期时间:1.1549999999988358
  • 最近 1001 个摘要周期的平均时间:1.6851748251747816 ms
  • 启动 DOM 加载(600 毫秒)

Safari - 不带可选绑定(888 个组件,总观察者数 889)

  • 观看者总数: 889
  • 最后摘要周期时间:1.0849999999991269
  • 过去 530 个摘要周期的平均时间:1.211632075471664 ms

Safari - 可选绑定(888 个组件,4 个可选绑定,观察者总数 4441)

  • 观察者总数: 4441
  • 上次摘要周期时间:1.7450000000026193
  • 过去 588 个摘要周期的平均时间:2.1167176870748237 ms

结论:

在最坏的情况下,$digest时间将增加 1 毫秒。我不认为这种上升会成为我的应用程序性能的瓶颈。这种观察者会在第一个$digest条件下失败(值 = get(current)) !== (last = watch.last) && etc ...),从而对处理时间产生很小的影响,因为它们永远不会改变或弄脏角度上下文!

我会

利用 template 属性可以是function (tElem, tAttrs) { ... } (docs) 的事实,该属性返回一个字符串以根据存在的属性修改模板。

我这样做的方法是使用 jQuery 和一些自定义元素来指示模板的哪些部分是有条件的。

下面是一个快速示例模板函数:

function template($element, $attrs) {
  var fullTemplate = $('<div><if-attr name="a"><div ng-if="$ctrl.a"></div></if-attr></div>');
  fullTemplate.find('if-attr').each(function() {
    if (attrs.hasOwnProperty($(this).attr('name'))) {
      $(this).replaceWith(this.innerHTML);
    } else {
      $(this).remove();
    }
  });
  return fullTemplate[0].outerHTML;
}

示例输出

template(null, {a: '1'}) => "<div><div ng-if="$ctrl.a"></div></div>"
template(null, {b: '1'}) => "<div></div>"

已知限制

如果您想从 URL 获取模板(并且它没有预填充在$templateCache中),这会分解,但这似乎不是您的情况。

如果缩小

文档指出,如果template是一个函数,则会注入$element$attrs。这意味着,如果要缩小代码,请确保使用缩小安全方法来指定函数参数名称。例如

template: ['$element', '$attrs', function ($elem, $attrs) { 
    // ...
}],

function templateFn($elem, $attrs) { 
    // ...
}
templateFn['$inject'] = ['$element', '$attrs'];
template: templateFn,

另一个建议是不创建通用组件。相反,将业务逻辑推迟到服务并创建特定组件。让使用者代码根据自己的需要决定使用哪个组件。如果在多个组件之间共享了大量常见行为,则可以将服务注入到组件中以重用它们。考虑将业务逻辑拆分为多个服务,并使用分层设计重用代码,但允许根据情况进行规范。将共享代码推送到抽象服务,并将其扩展到具体服务。将具体服务注入到组件中。:)快乐。

只需要与 {{::variable}} 保持一次绑定即可防止多个观察程序。然后,您将不会遇到任何性能问题。

您需要在组件调用后销毁组件绑定。

如果您需要更多详细信息,请检查并让我知道。

https://toddmotto.com/angular-1-5-lifecycle-hooks#using-ondestroy

一个好方法是@GregL的答案,使用具有不同属性的不同模板,但您也可以使用ng-attr-[nameCustomAttr]="value",对于可选绑定,请参阅我的相关答案,并以这种方式角度使用一种绑定类型,并检查属性是否有值并根据值添加或不添加属性。

为此,属性需要保留在指令/组件的模板上。

注意 :angularjs 仅为出现在 UI 中的变量创建监视。

好吧,祝你好运,我希望它能帮助你。