zoukankan      html  css  js  c++  java
  • EmberJs之数组绑定@each&[]

    写在前面

      好长时间没有写博客了,昨天花了些时间又整理了下之前发布过的《Ember.js之computed Property》文章,并创建了一个测试代码库,花了些时间,希望能使用测试代码的方式,来演示如何使用Ember.js同时能避免升级Ember版本后,一些功能上的变化所带来的隐含Bug。

     

      如果大家对Ember.js有兴趣想一起研究的话,欢迎大家一起维护测试代码库  :)

      本文主要是针对Ember.Array中的[]和@each,数组方法进行详细分析。

     

    文章索引

    JS前端框架之Ember.js系列

     

    一、如何使用[] & @each

      Ember中二者均可用于计算属性的绑定,使用十分方便,可以像如下方式定义依赖关系:

    1 totalArea: Ember.computed('sizeArray.[]', function () {
    2       return this.get('sizeArray').reduce(function (prev, cur) {
    3         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
    4       }, 0);
    5 }),

      或者:

    1 totalArea: Ember.computed(‘sizeArray.@each', function () {
    2       return this.get('sizeArray').reduce(function (prev, cur) {
    3         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
    4       }, 0);
    5 }),

      这样就定义了一个依赖于数组sizeArray的计算属性,建立一个绑定关系,只要sizeArray发生变化,totalArea就会被重新计算,使用十分简单,只要在function前面罗列出依赖的属性即可。虽然使用简单,但是在使用上还是有些细节的,下一节将用测试代码来讲解使用上的细节。

    注:Ember计算属性有多种写法,这里给出了Ember推荐写法,更多具体细节请参考文章《Ember.js之computed Property-1. What is CP》

     

    二、Array.[] & Array.@each特性总结

    (Ember v1.13.7) 

    1. Array.@each.property 特性

    当CP属性依赖于.property('columns.@each.isLoaded')时:

      - columns里面任何一个元素的isLoaded属性发生变化。

      - columns增加元素或者删除子元素。

      - columns数组本身被重新赋值。

      - 不能依赖@each.owner.@each.name 。

     1 test('computed property depend on @each.height', function (assert) {
     2   var Size = Ember.Object.extend({height: 0,  0});
     3   var rectangle = Ember.Object.extend({
     4     totalArea: Ember.computed('sizeArray.@each.height', function () {
     5       return this.get('sizeArray').reduce(function (prev, cur) {
     6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
     7       }, 0);
     8     }),
     9     sizeArray: [
    10       Size.create({height: 10,  10}),
    11       Size.create({height: 10,  10})
    12     ]
    13   }).create();
    14   var sizeArray = rectangle.get('sizeArray');
    15 
    16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
    17 
    18   sizeArray.pushObject(Size.create({height: 10,  10}));
    19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
    20 
    21   sizeArray.removeAt(0);
    22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
    23 
    24   sizeArray[0].set('height', 20);
    25   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after 'height' changed");
    26 
    27   sizeArray[0].set('width', 20);
    28   assert.equal(rectangle.get('totalArea'), 300, "the total area should not be changed after 'width' changed");
    29 
    30   sizeArray.clear();
    31   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
    32 });

    [参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L164-Lundefined)

     

    2. Array.@each特性

    当CP属性依赖于.property('columns.@each')时:

      - columns增加或删除元素。

      - columns自身被替换或重新赋值。

     1 test('computed property depend on @each', function (assert) {
     2   var Size = Ember.Object.extend({height: 0,  0});
     3   var rectangle = Ember.Object.extend({
     4     totalArea: Ember.computed('sizeArray.@each', function () {
     5       return this.get('sizeArray').reduce(function (prev, cur) {
     6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
     7       }, 0);
     8     }),
     9     sizeArray: [
    10       Size.create({height: 10,  10}),
    11       Size.create({height: 10,  10})
    12     ]
    13   }).create();
    14   var sizeArray = rectangle.get('sizeArray');
    15 
    16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
    17 
    18   sizeArray.pushObject(Size.create({height: 10,  10}));
    19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
    20 
    21   sizeArray.removeAt(0);
    22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
    23 
    24   sizeArray[0].set('height', 20);
    25   assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed");
    26 
    27   sizeArray.clear();
    28   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
    29 });

    [参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L104)

     

    3. Array.[]特性

    当CP属性依赖于.property('columns.[]')时:

      - 与绑定.property(‘columns.@each') 行为相同。

     1 test('computed property depend on []', function (assert) {
     2   var Size = Ember.Object.extend({height: 0,  0});
     3   var rectangle = Ember.Object.extend({
     4     totalArea: Ember.computed('sizeArray.[]', function () {
     5       return this.get('sizeArray').reduce(function (prev, cur) {
     6         return prev + Ember.get(cur, 'height') * Ember.get(cur, 'width');
     7       }, 0);
     8     }),
     9     sizeArray: [
    10       Size.create({height: 10,  10}),
    11       Size.create({height: 10,  10})
    12     ]
    13   }).create();
    14   var sizeArray = rectangle.get('sizeArray');
    15 
    16   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200");
    17 
    18   sizeArray.pushObject(Size.create({height: 10,  10}));
    19   assert.equal(rectangle.get('totalArea'), 300, "the total area should be 300 after added");
    20 
    21   sizeArray.removeAt(0);
    22   assert.equal(rectangle.get('totalArea'), 200, "the total area should be 200 after removed");
    23 
    24   sizeArray[0].set('height', 20);
    25   assert.equal(rectangle.get('totalArea'), 200, "the total area should not be changed");
    26 
    27   sizeArray.clear();
    28   assert.equal(rectangle.get('totalArea'), 0, "the total area should be 0 after reset rectangle.sizeArray");
    29 });

    [参考代码](https://github.com/Cuiyansong/ember-table-learnning/blob/master/ember-test/tests/unit/computed-property-test.js#L134)

     

    (Ember v2.0.0)

     

    2015-08-17 除建议用[]代替@each,暂未发现其他变化。

    [参考代码](https://github.com/emberjs/ember.js/blob/v2.0.0/packages/ember-metal/lib/computed.js#L224)

    注: 更多关于计算属性问题,请参考文章《Ember.js之computed Property-3.CP重要原则》

     

    三、源码分析Array.[] & Array.@each

    Array.@each返回的是一个特殊类型,这个类型便于实现绑定。

     1   '@each': computed(function() {
     2     if (!this.__each) {
     3       // ES6TODO: GRRRRR
     4       var EachProxy = requireModule('ember-runtime/system/each_proxy')['EachProxy'];
     5 
     6       this.__each = new EachProxy(this);
     7     }
     8 
     9     return this.__each;
    10   })

    [参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js#L516)

     

    Array.[]继承自Ember.Enumerable.[]并且返回this。

    1   '[]': computed({
    2     get(key) {
    3       return this;
    4     },
    5     set(key, value) {
    6       this.replace(0, get(this, 'length'), value);
    7       return this;
    8     }
    9   }),

    [参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/mixins/array.js#L164)

     

    二者之间唯一的区别是Array.[]返回的是一对象自身(普通对象组成的数组), 而Array.@each返回的是EachProxy对象, 针对普通对象组成的数组而言,仅仅能检测到数组长度的变化和对象本身的变化,对数组内容发生变化则不得而知,而EachProxy对每一个元素增加observerListener监听器,当数组内容发生变化时,通知数组发生变更,实现了数组元素这一级别的监听。

     1 var EachProxy = EmberObject.extend({
     2 
     3   init(content) {
     4     this._super(...arguments);
     5     this._content = content;
     6     content.addArrayObserver(this);
     7 
     8     // in case someone is already observing some keys make sure they are
     9     // added
    10     forEach(watchedEvents(this), function(eventName) {
    11       this.didAddListener(eventName);
    12     }, this);
    13   },
    14 
    15 ...

    [参考这里](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each_proxy.js#L109)

     

    四、相关引用

    [Ember-@each](http://emberjs.com/api/classes/Ember.Array.html#property__each)

    [Emer-EachProxy](https://github.com/emberjs/ember.js/blob/stable-1-13/packages/ember-runtime/lib/system/each_proxy.js)

    [Ember-study](https://github.com/Cuiyansong/ember-table-learnning)

     

  • 相关阅读:
    初入水:vector
    Sort Colors
    Palindrome Partitioning II
    Search for a Range
    Container With Most Water
    Palindrome Partitioning
    Longest Consecutive Sequence
    简单写了一个堆排序
    Best Time to Buy and Sell Stock III
    4-7
  • 原文地址:https://www.cnblogs.com/cuiyansong/p/4738274.html
Copyright © 2011-2022 走看看