Як реалізуються речі, які Magento 2 називає "міксинами"?


16

Об'єктні системи на базі Magento 2 містять функцію під назвою "міксин". Magento 2 mixin - це не те, що програмний інженер зазвичай вважає міксом / ознакою . Натомість Magento 2 mixin дозволяє змінювати об'єкт / значення, повернені модулем RequireJS до того, як цей об'єкт / значення буде використаний основною програмою. Ви налаштуєте Magento 2 mixin так (через файл Requjs-config.js)

var config = {
    'config':{
        'mixins': {
            //the module to modify
            'Magento_Checkout/js/view/form/element/email': {
                //your module that will do the modification
                'Pulsestorm_RequireJsRewrite/hook':true
            }
        }
    }
};

Тоді вам потрібно мати hook.js(або будь-який модуль RequireJS, який ви налаштували),

define([], function(){
    console.log("Hello");
    return function(theObjectReturnedByTheModuleWeAreHookingInto){
        console.log(theObjectReturnedByTheModuleWeAreHookingInto);
        console.log("Called");
        return theObjectReturnedByTheModuleWeAreHookingInto;
    };
});

повернути функцію. Magento викликає цю функцію, передаючи посилання на "модуль", який ви хочете змінити. У нашому прикладі це буде об’єкт, повернутий модулем RequireJS Magento_Checkout/js/view/form/element/email. Це також може бути функція або навіть масштабне значення (залежно від того, що повертає модуль RequireJS).

Здається, ця система називається mixinsтому, що вона дозволяє створювати поведінку типу mixin, якщо об'єкт, повернений оригінальним модулем RequireJS, підтримує extendметод.

define([], function(){
    'use strict';
    console.log("Hello");

    var mixin = {
        ourExtraMethod = function(){
            //...
        }
    };

    return function(theObjectReturnedByTheModuleWeAreHookingInto){
        console.log(theObjectReturnedByTheModuleWeAreHookingInto);
        console.log("Called");


        return theObjectReturnedByTheModuleWeAreHookingInto.extend(mixin);
    };
});

Однак сама система є лише способом підключення до створення об'єктів модулів.

Преамбула закінчена - хтось знає, як Magento реалізував цю функціональність? Веб-сайт RequireJS , схоже, не згадує міксин (хоча Google вважає, що ви можете захотіти сторінку плагіна ).

Поза межами requirejs-config.jsфайлів, основний javascript Magento 2 згадує лише про mixinsтри файли

$ find vendor/magento/ -name '*.js' | xargs ack mixins
vendor/magento/magento2-base/lib/web/mage/apply/main.js
73:                            if (obj.mixins) {
74:                                require(obj.mixins, function () {
79:                                    delete obj.mixins;

vendor/magento/magento2-base/lib/web/mage/apply/scripts.js
39:            if (_.has(obj, 'mixins')) {
41:                data[key].mixins = data[key].mixins || [];
42:                data[key].mixins = data[key].mixins.concat(obj.mixins);
43:                delete obj.mixins;

vendor/magento/magento2-base/lib/web/mage/requirejs/mixins.js
5:define('mixins', [
24:     * Adds 'mixins!' prefix to the specified string.
30:        return 'mixins!' + name;
76:     * Iterativly calls mixins passing to them
80:     * @param {...Function} mixins
84:        var mixins = Array.prototype.slice.call(arguments, 1);
86:        mixins.forEach(function (mixin) {
96:         * Loads specified module along with its' mixins.
102:                mixins   = this.getMixins(path),
103:                deps     = [name].concat(mixins);
111:         * Retrieves list of mixins associated with a specified module.
114:         * @returns {Array} An array of paths to mixins.
118:                mixins = config[path] || {};
120:            return Object.keys(mixins).filter(function (mixin) {
121:                return mixins[mixin] !== false;
126:         * Checks if specified module has associated with it mixins.
137:         * the 'mixins!' plugin prefix if it's necessary.
172:    'mixins'
173:], function (mixins) {
237:        deps = mixins.processNames(deps, context);
252:            queueItem[1] = mixins.processNames(lastDeps, context);

mixins.jsФайл , як видається RequireJS плагіна (на основі !...згадок в коментарях - це право) , але це не 100% ясно , коли main.jsабо scripts.jsвикликається Magento, або як призначені для користувача mixinsнастройки роблять його з requirejs-config.jsв систему слухача / гачку описано вище.

Хтось має пояснення того, як ця система була / впроваджена / архітектурована, маючи на увазі можливість виправити помилки, чому "міксин" може бути застосований чи не може застосовуватися?

Відповіді:


18

Я хотів би перейти до ваших питань, а потім спробую зрозуміти, що ви насправді можете робити з плагіном mixins . Отже, спочатку все.

Впровадження

Головне тут - здатність будь-якого плагіна RequireJS повністю переймати процес завантаження певних файлів. Це дозволяє змінити значення експорту модуля до того, як він буде переданий як вирішена залежність.

Погляньте на цю схематичну реалізацію того, що насправді плагін Magento custom mixins є:

// RequireJS config object.
// Like this one: app/code/Magento/Theme/view/base/requirejs-config.js
{
    //...

    // Every RequireJS plugin is a module and every module can
    // have it's configuration.
    config: {
        sampleMixinPlugin: {
            'path/to/the/sampleModule': ['path/to/extension']
        }
    }
}

define('sampleMixinPlugin', [
    'module'
] function (module) {
    'use strict';

    // Data that was defined in the previous step.
    var mixinsMap = module.config();

    return {
        /**
         * This method will be invoked to load a module in case it was requested
         * with a 'sampleMixinPlugin!' substring in it's path,
         * e.g 'sampleMixinPlugin!path/to/the/module'.
         */
        load: function (name, req, onLoad) {
            var mixinsForModule = [],
                moduleUrl = req.toUrl(name),
                toLoad;

            // Get a list of mixins that need to be applied to the module.
            if (name in mixinsMap) {
                mixinsForModule = mixinsMap[name];
            }

            toLoad = [moduleUrl].concat(mixinsForModule);

            // Load the original module along with mixins for it.
            req(toLoad, function (moduleExport, ...mixinFunctions) {
                // Apply mixins to the original value exported by the sampleModule.
                var modifiedExport = mixinFunctions.reduce(function (result, mixinFn) {
                        return mixinFn(result);
                }, moduleExport);

                // Tell RequireJS that this is what was actually loaded.
                onLoad(modifiedExport);
            });
        }
    }
});

Остання і найскладніша частина - це динамічно додавати "sampleMixinPlugin!" підрядка до запитуваних модулів. Для цього ми перехоплюємо defineта requireвиклики та змінюємо список залежностей до того, як вони будуть оброблені оригінальним методом навантаження RequireJS. Це трохи хитро, і я рекомендую поглянути на реалізацію, lib/web/mage/requirejs/mixins.jsякщо ви хочете, як вона працює.

Налагодження

Я рекомендую такі кроки:

  • Переконайтеся, що конфігурація для "mixins!" плагін насправді є .
  • Перевірте, чи змінюється шлях до модуля . Тобто це перетворюється з path/to/moduleна mixins!path/to/module.

І останнє, але не менш важливе, requiresjs/mixins.jsне має нічого спільного з модулями main.jsабо script.jsмодулями, оскільки вони можуть лише розширити конфігурацію, передану з data-mage-initатрибуту:

<div data-mage-init='{
    "path/to/module": {
        "foo": "bar",
        "mixins": ["path/to/configuration-modifier"]
    }
}'></div>

Я маю на увазі, що перші два файли не псуються зі значенням, поверненим модулем, натомість вони попередньо обробляють конфігурацію примірника.

Приклади використання

Для початку я хотів би встановити запис прямо, що так звані "mixins" (ви маєте рацію щодо неправильного найменування) насправді дозволяють змінювати експортоване значення модуля будь-яким способом, який вам потрібно. Я б сказав, що це загальний механізм.

Ось короткий зразок додавання додаткової функціональності функції, що експортується модулем:

// multiply.js
define(function () {
    'use strict';

    /**
     * Multiplies two numeric values.
     */
    function multiply(a, b) {
        return a * b;
    }

    return multiply;
});

// extension.js
define(function () {
    'use strict';

    return function (multiply) {
        // Function that allows to multiply an arbitrary number of values.
        return function () {
            var args = Array.from(arguments);

            return args.reduce(function (result, value) {
                return multiply(result, value);
            }, 1);
        };
    };
});

// dependant.js
define(['multiply'], function (multiply) {
    'use strict';

    console.log(multiply(2, 3, 4)); // 24
});

Ви можете реалізувати фактичний mixin для будь-якого об'єкта / функції, повернутого модулем, і зовсім не потрібно залежати від extendметоду.

Розширення функції конструктора:

// construnctor.js
define(function () {
    'use strict';

    function ClassA() {
        this.property = 'foo';
    }

    ClassA.prototype.method = function () {
        return this.property + 'bar';
    }

    return ClassA;
});

// mixin.js
define(function () {
    'use strict';

    return function (ClassA) {
        var originalMethod = ClassA.prototype.method;

        ClassA.prototype.method = function () {
            return originalMethod.apply(this, arguments) + 'baz';
        };

        return ClassA;
    }
});

Я сподіваюся, що це відповість на ваші запитання.

З повагою


Дякую! Тільки те, що я шукав - єдине інше питання, яке я мав би, - що робить mixinsконфігурація x-magento-initта data-mage-initконфігурації? тобто - у наведеному вище прикладі path/to/configuration-modifierтакож повертається зворотний виклик, який може змінювати дані конфігурації? Або щось інше?
Алан Шторм

Так, саме! Він повинен повернути зворотний виклик, з якого ви могли б змінити дані конфігурації.
Денис Руль

ви, здається, досить добре знаєте свій шлях навколо передньої частини - будь-яке розуміння цих двох питань? magento.stackexchange.com/questions/147899/… magento.stackexchange.com/questions/147880/…
Alan Storm

4

Щоб закруглити Денис Руль в відповідь .

Отже, якщо ви переглянете сторінку Magento, ось три <script/>теги, які завантажують Magento.

<script  type="text/javascript"  src="http://magento.example.com/pub/static/frontend/Magento/luma/en_US/requirejs/require.js"></script>
<script  type="text/javascript"  src="http://magento.example.com/pub/static/frontend/Magento/luma/en_US/mage/requirejs/mixins.js"></script>
<script  type="text/javascript"  src="http://magento.example.com/pub/static/_requirejs/frontend/Magento/luma/en_US/requirejs-config.js"></script>

Це сам RequireJS ( require.js), mixins.jsплагін і об'єднана конфігурація RequireJS ( requirejs-config.js).

mixins.jsФайл визначає RequireJS плагін. Цей плагін відповідає за завантаження та виклик модулів RequireJS, які слухають інстанції інших модулів RequireJS.

Цей плагін також містить програму Requjs після того, як він визначає плагін mixin.

require([
    'mixins'
], function (mixins) {
    'use strict';
    //...

    /**
     * Overrides global 'require' method adding to it dependencies modfication.
     */
    window.require = function (deps, callback, errback, optional) {
        //...
    };

    //...

    window.define = function (name, deps, callback) {
        //...
    };

    window.requirejs = window.require;
});

Ця друга програма завантажує тільки певний mixinsплагін , як залежність, а потім перевизначає глобальну require, defineі requirejsфункцію. Це переосмислення - це те, що дозволяє системі "насправді не змішатися" підключитися до початкової інстанції модуля RequireJS перед тим, як повернути речі до звичайних функцій.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.