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/app
wywoł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 ( data
poniż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 types
i layout
RequireJS. Aplikacja ładuje również Magento_Ui/js/lib/ko/initialize
bibliotekę RequireJS. Te initialize
rzuty 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/engine
module 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 scope
powiązanie i ./bind/scope
wywoł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.get
metoda wyciągnie już wygenerowany obiekt, używając łańcucha w component
zmiennej jako identyfikatora, i przekaże go do applyComponents
metody jako trzeciego parametru. Identyfikator ciągu to wartość scope:
( customer_listing.customer_listing
powyż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 createChildContext
to utworzy nowy obiekt viewModel oparty na już utworzonym obiekcie składowym, a następnie zastosuje go do wszystkich elementów potomnych oryginału, div
który został użyty data-bind=scope:
.
Czym jest już utworzony obiekt komponentu? Pamiętasz wezwanie do layout
powrotu app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
layout
Funkcji / 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ł config
obiektu, aw tym obiekcie konfiguracyjnym będzie szukał component
klucza. Jeśli znajdzie klucz komponentu, to zrobi to
Służy RequireJS
do 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 registry
obiekcie / 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. scope
Wartość customer_listing.customer_listing
.
Jeśli spojrzymy na obiekt JSON z text/x-magento-init
inicjalizacji
{
"*": {
"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_listing
obiekt ma config
obiekt, a ten obiekt konfiguracyjny ma component
obiekt ustawiony na uiComponent
. uiComponent
Ciąg jest moduł RequireJS. W rzeczywistości jest to alias RequireJS, który odpowiada Magento_Ui/js/lib/core/collection
modułowi.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
W layout.js
Magento 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 collection
jest to obiekt javascript, który został ulepszony zarówno przez lib/core/element/element
moduł, jak i lib/core/class
moduł. Badanie tych dostosowań wykracza poza zakres tej odpowiedzi.
Po utworzeniu wystąpienia layout.js
zapisuje to object
w rejestrze. Oznacza to, że Knockout rozpoczyna przetwarzanie powiązań i napotyka niestandardowe scope
powią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, getTemplate
metoda wywoływana, gdy Knockout wywołuje tagless binding ( <!-- ko template: getTemplate() --><!-- /ko -->
), jest getTemplate
metodą na new collection
obiekcie.
<uiComponents/>
z układu XML układu. Korzyści, jakie otrzymują, to możliwość zamiany modeli widoków na tej samej stronie na inny zestaw tagów.Wiązanie dowolnego z nokautowych szablonów JS odbywa się w plikach .xml modułu. Korzystając z modułu Checkout jako przykładu, możesz znaleźć konfigurację
content
szablonu wvendor/magento/module-checkout/view/frontend/layout/default.xml
W tym pliku widać, że klasa bloku ma węzły, które definiują „jsLayout” i wywołują
<item name="minicart_content" xsi:type="array">
. To trochę okrągły logik, ale jeśli tamvendor/magento/module-checkout/view/frontend/templates/cart/minicart.phtml
jesteś, zobaczysz następującą linię:Więc danych wiążą kieruje gdzie szukać jakiejkolwiek zagnieżdżonego szablonu, w tym przypadku jest to
Magento_Checkout/js/view/minicart
odvendor/magento/module-checkout/view/frontend/web/js/view/minicart.js
do logiki (lub MV w nokautów Model-View-View System modelu) i maszMagento_Checkout/minicart/content
(lub V w nokautów Model-View-Widok Model system) dla wywołania szablonu. Tak więc szablon, który jest pobierany w tym miejscu, tovendor/magento/module-checkout/view/frontend/web/template/minicart/content.html
.Naprawdę nie jest trudno to rozgryźć, kiedy przyzwyczaisz się do przeglądania plików .xml. Większość tego nauczyłem się tutaj, jeśli potrafisz przejść przez zepsuty angielski. Ale do tej pory uważam, że integracja Knockout jest najmniej udokumentowaną częścią M2.
źródło
Jestem prawie pewien, że globalna
getTemplate
metoda JS, której szukasz, jest zdefiniowanaapp/code/Magento/Ui/view/base/web/js/lib/core/element/element.js
, możesz ją znaleźć tutaj: https://github.com/magento/magento2/blob/4d71bb4780625dce23274c90e45788a72f345dd9/app/code/Magento/Ui/view/base /web/js/lib/core/element/element.js#L262Gdy rozmawiam przez telefon, trudno mi się dowiedzieć, jak przebiega wiązanie.
źródło