Подтвердить что ты не робот

Для initComponent() или нет для initComponent()

Я боюсь при создании приложения в ExtJS 4, и часть этого путаницы в том, когда нужно что-то настраивать в initComponent(), а когда не...

Например, в Sencha собственный MVC Application Architecture doc при первом создании представления сетки они определили встроенное хранилище в методе initComponent(). (См. Раздел "Определение вида" )

Далее, когда они вытеснили хранилище в отдельный класс, они перенесли определение вне initComponent(). Есть полезный комментарий, который обращает внимание на этот факт, но объяснений нет. (См. Раздел "Создание раздела" Модель и магазин ").

Я предполагаю, что причина должна быть очевидной, но мне это не хватает. Любые указатели?

4b9b3361

Ответ 1

Если у вас нет глубокого понимания того, как работает система класса ExtJS, вы можете следовать этому:

Объявить все не-примитивные типы в initComponent().

Терминология

  • Примитивные типы - строки, булевы, целые числа и т.д.
  • Непримитивы - массивы и объекты.

Объяснение

Если компонент, который вы продлеваете, должен быть создан несколько раз, любые не примитивные конфиги, объявленные как опция конфигурации (вне initComponent), будут совместно использоваться всеми экземплярами.

Из-за этого многие люди столкнулись с проблемами, когда расширенный компонент (обычно расширенная сетка) создается на нескольких вкладках.

Это поведение объясняется в ответе sra ниже и в этой статье Skirtle Den. Вы также можете прочитать этот вопрос SO.

Ответ 2

Сначала я остановлюсь на своих комментариях:

@Александр Токарев Не поймите меня неправильно. Я не говорю о конфигурациях компонентов или, что еще хуже, о случаях и переносит их на initComponent(), это не моя точка.

Теперь я думаю об этом.

initComponent() должен разрешить все необходимое время экземпляра этого класса. Не больше, не меньше.

Вы можете испортить нагрузку при определении классов, и большинство из них происходит, потому что люди не понимают, как работает система класса ExtJS. Поскольку речь идет о компонентах, основное внимание будет уделено этим. Это также будет упрощенный пример, который должен показывать только некоторую ошибку, которую я видел много раз.

Давайте начнем: у нас есть настраиваемая панель, которая делает много отличных вещей. Это вызывает необходимость в пользовательской конфигурации, мы называем ее foo. Мы добавляем его вместе с нашей опцией конфигурации по умолчанию для определения класса, чтобы мы могли получить к нему доступ:

Ext.define('Custom', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.custpanel',

    foo: {
        bar: null  
    },

    initComponent: function() {
        this.callParent(arguments);
    }
});

Но после тестирования все становится странно. Значения наших конфигураций, похоже, волшебным образом меняются. Здесь JSFiddle Случилось так, что все созданные экземпляры ссылаются на один и тот же экземпляр foo. Но в последнее время я сделал это

store: {
    fields: [ ... ],
    proxy: {
        type: 'direct',
        directFn: 'Direct.Store.getData'
    }
}

с магазином, и это сработало. Итак, почему не работает foo?

Большинство людей не видит никакой разницы между этим маленьким foo объектом и конфигурацией (ExtJS), которая в основном правильна, потому что оба являются объектами (экземплярами). Но разница в том, что все классы, отправленные sencha, прекрасно знают, какие свойства конфигурации они ожидают и заботятся о них.

Например, свойство хранилища сетки разрешено StoreManager и поэтому может быть:

  • Строка
  • storeId или
  • хранить экземпляр или
  • сохранить конфигурационный объект.

Во время инициализации сетки любой из них получает разрешение и перезаписывается фактическим экземпляром хранилища. Магазин - всего лишь один пример. Я думаю, что более известным является массив элементов. Это массив во время определения и он переопределяется для каждого экземпляра с помощью MixedCollection (если я не ошибаюсь).

Да, существует разница между определением класса и созданным им экземпляром. Но нам нужно позаботиться о любом новом свойстве, которое содержит ссылку типа foo сверху, и это не так сложно. Вот что нам нужно сделать, чтобы исправить его для примера foo

Ext.define('Custom', {
    extend: 'Ext.panel.Panel',
    alias: 'widget.custpanel',

    foo: {
        bar: null  
    },

    initComponent: function() {
        this.foo = Ext.apply({}, this.foo);
        this.callParent(arguments);
    }
});

Здесь JSFiddle

Теперь мы заботимся о config foo при создании экземпляра. Теперь этот пример foo упрощен и не всегда будет легко разрешить конфигурацию.

Заключение

Всегда записывайте определение класса как конфигурации! Они не должны содержат любые упомянутые экземпляры, за исключением простой конфигурации, и должны позаботьтесь об этом, чтобы разрешить их при создании экземпляра.

Отказ

Я не претендую на то, чтобы охватить все это очень короткое письмо!

Ответ 3

Я обычно выступаю за максимально возможную конфигурацию в настройках конфигурации класса, потому что он читает лучше и проще переопределять в подклассах. Кроме того, существует большая вероятность того, что в будущем Sencha Cmd будет иметь оптимизирующий компилятор, поэтому, если вы сохраните свой код декларативным, это может выиграть от оптимизации.

Для сравнения:

Ext.define('MyPanel', {
    extend: 'Ext.grid.Panel',

    initComponent: function() {
        this.callParent();
        this.store = new Ext.data.Store({
            fields: [ ... ],
            proxy: {
                type: 'direct',
                directFn: Direct.Store.getData
            }
        });
        this.foo = 'bar';
    }
});

...

var panel = new MyPanel();

и

Ext.define('MyPanel', {
    extend: 'Ext.grid.Panel',
    alias: 'widget.mypanel',

    foo: 'bar',

    store: {
        fields: [ ... ],
        proxy: {
            type: 'direct',
            directFn: 'Direct.Store.getData'
        }
    }
});

...

var panel = Ext.widget({
    xtype: 'mypanel',
    foo: 'baz'
});

Обратите внимание, что эти подходы очень разные. В первом примере мы сильно кодируем: значения свойства объекта, конфигурацию хранилища, имя класса MyPanel при его использовании; мы практически убиваем идею класса, потому что он становится нерациональным. Во втором примере мы создаем шаблон, который можно многократно использовать с возможной другой конфигурацией - в основном, что касается всей системы классов.

Однако фактическая разница лежит глубже. В первом случае мы фактически откладываем конфигурацию класса до времени выполнения, тогда как во втором случае мы определяем конфигурацию класса и применяем его на очень разных этапах. На самом деле, мы можем легко сказать, что второй подход вводит что-то, чего не хватает JavaScript изначально: фаза времени компиляции. И это дает нам множество возможностей, которые используются в самом коде фреймворка; если вам нужны некоторые примеры, посмотрите Ext.app.Controller и Ext.app.Application в последней версии бета-версии 4.2.

С более практической точки зрения второй подход лучше, потому что его легче читать и решать. Как только вы поймете идею, вы обнаружите, что пишете весь свой код, потому что это просто проще.

Посмотрите на это так: если вы напишете старое веб-приложение стиля, создающее HTML и прочее на стороне сервера, вы бы попытались не смешивать HTML-код с кодом, не так ли? Шаблоны слева, код справа. Это практически то же самое, что и данные жесткого кодирования в initComponent: уверен, что это работает, вплоть до точки. Затем он становится чашей спагетти, которую трудно поддерживать и растягивать. О, и все это проверять! Тьфу.

Теперь раз, когда вам нужно что-то делать с экземпляром во время выполнения, в отличие от времени определения класса - классический пример - использование прослушивателей событий или вызов control в контроллерах, Вам нужно будет взять фактические ссылки на функции из экземпляра объекта, и вы должны сделать это в initComponent или init. Тем не менее, мы работаем над ослаблением этой проблемы - не должно быть жесткого требования жестко кодировать все это; Observable.on() уже поддерживает имена прослушивателей строк, а материал MVC тоже скоро.

Как я уже говорил в комментариях выше, мне придется написать статью или руководство для документов, объясняя вещи. Это, вероятно, придется ждать, пока не будет выпущено 4.2; между тем этот ответ, должно быть, должен пролить свет на этот вопрос.

Ответ 4

Я искал ответ на тот же вопрос, когда я оказался здесь, и, увидев эти ответы, я разочаровался. Ни один из них не отвечает на вопрос: initComponent() или конструктор?

Приятно знать, что объекты класса конфигурации разделяются, и вам нужно инициализировать/обрабатывать их на один экземпляр, но код может перейти в конструктор, а также в функцию initComponent().

Мое предположение заключалось в том, что конструктор класса Component вызывает initComponent() где-то посередине, и я был не очень прав: просто нужно было посмотреть на исходный код, на самом деле Конструктор AbstractComponent.

Итак, это выглядит так:

AbstractComponent/ctor:
- stuffBeforeIC()
- initComponent()
- stuffAfterIC()

Теперь, если вы расширяете компонент, вы получите что-то вроде этого:

constructor: function () {
  yourStuffBefore();
  this.callParent(arguments);
  yourStuffAfter();
},
initComponent: function () {
  this.callParent();
  yourInitComp()
}

Окончательный порядок, вызываемый им:

- yourStuffBefore()
- base ctor by callParent:
  - stuffBeforeIC()
  - initComponent:
    - base initComponent by callParent
    - yourInitComp()
  - stuffAfterIC()
- yourStuffAfter()

Итак, в конце концов, все зависит от того, хотите ли вы/нужно вводить код между stuffBeforeIC и stuffAfterIC, который вы можете найти в конструкторе класса, который будет расширяться.