通用角度组件 - 可选绑定

时间:2016-03-15 23:52:10

标签: javascript angularjs angularjs-directive

我想创建一堆通用组件(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更新,当我们知道它们时永远都是假的。

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

我创建了一个costum包装器指令,以利用angular 1.5中的延迟转换编译

像这样(伪代码,未经过测试):

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

通过这种方式,我认为只有在ng-if为真时才会编译optional-binding-once中的代码,因此如果没有定义绑定,则会减少一个观察者。

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

嗯,我认为当没有填充可选绑定时,没有一个简单的解决方案来减少组件内的观察者数量。

我通过角度的$ digest阶段运行了一些测试,以检查这类观察者的数量增加是否真的有问题。

以下是我的结果:

针对最坏情况的测试,其中包含888个具有4个可选绑定的组件。

Chrome - 没有可选绑定(888个组件,总观察者889)

  • 总观察者:889
  • 最后摘要周期时间:0.9950000000026193
  • 过去1004个摘要周期的平均时间:1.0544920318724353 ms
  • 开始dom加载(400ms)

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

  • Total Watchers:4441
  • 最后摘要周期时间:1.1549999999988358
  • 过去1001个摘要周期的平均时间:1.6851748251747816 ms
  • 开始dom加载(600ms)

Safari - 没有可选绑定(888个组件,总观察者889)

  • 总观察者:889
  • 最后摘要周期时间:1.0849999999991269
  • 最近530个摘要周期的平均时间:1.211632075471664 ms

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

  • Total Watchers:4441
  • 最后摘要周期时间:1.7450000000026193
  • 过去588个摘要周期的平均时间:2.1167176870748237 ms

结论:

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

5 个答案:

答案 0 :(得分:1)

我会利用这样一个事实: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'}) =&gt; "<div><div ng-if="$ctrl.a"></div></div>"
template(null, {b: '1'}) =&gt; "<div></div>"

已知限制

如果您想从网址中提取模板(并且未在$templateCache中预先填充),则会出现故障,但这似乎不是您的情况。

如果缩小

documentation表示如果template是一个函数,则会注入$element$attrs。这意味着如果您正在缩小代码,请确保使用指定函数参数名称的缩小安全方法。 例如

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

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

template: templateFn,

答案 1 :(得分:1)

所有你需要保持与{{:: variable}}的一次时间绑定,以防止多个观察者。然后你就不会遇到任何性能问题。

答案 2 :(得分:0)

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

答案 3 :(得分:0)

一个好方法是@GregL的答案,使用具有不同属性的不同模板,但是你也可以使用ng-attr- [nameCustomAttr] =“value”,对于可选绑定,请参阅我的{{3}以这种方式,角度使用一种bindingOnce并检查attr是否具有值,并添加或不添加该值的属性。

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

注意:angularjs仅为UI中显示的变量创建监视。

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

答案 4 :(得分:0)

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

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

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