El código PHP para un componente de UI representa una inicialización de JavaScript que se ve así
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Este bit de código en la página significa que Magento invocará el Magento_Ui/js/core/app
módulo RequireJS para recuperar una devolución de llamada, y luego llamará a esa devolución de llamada que pasa en el {types:..., components:...}
objeto JSON como argumento (a data
continuación)
#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);
};
});
El objeto de datos contiene todos los datos necesarios para representar el componente de la interfaz de usuario, así como una configuración que vincula ciertas cadenas con ciertos módulos Magento RequireJS. Esa asignación ocurre en los módulos types
y layout
RequireJS. La aplicación también carga la Magento_Ui/js/lib/ko/initialize
biblioteca RequireJS. El initialize
módulo inicia la integración 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();
});
Cada bind/...
módulo RequireJS individual configura un enlace personalizado único para Knockout.
Los extender/...
módulos RequireJS agregan algunos métodos auxiliares a los objetos nativos de KnockoutJS.
Magento también extiende la funcionalidad del motor de plantillas javascript de Knockout en el ./template/engine
módulo RequireJS.
Finalmente, Magento llama applyBindings()
al objeto KnockoutJS. Esto es normalmente donde un programa Knockout vincularía un modelo de vista a la página HTML; sin embargo, Magento llama applyBindings
sin un modelo de vista. Esto significa que Knockout comenzará a procesar la página como una vista, pero sin datos vinculados.
En una configuración original de Knockout, esto sería un poco tonto. Sin embargo, debido a los enlaces de Knockout personalizados mencionados anteriormente, hay muchas oportunidades para que Knockout haga cosas.
Estamos interesados en el alcance vinculante. Puede ver eso en este HTML, también representado por el sistema de componentes 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>
Específicamente, el data-bind="scope: 'customer_listing.customer_listing'">
atributo. Cuando se inicie Magento applyBindings
, Knockout verá este scope
enlace personalizado e invocará el ./bind/scope
módulo RequireJS. La capacidad de aplicar un enlace personalizado es KnockoutJS puro. La implementación del enlace de alcance es algo que Magento Inc. ha hecho.
La implementación del enlace de alcance está en
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Lo importante en este archivo está aquí
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Sin entrar demasiado en los detalles, el registry.get
método extraerá un objeto ya generado utilizando la cadena en la component
variable como identificador, y lo pasará al applyComponents
método como el tercer parámetro. El identificador de cadena es el valor de scope:
( customer_listing.customer_listing
arriba)
En 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 llamada a createChildContext
creará lo que es, esencialmente, un nuevo objeto viewModel basado en el objeto componente ya instanciado, y luego lo aplicará a todos los elementos descendientes del original div
que se usó data-bind=scope:
.
Entonces, ¿cuál es el objeto componente ya instanciado ? ¿Recuerdas la llamada para layout
volver app.js
?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
La layout
función / módulo descenderá al pasado data.components
(nuevamente, estos datos provienen del objeto pasado a través de text/x-magento-init
). Para cada objeto que encuentre, buscará un config
objeto, y en ese objeto de configuración buscará una component
clave. Si encuentra una clave de componente, lo hará
Utilícelo RequireJS
para devolver una instancia de módulo, como si se llamara al módulo en una dependencia requirejs
/ define
.
Llame a esa instancia de módulo como un constructor de JavaScript
Almacene el objeto resultante en el registry
objeto / módulo
Entonces, eso es mucho para asimilar. Aquí hay una revisión rápida, usando
<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>
como punto de partida El scope
valor es customer_listing.customer_listing
.
Si miramos el objeto JSON desde la text/x-magento-init
inicialización
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Vemos que el components.customer_listing.customer_listing
objeto tiene un config
objeto, y ese objeto de configuración tiene un component
objeto establecido en uiComponent
. La uiComponent
cadena es un módulo RequireJS. De hecho, es un alias RequireJS que corresponde al Magento_Ui/js/lib/core/collection
módulo.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
En layout.js
, Magento ha ejecutado un código que es equivalente al siguiente.
//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*/})
}
Para los realmente curiosos, si echas un vistazo al modelo de colección y sigues su ruta de ejecución, descubrirás que collection
es un objeto javascript que ha sido mejorado tanto por el lib/core/element/element
módulo como por el lib/core/class
módulo. Investigar estas personalizaciones está más allá del alcance de esta respuesta.
Una vez instanciado, layout.js
almacena esto object
en el registro. Esto significa que cuando Knockout comienza a procesar los enlaces y encuentra el scope
enlace personalizado
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento recuperará este objeto del registro y lo vinculará como el modelo de vista para las cosas dentro del div
. En otras palabras, el getTemplate
método que se llama cuando Knockout invoca el enlace sin etiquetas ( <!-- ko template: getTemplate() --><!-- /ko -->
) es el getTemplate
método en el new collection
objeto.
<uiComponents/>
desde el sistema XML de diseño. Los beneficios que obtienen es la capacidad de intercambiar modelos de vista en la misma página por un conjunto diferente de etiquetas.El enlace para cualquiera de las plantillas JS noqueadas se produce en los archivos .xml del módulo. Usando el módulo Checkout como ejemplo, puede encontrar la configuración de la
content
plantilla envendor/magento/module-checkout/view/frontend/layout/default.xml
En este archivo puede ver que la clase de bloque tiene nodos que definen el "jsLayout" y llaman al
<item name="minicart_content" xsi:type="array">
. Es un poco como un round robin de lógica, pero si estás dentrovendor/magento/module-checkout/view/frontend/templates/cart/minicart.phtml
verás esta línea:Por lo que los datos se unen dirige dónde buscar cualquier plantilla anidada, en este caso es el
Magento_Checkout/js/view/minicart
devendor/magento/module-checkout/view/frontend/web/js/view/minicart.js
la lógica (o MV en knockouts Modelo-Vista-Vista sistema de modelo) y usted tieneMagento_Checkout/minicart/content
(o V en knockouts Modelo-Vista-View Model sistema) para la plantilla de llamada. Entonces, la plantilla que se está extrayendo en este lugar esvendor/magento/module-checkout/view/frontend/web/template/minicart/content.html
.Realmente no es difícil de entender una vez que te acostumbras a buscar en los archivos .xml. La mayoría de esto lo aprendí aquí si puedes pasar el inglés roto. Pero hasta ahora siento que la integración de Knockout es la parte menos documentada de M2.
fuente
Estoy bastante seguro de que el método JS global
getTemplate
que está buscando se define enapp/code/Magento/Ui/view/base/web/js/lib/core/element/element.js
: puede encontrarlo aquí: https://github.com/magento/magento2/blob/4d71bb4780625dce23274c90e45788a72f345dd9/app/code/Magento/Ui/view/base /web/js/lib/core/element/element.js#L262Como estoy en mi teléfono, me resulta difícil descubrir exactamente cómo se realiza el enlace.
fuente