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/appRequireJS-Modul aufruft , um einen Rückruf abzurufen, und diesen Rückruf dann {types:..., components:...}als Argument im JSON-Objekt übergibt ( datasiehe 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 typesund layoutRequireJS. Die Anwendung lädt auch die Magento_Ui/js/lib/ko/initializeRequireJS-Bibliothek. Das initializeModul 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/engineRequireJS-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 scopeBindung und ruft das ./bind/scopeRequireJS-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.getMethode ein bereits generiertes Objekt unter Verwendung der Zeichenfolge in der componentVariablen als Bezeichner ab und übergibt es applyComponentsals dritten Parameter an die Methode. Die Zeichenkettenkennung ist der Wert von scope:( customer_listing.customer_listingoben)
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 createChildContextwird im Wesentlichen ein neues viewModel-Objekt erstellt, das auf dem bereits instanziierten Komponentenobjekt basiert, und dann auf alle untergeordneten Elemente des verwendeten Originals divangewendet data-bind=scope:.
Also, was ist das bereits instanziierte Komponentenobjekt? Denken Sie daran , um den Anruf layoutauf der Rückseite app.js?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
Die layoutFunktion / der Baustein steigt in das übergebene data.componentsObjekt ab (diese Daten stammen wiederum aus dem übergebenen Objekt text/x-magento-init). Für jedes gefundene Objekt wird nach einem configObjekt gesucht, und in diesem Konfigurationsobjekt wird nach einem componentSchlüssel gesucht. Wenn es einen Komponentenschlüssel findet, wird es
Verwenden Sie RequireJSdiese 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 registryObjekt / 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 scopeWert ist customer_listing.customer_listing.
Betrachten wir das JSON-Objekt aus der text/x-magento-initInitialisierung
{
"*": {
"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_listingObjekt ein configObjekt hat und dass das Konfigurationsobjekt ein Objekt hat component, das auf gesetzt ist uiComponent. Die uiComponentZeichenfolge ist ein RequireJS-Modul. Tatsächlich ist es ein RequireJS-Alias, der dem Magento_Ui/js/lib/core/collectionModul entspricht.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
In layout.jshat 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 collectiones sich um ein JavaScript-Objekt handelt, das sowohl vom lib/core/element/elementModul als auch vom lib/core/classModul verbessert wurde . Die Untersuchung dieser Anpassungen geht über den Rahmen dieser Antwort hinaus.
Nach der Instanziierung wird layout.jsdies objectin der Registrierung gespeichert. Dies bedeutet, wenn Knockout mit der Verarbeitung der Bindungen beginnt und auf die benutzerdefinierte scopeBindung 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 getTemplateMethode, die aufgerufen wird, wenn Knockout die taglose Bindung ( <!-- ko template: getTemplate() --><!-- /ko -->) aufruft, ist die getTemplateMethode für das new collectionObjekt.