Kod PHP dla komponentu interfejsu użytkownika renderuje inicjalizację javascript, która wygląda następująco
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Ten fragment kodu na stronie oznacza, że Magento Magento_Ui/js/core/appwywoła moduł RequireJS w celu pobrania wywołania zwrotnego, a następnie wywoła to wywołanie zwrotne w {types:..., components:...}obiekcie JSON jako argument ( dataponiżej)
#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);
};
});
Obiekt danych zawiera wszystkie dane potrzebne do renderowania komponentu interfejsu użytkownika, a także konfigurację, która łączy określone ciągi z niektórymi modułami Magento RequireJS. To mapowanie odbywa się w modułach typesi layoutRequireJS. Aplikacja ładuje również Magento_Ui/js/lib/ko/initializebibliotekę RequireJS. Te initializerzuty modułów off integracji KnockoutJS 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();
});
Każdy bind/...moduł RequireJS tworzy pojedyncze niestandardowe powiązanie dla Knockout.
Te extender/...moduły RequireJS dodać pewne metody pomocnika do rodzimych KnockoutJS obiektów.
Magento rozszerza także funkcjonalność silnika szablonów javascript w Knockout w ./template/enginemodule RequireJS.
Wreszcie Magento wywołuje applyBindings()obiekt KnockoutJS. Jest to zwykle miejsce, w którym program Knockout powiązałby model widoku ze stroną HTML - jednak Magento wywołuje applyBindings bez modelu widoku. Oznacza to, że Knockout rozpocznie przetwarzanie strony jako widoku, ale bez powiązania danych.
W standardowej konfiguracji Knockout byłoby to trochę głupie. Jednak ze względu na wspomniane wcześniej niestandardowe wiązania Knockout, Knockout ma wiele możliwości do zrobienia różnych rzeczy.
Jesteśmy zainteresowani wiążącym zakresem . Możesz to zobaczyć w tym kodzie HTML, również renderowanym przez system komponentu interfejsu użytkownika 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>
W szczególności data-bind="scope: 'customer_listing.customer_listing'">atrybut. Kiedy Magento się rozpocznie applyBindings, Knockout zobaczy to niestandardowe scopepowiązanie i ./bind/scopewywoła moduł RequireJS. Możliwość zastosowania niestandardowego wiązania jest czysta KnockoutJS. Realizacja wiążącego zakresie jest coś Magento Inc. zrobiła.
Implementacja wiązania zakresu jest na
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Ważny fragment tego pliku jest tutaj
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Bez wchodzenia w szczegóły, registry.getmetoda wyciągnie już wygenerowany obiekt, używając łańcucha w componentzmiennej jako identyfikatora, i przekaże go do applyComponentsmetody jako trzeciego parametru. Identyfikator ciągu to wartość scope:( customer_listing.customer_listingpowyżej)
W 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);
}
wywołanie createChildContextto utworzy nowy obiekt viewModel oparty na już utworzonym obiekcie składowym, a następnie zastosuje go do wszystkich elementów potomnych oryginału, divktóry został użyty data-bind=scope:.
Czym jest już utworzony obiekt komponentu? Pamiętasz wezwanie do layoutpowrotu app.js?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
layoutFunkcji / moduł spadnie do przekazany w data.components(ponownie, w tym dane pochodzą z obiektu został przekazany text/x-magento-init). Dla każdego znalezionego obiektu będzie szukał configobiektu, aw tym obiekcie konfiguracyjnym będzie szukał componentklucza. Jeśli znajdzie klucz komponentu, to zrobi to
Służy RequireJSdo zwracania instancji modułu - tak jakby moduł był wywoływany w zależności requirejs/ define.
Wywołaj tę instancję modułu jako konstruktor javascript
Zapisz wynikowy obiekt w registryobiekcie / module
To jest więc bardzo dużo do zrobienia. Oto krótka recenzja, za pomocą
<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>
jako punkt wyjścia. scopeWartość customer_listing.customer_listing.
Jeśli spojrzymy na obiekt JSON z text/x-magento-initinicjalizacji
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Widzimy, że components.customer_listing.customer_listingobiekt ma configobiekt, a ten obiekt konfiguracyjny ma componentobiekt ustawiony na uiComponent. uiComponentCiąg jest moduł RequireJS. W rzeczywistości jest to alias RequireJS, który odpowiada Magento_Ui/js/lib/core/collectionmodułowi.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
W layout.jsMagento uruchomiono kod, który jest równoważny z poniższym.
//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*/})
}
Dla naprawdę ciekawych, jeśli spojrzysz na model kolekcji i podążasz jego ścieżką wykonania, odkryjesz, że collectionjest to obiekt javascript, który został ulepszony zarówno przez lib/core/element/elementmoduł, jak i lib/core/classmoduł. Badanie tych dostosowań wykracza poza zakres tej odpowiedzi.
Po utworzeniu wystąpienia layout.jszapisuje to objectw rejestrze. Oznacza to, że Knockout rozpoczyna przetwarzanie powiązań i napotyka niestandardowe scopepowiązanie
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento pobierze ten obiekt z rejestru i powiąże go jako model widoku dla elementów wewnątrz div. Innymi słowy, getTemplatemetoda wywoływana, gdy Knockout wywołuje tagless binding ( <!-- ko template: getTemplate() --><!-- /ko -->), jest getTemplatemetodą na new collectionobiekcie.