Il codice PHP per un componente UI esegue il rendering di un'inizializzazione javascript simile a questa
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Questo bit di codice nella pagina indica che Magento invocherà il Magento_Ui/js/core/appmodulo RequireJS per recuperare un callback, quindi chiamerà quel callback passando {types:..., components:...}nell'oggetto JSON come argomento ( datasotto)
#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);
};
});
L'oggetto dati contiene tutti i dati necessari per eseguire il rendering del componente UI, nonché una configurazione che collega determinate stringhe con determinati moduli Magento RequireJS. Tale mappatura avviene nei moduli typese layoutRequireJS. L'applicazione carica anche la Magento_Ui/js/lib/ko/initializelibreria RequireJS. Il initializemodulo dà il via all'integrazione di Magento KnockoutJS.
/**
* 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();
});
Ogni singolo bind/...modulo RequireJS imposta una singola associazione personalizzata per Knockout.
Il extender/... moduli RequireJS aggiungono alcuni metodi di supporto agli oggetti nativi KnockoutJS.
Magento estende anche la funzionalità del motore di template javascript di Knockout nel ./template/enginemodulo RequireJS.
Infine, Magento chiama applyBindings()l'oggetto KnockoutJS. Questo è normalmente dove un programma Knockout vincolerebbe un modello di vista alla pagina HTML, tuttavia Magento chiama applyBindings senza un modello di vista. Ciò significa che Knockout inizierà a elaborare la pagina come vista, ma senza alcun limite di dati.
In una configurazione Knockout di serie, questo sarebbe un po 'sciocco. Tuttavia, a causa dei collegamenti Knockout personalizzati precedentemente menzionati, ci sono molte opportunità per Knockout di fare qualcosa.
Siamo interessati all'ambito dell'associazione. Puoi vederlo in questo HTML, reso anche dal sistema dei componenti dell'interfaccia utente di PHP.
<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>
In particolare, l' data-bind="scope: 'customer_listing.customer_listing'">attributo. Quando Magento prende il via applyBindings, Knockout vedrà questa scopeassociazione personalizzata e invocherà il ./bind/scopemodulo RequireJS. La possibilità di applicare un'associazione personalizzata è pura KnockoutJS. L' implementazione del binding dell'ambito è qualcosa che Magento Inc. ha fatto.
L'implementazione del perimetro di applicazione è a
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Il bit importante in questo file è qui
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Senza entrare troppo nei dettagli, il registry.getmetodo estrarrà un oggetto già generato usando la stringa nella componentvariabile come identificatore e lo passerà al applyComponentsmetodo come terzo parametro. L'identificatore di stringa è il valore di scope:(customer_listing.customer_listing sopra)
Nel 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);
}
la chiamata a createChildContextcreerà quello che è, essenzialmente, un nuovo oggetto viewModel basato sull'oggetto componente già istanziato, quindi lo applicherà a tutti gli elementi discendenti dell'originale divutilizzato data-bind=scope:.
Quindi, qual è l' oggetto componente già istanziato ? Ricordi la chiamata per layoutrientrare app.js?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
La layoutfunzione / modulo scenderà nel passato data.components(di nuovo, questi dati provengono dall'oggetto passato tramite text/x-magento-init). Per ogni oggetto che trova, cercherà un configoggetto e in quell'oggetto di configurazione cercherà una componentchiave. Se trova una chiave componente, lo farà
Utilizzare RequireJSper restituire un'istanza del modulo - come se il modulo fosse chiamato in una requirejs/ definedipendenza.
Chiama l'istanza del modulo come costruttore javascript
Memorizza l'oggetto risultante registrynell'oggetto / modulo
Quindi, è molto da prendere in considerazione. Ecco una breve recensione, utilizzando
<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>
come punto di partenza. Il scopevalore è customer_listing.customer_listing.
Se osserviamo l'oggetto JSON text/x-magento-initdall'inizializzazione
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Vediamo che l' components.customer_listing.customer_listingoggetto ha un configoggetto e quell'oggetto di configurazione ha un componentoggetto su cui è impostato uiComponent. La uiComponentstringa è un modulo RequireJS. In realtà, è un alias RequireJS che corrisponde al Magento_Ui/js/lib/core/collectionmodulo.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
In layout.js, Magento ha eseguito un codice equivalente al seguente.
//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*/})
}
Per i davvero curiosi, se dai un'occhiata al modello di raccolta e segui il suo percorso di esecuzione, scoprirai che collectionè un oggetto javascript che è stato migliorato sia dal lib/core/element/elementmodulo che dal lib/core/classmodulo. La ricerca di queste personalizzazioni va oltre lo scopo di questa risposta.
Una volta istanziato, lo layout.jsmemorizza objectnel registro. Ciò significa che Knockout inizia a elaborare i binding e incontra l' scopeassociazione personalizzata
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento recupererà questo oggetto dal registro e lo legherà come modello di visualizzazione per le cose all'interno di div. In altre parole, il getTemplatemetodo che viene chiamato quando Knockout invoca l'associazione senza tag ( <!-- ko template: getTemplate() --><!-- /ko -->) è il getTemplatemetodo new collectionsull'oggetto.