Код 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і layoutRequireJS. Додаток також завантажує 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.jsMagento запущений код, еквівалентний наступному.
//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об'єкті.