Como o Magento 2 aplica as ligações KnockoutJS

19

Por uma leitura muito superficial da documentação do KnockoutJS, a inicialização de uma visualização Knockout muito básica é semelhante à seguinte

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

ie - você cria uma função javascript destinada a ser usada como construtor de objetos, instancia um objeto a partir dele e passa esse objeto para o ko.applyBindingsmétodo do objeto knockout global ( ko)

No entanto, no Magento 2, se você carregar uma página de back-end com uma interface do usuário em grade, o Magento inicializará o 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, por sua vez, carrega o Magento_Ui/js/lib/ko/initializemódulo, que parece inicializar o uso do KnockoutJS pelo Magento. No entanto, se você olhar para a fonte do módulo de inicialização.

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

Você vê o Magento chamado de ko.applyBindings();objeto sem um objeto de exibição . Isso não faz nenhum sentido, e não tenho certeza se é o meu entendimento limitado de Knockout ou Magento fazendo algo personalizado / estranho aqui.

É aqui que o Magento realmente aplica as ligações Knockout? Ou isso acontece em outro lugar? Ou o Magento está fazendo algo complicado para interceptar o código Knockout e processá-lo em outro lugar?

Alan Storm
fonte

Respostas:

38

A Magento_Ui/js/lib/ko/initializebiblioteca é, de fato, onde o Magento inicializa sua instância Knockout. O Magento não atribui um ViewModel quando aplica ligações.

A chave que falta aqui é a ligação KnockoutJS personalizada denominada scope.

Quando a instância Knockout do Magento encontra uma scope:ligação como esta

<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>

Ele pega o valor dessa ligação (nomeada customer) e a usa para carregar e aplicar um ViewModel para os nós internos do uiRegistry. Você pode depurar os dados associados a um escopo específico com alguma predepuração simples do KnockoutJS

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

O uiRegistryé um dicionário simples, como objeto, implementado no 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',

Os objetos são colocados no registro através dos bits de javascript que se parecem com isso

<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>

O programa no Magento_Ui/js/core/appmódulo examinará a componentschave do objeto passado e, para cada subobjeto,

  1. Busque o objeto retornado pelo RequireJSmódulo especificado na componentchave ( Magento_Customer/js/view/customer)

  2. Use esse objeto para instanciar um novo objeto javascript (veja abaixo)

  3. Atribua quaisquer chaves de dados extras ao mesmo objeto

  4. Adicione o mesmo objeto ao uiRegistrycom a chave do objeto original ( customeracima)

Se você não tem certeza de como o x-magento-initscript funciona, escrevi um artigo sobre isso aqui .

Há uma análise mais aprofundada do app.jsprocesso nesta resposta .

A implementação da ligação do escopo é definida aqui

vendor/magento//module-ui/view/base/web/js/lib/ko/bind/scope.js
Alan Storm
fonte
Alan, é uma ótima resposta! Obrigado pela informação. Em relação ao ponto 3, como adicionar chaves de dados extras ao objeto recém-instanciado. Serão propriedades ou algo mais?
Timik 8/03/18