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/app
modulo RequireJS per recuperare un callback, quindi chiamerà quel callback passando {types:..., components:...}
nell'oggetto JSON come argomento ( data
sotto)
#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 types
e layout
RequireJS. L'applicazione carica anche la Magento_Ui/js/lib/ko/initialize
libreria RequireJS. Il initialize
modulo 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/engine
modulo 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 scope
associazione personalizzata e invocherà il ./bind/scope
modulo 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.get
metodo estrarrà un oggetto già generato usando la stringa nella component
variabile come identificatore e lo passerà al applyComponents
metodo 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 createChildContext
creerà quello che è, essenzialmente, un nuovo oggetto viewModel basato sull'oggetto componente già istanziato, quindi lo applicherà a tutti gli elementi discendenti dell'originale div
utilizzato data-bind=scope:
.
Quindi, qual è l' oggetto componente già istanziato ? Ricordi la chiamata per layout
rientrare app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
La layout
funzione / 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 config
oggetto e in quell'oggetto di configurazione cercherà una component
chiave. Se trova una chiave componente, lo farà
Utilizzare RequireJS
per restituire un'istanza del modulo - come se il modulo fosse chiamato in una requirejs
/ define
dipendenza.
Chiama l'istanza del modulo come costruttore javascript
Memorizza l'oggetto risultante registry
nell'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 scope
valore è customer_listing.customer_listing
.
Se osserviamo l'oggetto JSON text/x-magento-init
dall'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_listing
oggetto ha un config
oggetto e quell'oggetto di configurazione ha un component
oggetto su cui è impostato uiComponent
. La uiComponent
stringa è un modulo RequireJS. In realtà, è un alias RequireJS che corrisponde al Magento_Ui/js/lib/core/collection
modulo.
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/element
modulo che dal lib/core/class
modulo. La ricerca di queste personalizzazioni va oltre lo scopo di questa risposta.
Una volta istanziato, lo layout.js
memorizza object
nel registro. Ciò significa che Knockout inizia a elaborare i binding e incontra l' scope
associazione 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 getTemplate
metodo che viene chiamato quando Knockout invoca l'associazione senza tag ( <!-- ko template: getTemplate() --><!-- /ko -->
) è il getTemplate
metodo new collection
sull'oggetto.