Le code PHP pour un composant d'interface utilisateur rend une initialisation javascript qui ressemble à ceci
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Ce bit de code dans la page signifie que Magento invoquera le Magento_Ui/js/core/app
module RequireJS pour récupérer un rappel, puis appellera ce rappel en passant dans l' {types:..., components:...}
objet JSON comme argument ( data
ci-dessous)
#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'objet de données contient toutes les données nécessaires au rendu du composant d'interface utilisateur, ainsi qu'une configuration qui relie certaines chaînes à certains modules Magento RequireJS. Ce mappage se produit dans les modules types
et layout
RequireJS. L'application charge également la Magento_Ui/js/lib/ko/initialize
bibliothèque RequireJS. Le initialize
module lance l'intégration KnockoutJS de 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();
});
Chaque bind/...
module RequireJS individuel configure une liaison personnalisée unique pour Knockout.
le extender/...
modules RequireJS ajoutent des méthodes d'assistance aux objets KnockoutJS natifs.
Magento étend également les fonctionnalités du moteur de modèle javascript de Knockout dans le ./template/engine
module RequireJS.
Enfin, Magento fait appel applyBindings()
à l'objet KnockoutJS. C'est normalement là qu'un programme Knockout lierait un modèle de vue à la page HTML - cependant, Magento appelle applyBindings
sans modèle de vue. Cela signifie que Knockout commencera à traiter la page comme une vue, mais sans données liées.
Dans une configuration stock Knockout, ce serait un peu idiot. Cependant, en raison des liaisons Knockout personnalisées mentionnées précédemment, Knockout a de nombreuses possibilités de faire des choses.
Nous sommes intéressés par la liaison de portée . Vous pouvez le voir dans ce HTML, également rendu par le système de composants 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>
Plus précisément, l' data-bind="scope: 'customer_listing.customer_listing'">
attribut. Lorsque Magento démarre applyBindings
, Knockout verra cette scope
liaison personnalisée et invoquera le ./bind/scope
module RequireJS. La possibilité d'appliquer une liaison personnalisée est pure KnockoutJS. L' implémentation de la liaison de portée est quelque chose que Magento Inc. a fait.
La mise en œuvre de la liaison de portée est à
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
La partie importante de ce fichier est ici
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Sans entrer trop dans les détails, la registry.get
méthode extraira un objet déjà généré en utilisant la chaîne dans la component
variable comme identifiant et la passera à la applyComponents
méthode comme troisième paramètre. L'identifiant de chaîne est la valeur de scope:
(customer_listing.customer_listing
ci-dessus)
Dans 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);
}
l'appel à createChildContext
créera ce qui est, essentiellement, un nouvel objet viewModel basé sur l'objet composant déjà instancié, puis l'appliquera à tous les éléments descendants de l'original div
utilisé data-bind=scope:
.
Alors, quel est l' objet composant déjà instancié ? Rappelez-vous l'appel à layout
revenir app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
La layout
fonction / module descendra dans le passé data.components
(encore une fois, ces données proviennent de l'objet passé via text/x-magento-init
). Pour chaque objet qu'il trouve, il cherchera un config
objet, et dans cet objet de configuration, il cherchera une component
clé. S'il trouve une clé de composant, il
Permet RequireJS
de renvoyer une instance de module - comme si le module était appelé dans une dépendance requirejs
/ define
.
Appelez cette instance de module en tant que constructeur javascript
Stocker l'objet résultant dans l' registry
objet / module
Donc, c'est beaucoup à retenir. Voici un bref examen, en utilisant
<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>
comme point de départ. La scope
valeur est customer_listing.customer_listing
.
Si nous regardons l'objet JSON depuis l' text/x-magento-init
initialisation
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Nous voyons que l' components.customer_listing.customer_listing
objet a un config
objet, et cet objet de configuration a un component
objet qui est défini sur uiComponent
. La uiComponent
chaîne est un module RequireJS. En fait, c'est un alias RequireJS qui correspond au Magento_Ui/js/lib/core/collection
module.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
Dans layout.js
, Magento a exécuté un code équivalent à ce qui suit.
//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*/})
}
Pour les plus curieux, si vous jetez un œil au modèle de collection et suivez son chemin d'exécution, vous découvrirez qu'il collection
s'agit d'un objet javascript qui a été amélioré à la fois par le lib/core/element/element
module et le lib/core/class
module. La recherche de ces personnalisations dépasse le cadre de cette réponse.
Une fois instancié, layout.js
stocke cela object
dans le registre. Cela signifie que Knockout commence à traiter les liaisons et rencontre la scope
liaison personnalisée
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento récupérera cet objet du registre et le liera comme modèle de vue pour les choses à l'intérieur de div
. En d'autres termes, la getTemplate
méthode qui est appelée lorsque Knockout appelle la liaison sans étiquette ( <!-- ko template: getTemplate() --><!-- /ko -->
) est la getTemplate
méthode sur l' new collection
objet.