¿Cómo aplica Magento 2 los enlaces KnockoutJS?

19

Según una lectura muy superficial de la documentación de KnockoutJS, la inicialización de una vista de Knockout muy básica se parece a la siguiente

// This is a simple *viewmodel* - JavaScript that defines the data and behavior of your UI
function AppViewModel() {
    this.firstName = "Bert";
    this.lastName = "Bertington";
}

// Activates knockout.js
ko.applyBindings(new AppViewModel());

es decir, crea una función javascript destinada a ser utilizada como un constructor de objetos, crea una instancia de un objeto a partir de él y luego pasa ese objeto al ko.applyBindingsmétodo del objeto de eliminación global ( ko)

Sin embargo, en Magento 2, si carga una página de fondo con una IU de cuadrícula, Magento inicializará el js/core/app.jsmódulo RequireJS

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
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);
    };
});

Este módulo, a su vez, carga el Magento_Ui/js/lib/ko/initializemódulo, que parece inicializar el uso de KnockoutJS por parte de Magento. Sin embargo, si observa la fuente del módulo de inicialización.

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();
});

Verá que Magento ha llamado el ko.applyBindings();objeto sin un objeto de vista . Esto no tiene ningún sentido, y no estoy seguro si es mi comprensión limitada de Knockout o de que Magento haga algo personalizado / extraño aquí.

¿Es aquí donde Magento realmente aplica los enlaces de Knockout? ¿O eso sucede en otro lugar? ¿O está haciendo Magento algo complicado para interceptar el código Knockout y procesarlo en otro lugar?

Alan Storm
fuente

Respuestas:

38

La Magento_Ui/js/lib/ko/initializebiblioteca es, de hecho, donde Magento inicializa su instancia de Knockout. Magento no asigna un ViewModel cuando aplica enlaces.

La clave que falta aquí es el enlace KnockoutJS personalizado llamado scope.

Cuando la instancia de Knockout de Magento encuentra un scope:enlace como este

<li class="greet welcome" data-bind="scope: 'customer'">
    <span data-bind="text: customer().fullname ? $t('Welcome, %1!').replace('%1', customer().fullname) : 'Default welcome msg!'"></span>
</li>

Toma el valor de ese enlace (nombrado customer) y lo usa para cargar y aplicar un ViewModel para los nodos internos desde uiRegistry. Puede depurar los datos vinculados para un ámbito particular con alguna predepuración simple de KnockoutJS

<div data-bind="scope: 'someScope'">
    <pre data-bind="text: ko.toJSON($data, null, 2)"></pre>            
</div>

El uiRegistryes un diccionario simple como objeto, implementado en el Magento_Ui/js/lib/registry/registrymódulo RequireJS.

vendor/magento/module-ui/view/base/requirejs-config.js
17:            uiRegistry:     'Magento_Ui/js/lib/registry/registry',

Los objetos se colocan en el registro a través de los bits de JavaScript que se ven así

<script type="text/x-magento-init">
{
    "*": {
        "Magento_Ui/js/core/app": {
            "components": {
                "customer": {
                    "component": "Magento_Customer/js/view/customer",
                    "extra_data_1":"some_value",
                    "more_extra":"some_other_value",
                }
            }
        }
    }
}
</script>

El programa en el Magento_Ui/js/core/appmódulo examinará la componentsclave del objeto pasado y para cada subobjeto

  1. Obtener el objeto devuelto por el RequireJSmódulo especificado de la componentclave ( Magento_Customer/js/view/customer)

  2. Use ese objeto para crear una instancia de un nuevo objeto javascript (ver más abajo)

  3. Asigne cualquier clave de datos adicional a ese mismo objeto

  4. Agregue ese mismo objeto al uiRegistrycon la clave del objeto original ( customerarriba)

Si no está seguro de cómo funciona el x-magento-initguión, he escrito un artículo al respecto aquí .

Hay un examen más profundo del app.jsproceso en esta respuesta .

La implementación del enlace de alcance se define aquí

vendor/magento//module-ui/view/base/web/js/lib/ko/bind/scope.js
Alan Storm
fuente
Alan es una gran respuesta! Gracias por la información. Con respecto al punto 3 de la viñeta, ¿cómo agregará claves de datos adicionales al objeto recién instanciado? ¿Serán propiedades u otra cosa?
Timik