Код 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 устанавливает одну привязку для Knockout.
В extender/...
RequireJS модули добавить некоторые вспомогательные методы для собственных объектов KnockoutJS.
Magento также расширяет функциональность механизма шаблонов JavaScript Knockout в ./template/engine
модуле RequireJS.
Наконец, Magento вызывает applyBindings()
объект KnockoutJS. Обычно в этом случае программа Knockout связывает модель представления с HTML-страницей, однако Magento вызывает applyBindings
без модели представления. Это означает, что Knockout начнет обрабатывать страницу как представление, но без привязки к данным.
В настройке Knockout это было бы немного глупо. Тем не менее, из-за ранее упомянутых пользовательских привязок Knockout, у Knockout есть много возможностей что-то сделать.
Мы заинтересованы в привязке объема . Вы можете видеть это в этом HTML, также представленном системой компонентов PHP UI.
<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 вызывает связывание без тега ( <!-- ko template: getTemplate() --><!-- /ko -->
), является getTemplate
методом new collection
объекта.