Der PHP-Code für eine UI-Komponente gibt eine JavaScript-Initialisierung wieder, die so aussieht
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Dieses Codebit auf der Seite bedeutet, dass Magento das Magento_Ui/js/core/app
RequireJS-Modul aufruft , um einen Rückruf abzurufen, und diesen Rückruf dann {types:..., components:...}
als Argument im JSON-Objekt übergibt ( data
siehe unten).
#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);
};
});
Das Datenobjekt enthält alle zum Rendern der UI-Komponente erforderlichen Daten sowie eine Konfiguration, die bestimmte Zeichenfolgen mit bestimmten Magento RequireJS-Modulen verknüpft. Diese Zuordnung erfolgt in den Modulen types
und layout
RequireJS. Die Anwendung lädt auch die Magento_Ui/js/lib/ko/initialize
RequireJS-Bibliothek. Das initialize
Modul startet die KnockoutJS-Integration von 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();
});
Jedes einzelne bind/...
RequireJS-Modul richtet eine einzelne benutzerdefinierte Bindung für Knockout ein.
Die extender/...
RequireJS-Module fügen systemeigenen KnockoutJS-Objekten einige Hilfsmethoden hinzu.
Magento erweitert auch die Funktionalität der JavaScript-Template-Engine von Knockout im ./template/engine
RequireJS-Modul.
Schließlich ruft Magento applyBindings()
das KnockoutJS-Objekt auf. Dies ist normalerweise der Fall, wenn ein Knockout-Programm ein Ansichtsmodell an die HTML-Seite bindet. Magento ruft jedoch applyBindings
ohne Ansichtsmodell auf. Dies bedeutet, dass Knockout die Seite als Ansicht verarbeitet, jedoch keine Daten gebunden sind.
In einem Standard-Knockout-Setup wäre dies ein wenig albern. Aufgrund der zuvor erwähnten benutzerdefinierten Knockout-Bindungen gibt es für Knockout jedoch viele Möglichkeiten, Dinge zu tun.
Wir interessieren uns für die Umfangsbindung . Sie können dies in diesem HTML sehen, das auch vom PHP UI Component-System gerendert wird.
<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>
Insbesondere das data-bind="scope: 'customer_listing.customer_listing'">
Attribut. Wenn Magento startet applyBindings
, sieht Knockout diese benutzerdefinierte scope
Bindung und ruft das ./bind/scope
RequireJS-Modul auf. Die Möglichkeit, eine benutzerdefinierte Bindung anzuwenden, ist reines KnockoutJS. Die Implementierung der Scope-Bindung wurde von Magento Inc. durchgeführt.
Die Umsetzung der Scope-Bindung erfolgt um
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Das wichtige Bit in dieser Datei ist hier
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Ohne auf die Details einzugehen, ruft die registry.get
Methode ein bereits generiertes Objekt unter Verwendung der Zeichenfolge in der component
Variablen als Bezeichner ab und übergibt es applyComponents
als dritten Parameter an die Methode. Die Zeichenkettenkennung ist der Wert von scope:
( customer_listing.customer_listing
oben)
Im 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);
}
Durch den Aufruf von createChildContext
wird im Wesentlichen ein neues viewModel-Objekt erstellt, das auf dem bereits instanziierten Komponentenobjekt basiert, und dann auf alle untergeordneten Elemente des verwendeten Originals div
angewendet data-bind=scope:
.
Also, was ist das bereits instanziierte Komponentenobjekt? Denken Sie daran , um den Anruf layout
auf der Rückseite app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
Die layout
Funktion / der Baustein steigt in das übergebene data.components
Objekt ab (diese Daten stammen wiederum aus dem übergebenen Objekt text/x-magento-init
). Für jedes gefundene Objekt wird nach einem config
Objekt gesucht, und in diesem Konfigurationsobjekt wird nach einem component
Schlüssel gesucht. Wenn es einen Komponentenschlüssel findet, wird es
Verwenden Sie RequireJS
diese Option , um eine Modulinstanz zurückzugeben - als ob das Modul in einer Abhängigkeit requirejs
/ aufgerufen worden wäre define
.
Rufen Sie diese Modulinstanz als JavaScript-Konstruktor auf
Speichern Sie das resultierende Objekt im registry
Objekt / Modul
Das ist also eine Menge zu beachten. Hier ist eine kurze Übersicht über die Verwendung von
<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>
als Ausgangspunkt. Der scope
Wert ist customer_listing.customer_listing
.
Betrachten wir das JSON-Objekt aus der text/x-magento-init
Initialisierung
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Wir sehen, dass das components.customer_listing.customer_listing
Objekt ein config
Objekt hat und dass das Konfigurationsobjekt ein Objekt hat component
, das auf gesetzt ist uiComponent
. Die uiComponent
Zeichenfolge ist ein RequireJS-Modul. Tatsächlich ist es ein RequireJS-Alias, der dem Magento_Ui/js/lib/core/collection
Modul entspricht.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
In layout.js
hat Magento Code ausgeführt, der dem folgenden entspricht.
//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*/})
}
Wenn Sie einen Blick in das Sammlungsmodell werfen und dessen Ausführungspfad verfolgen, werden Sie feststellen, dass collection
es sich um ein JavaScript-Objekt handelt, das sowohl vom lib/core/element/element
Modul als auch vom lib/core/class
Modul verbessert wurde . Die Untersuchung dieser Anpassungen geht über den Rahmen dieser Antwort hinaus.
Nach der Instanziierung wird layout.js
dies object
in der Registrierung gespeichert. Dies bedeutet, wenn Knockout mit der Verarbeitung der Bindungen beginnt und auf die benutzerdefinierte scope
Bindung stößt
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento holt dieses Objekt wieder aus der Registrierung und bindet es als Ansichtsmodell für die Objekte in der div
. Mit anderen Worten, die getTemplate
Methode, die aufgerufen wird, wenn Knockout die taglose Bindung ( <!-- ko template: getTemplate() --><!-- /ko -->
) aufruft, ist die getTemplate
Methode für das new collection
Objekt.