Как Magento 2 применяет KnockoutJS Bindings

19

При очень кратком чтении документации KnockoutJS инициализация очень простого представления Knockout выглядит следующим образом

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

то есть - вы создаете функцию javascript, предназначенную для использования в качестве конструктора объекта, создаете экземпляр объекта из него и затем передаете этот объект в ko.applyBindingsметод объекта глобального нокаута ( ko)

Однако в Magento 2, если вы загрузите серверную страницу с помощью Grid UI, Magento инициализирует js/core/app.jsмодуль 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);
    };
});

Этот модуль, в свою очередь, загружает Magento_Ui/js/lib/ko/initializeмодуль, который, по- видимому, инициализирует использование KnockoutJS в Magento. Тем не менее, если вы посмотрите на источник инициализации модуля.

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

Вы видите, что Magento называет ko.applyBindings();объект без объекта просмотра . Это не имеет никакого смысла, и я не уверен, что это мое ограниченное понимание нокаута или Magento, делающего что-то нестандартное / странное.

Это где Magento на самом деле применяет привязки нокаута? Или это происходит где-то еще? Или Magento делает что-то хитрое, чтобы перехватить код Knockout и обработать его в другом месте?

Алан Сторм
источник

Ответы:

38

Magento_Ui/js/lib/ko/initializeБиблиотека, на самом деле, где Magento инициализирует экземпляр Knockout. Magento не назначает ViewModel, когда применяет привязки.

Недостающим ключом здесь является именованная привязка KnockoutJSscope .

Когда экземпляр Magento Knockout встречает такую scope:привязку

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

Он принимает значение этой привязки (named customer) и использует его для загрузки и применения ViewModel для внутренних узлов из uiRegistry. Вы можете отлаживать данные, привязанные к определенной области, с помощью некоторой простой preотладки KnockoutJS

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

uiRegistryПростой словарь как объект, реализованный в Magento_Ui/js/lib/registry/registryмодуле RequireJS.

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

Объекты помещаются в реестр через биты JavaScript, которые выглядят так

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

Программа в Magento_Ui/js/core/appмодуле будет проверять componentsключ переданного объекта, и для каждого подобъекта будет

  1. Извлечь объект, возвращаемый указанным RequireJSмодулем из componentключа ( Magento_Customer/js/view/customer)

  2. Используйте этот объект для создания нового объекта javascript (см. Ниже)

  3. Назначьте любые дополнительные ключи данных этому же объекту

  4. Добавьте этот же объект к uiRegistryключу исходного объекта ( customerвыше)

Если вы не уверены, как x-magento-initработает скрипт, я написал статью об этом здесь .

В этом ответе есть более глубокое изучение app.jsпроцесса .

Реализация привязки области определяется здесь

vendor/magento//module-ui/view/base/web/js/lib/ko/bind/scope.js
Алан Сторм
источник
Алан, это отличный ответ! Спасибо за информацию. Что касается пункта 3, как он будет добавлять дополнительные ключи данных к вновь созданному объекту. Это будут свойства или что-то еще?
Тимик,