Код PHP для компонента інтерфейсу надає ініціалізацію javascript, яка виглядає приблизно так
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Цей біт коду на сторінці означає, що Magento викликає Magento_Ui/js/core/app
модуль RequireJS для отримання зворотного дзвінка, а потім викликає цей зворотний виклик, що передається в {types:..., components:...}
об'єкт JSON як аргумент ( data
нижче)
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
define([
'./renderer/types',
'./renderer/layout',
'Magento_Ui/js/lib/ko/initialize'
], function (types, layout) {
'use strict';
return function (data) {
types.set(data.types);
layout(data.components);
};
});
Об'єкт даних містить усі дані, необхідні для візуалізації компонента інтерфейсу, а також конфігурацію, яка пов'язує певні рядки з певними модулями Magento RequireJS. Це відображення відбувається в модулях types
і layout
RequireJS. Додаток також завантажує Magento_Ui/js/lib/ko/initialize
бібліотеку RequireJS. У initialize
модулі штовхає інтеграцію KnockoutJS Magento в.
/**
* Copyright © 2016 Magento. All rights reserved.
* See COPYING.txt for license details.
*/
/** Loads all available knockout bindings, sets custom template engine, initializes knockout on page */
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
define([
'ko',
'./template/engine',
'knockoutjs/knockout-repeat',
'knockoutjs/knockout-fast-foreach',
'knockoutjs/knockout-es5',
'./bind/scope',
'./bind/staticChecked',
'./bind/datepicker',
'./bind/outer_click',
'./bind/keyboard',
'./bind/optgroup',
'./bind/fadeVisible',
'./bind/mage-init',
'./bind/after-render',
'./bind/i18n',
'./bind/collapsible',
'./bind/autoselect',
'./extender/observable_array',
'./extender/bound-nodes'
], function (ko, templateEngine) {
'use strict';
ko.setTemplateEngine(templateEngine);
ko.applyBindings();
});
Кожен окремий bind/...
модуль RequireJS встановлює єдину власну прив'язку для нокауту.
У extender/...
RequireJS модулі додати деякі допоміжні методи для власних об'єктів KnockoutJS.
Magento також розширює функціональність механізму шаблонів JavaScript Knockout в ./template/engine
модулі RequireJS.
Нарешті, Magento викликає applyBindings()
об'єкт KnockoutJS. Зазвичай програма Knockout прив'язує модель перегляду до сторінки HTML, однак Magento дзвонить applyBindings
без моделі перегляду. Це означає, що Knockout почне обробляти сторінку як перегляд, але без жодних даних.
У налаштуваннях нокауту це було б трохи нерозумно. Однак через раніше згадані користувацькі прив'язки на нокауті, у Knockout є багато можливостей робити щось.
Нас цікавить обов'язковість сфери застосування . Ви можете бачити, що в цьому HTML, також виведеному системою PHP UI Component.
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
<div class="spinner">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<!-- ko template: getTemplate() --><!-- /ko -->
<script type="text/x-magento-init">
</script>
</div>
Зокрема, data-bind="scope: 'customer_listing.customer_listing'">
атрибут. Коли Magento стартує applyBindings
, Knockout побачить цю власну scope
прив'язку та викликає ./bind/scope
модуль RequireJS. Можливість застосувати власну прив'язку є чистою KnockoutJS. Реалізація сфери зв'язування є те , що Magento Inc. зробив.
Реалізація прив'язки сфери дії знаходиться на
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Тут важливий біт
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Не вдаючись в деталі, то registry.get
метод буде витягнути вже створений об'єкт , використовуючи рядок в component
змінної в якості ідентифікатора, і передати його applyComponents
методу в якості третьої параметра. Ідентифікатор рядка - це значення scope:
( customer_listing.customer_listing
вище)
В applyComponents
function applyComponents(el, bindingContext, component) {
component = bindingContext.createChildContext(component);
ko.utils.extend(component, {
$t: i18n
});
ko.utils.arrayForEach(el.childNodes, ko.cleanNode);
ko.applyBindingsToDescendants(component, el);
}
заклик до createChildContext
створить те, що є, по суті, новим об'єктом viewModel на основі вже створеного компонентного об'єкта, а потім застосує його до всього нащадкового елемента оригіналу, div
який використовувався data-bind=scope:
.
Отже, що таке вже створений об'єкт компонента? Пам'ятаєте дзвінок, щоб layout
повернутися назад app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
layout
Функція / модуль буде спускатися в переданому в data.components
(знову ж , це дані надходять від об'єкта , переданого в через text/x-magento-init
). Для кожного об'єкта, який він знайде, він буде шукати config
об'єкт, і в цьому об’єкті конфігурації він шукатиме component
ключ. Якщо він знайде компонентний ключ, він буде
Використовуйте RequireJS
для повернення екземпляра модуля - як би модуль викликався в requirejs
/ define
залежності.
Викликайте цей екземпляр модуля як конструктор javascript
Збережіть отриманий об'єкт у registry
об'єкті / модулі
Отже, це багато для чого взяти. Ось короткий огляд із використанням
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
<div class="spinner">
<span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
</div>
</div>
<!-- ko template: getTemplate() --><!-- /ko -->
<script type="text/x-magento-init">
</script>
</div>
як вихідний пункт. scope
Значення customer_listing.customer_listing
.
Якщо ми подивимось на об'єкт JSON з text/x-magento-init
ініціалізації
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Ми бачимо, що в components.customer_listing.customer_listing
об'єкта є config
об'єкт, а в цьому конфігураційному об'єкті встановлений component
об'єкт uiComponent
. uiComponent
Рядок являє собою модуль RequireJS. Фактично, його псевдонім RequireJS відповідає Magento_Ui/js/lib/core/collection
модулю.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
У layout.js
Magento запущений код, еквівалентний наступному.
//The actual code is a bit more complicated because it
//involves jQuery's promises. This is already a complicated
//enough explanation without heading down that path
require(['Magento_Ui/js/lib/core/collection'], function (collection) {
object = new collection({/*data from x-magento-init*/})
}
По-справжньому цікавим, якщо ви подивитеся на модель колекції і дотримуєтесь її шляху виконання, ви виявите, що collection
це об’єкт javascript, який був розширений і lib/core/element/element
модулем, і lib/core/class
модулем. Дослідження цих налаштувань виходить за рамки цієї відповіді.
Після того, як миттєво встановлено, layout.js
зберігає це object
в реєстрі. Це означає, що Knockout починає обробку прив’язок і стикається з власною scope
прив'язкою
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento поверне цей об’єкт із реєстру та прив’яже його як модель перегляду для речей всередині div
. Іншими словами, getTemplate
метод, який викликається, коли Knockout викликає зв'язок tagless ( <!-- ko template: getTemplate() --><!-- /ko -->
), - це getTemplate
метод на new collection
об'єкті.