Bagaimana Are the Things Magento 2 menyebut "mixin" Diimplementasikan?

16

Sistem objek berbasis RequireJS Magento 2 berisi fitur yang disebut "mixins". Mixin Magento 2 bukan apa yang biasanya dianggap oleh insinyur perangkat lunak sebagai mixin / sifat . Sebagai gantinya, mixin Magento 2 memungkinkan Anda untuk memodifikasi objek / nilai yang dikembalikan oleh modul RequireJS sebelum objek / nilai tersebut digunakan oleh program utama. Anda mengonfigurasi mixin Magento 2 seperti ini (melalui file requireejs-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
            }
        }
    }
};

Kemudian, Anda harus memiliki hook.js(atau modul RequireJS apa pun yang telah Anda konfigurasi),

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

mengembalikan fungsi. Magento akan memanggil fungsi ini, meneruskan referensi ke "modul" yang ingin Anda modifikasi. Dalam contoh kita ini akan menjadi objek yang dikembalikan oleh modul RequireJS Magento_Checkout/js/view/form/element/email. Ini juga bisa berupa fungsi, atau bahkan nilai scaler (tergantung pada apa yang dikembalikan modul RequireJS).

Sistem ini tampaknya dipanggil mixinskarena memungkinkan Anda untuk membuat perilaku seperti mixin jika objek yang dikembalikan oleh modul RequireJS asli mendukung extendmetode ini.

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

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

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


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

Namun, sistem itu sendiri hanyalah cara untuk menghubungkan ke pembuatan objek modul.

Pembukaan selesai - apakah ada yang tahu bagaimana Magento telah menerapkan fungsi ini? Situs web RequireJS tampaknya tidak menyebutkan mixin (meskipun Google berpikir Anda mungkin ingin halaman plugin RequireJS ).

Di luar requirejs-config.jsfile, javascript inti Magento 2 hanya menyebutkan mixinsdalam tiga file

$ 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);

The mixins.jsfile muncul menjadi RequireJS Plugin (berdasarkan !...menyebutkan di komentar -? Apakah ini benar) tapi itu tidak 100% jelas kapan main.jsatau scripts.jsdipanggil oleh Magento, atau bagaimana kebiasaan mixinskonfigurasi membuatnya dari requirejs-config.jske pendengar / sistem hook dijelaskan di atas.

Adakah yang memiliki penjelasan tentang bagaimana sistem ini diimplementasikan / diarsipkan, dengan tujuan untuk dapat men-debug mengapa "mixin" dapat diterapkan atau tidak?

Alan Storm
sumber

Jawaban:

18

Saya ingin langsung membahas pertanyaan Anda dan kemudian saya akan mencoba menjelaskan apa yang sebenarnya dapat Anda lakukan dengan mixin plugin . Jadi, hal pertama yang pertama.

Penerapan

Hal utama di sini adalah kemampuan setiap plugin RequireJS untuk sepenuhnya mengambil alih proses pemuatan file tertentu. Ini memungkinkan untuk memodifikasi nilai ekspor modul sebelum akan diteruskan sebagai ketergantungan yang diselesaikan.

Lihatlah implementasi samar-samar dari apa sebenarnya plugin mixins kustom Magento ini:

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

Bagian terakhir dan paling menantang adalah untuk secara dinamis menambahkan 'sampleMixinPlugin!' substring ke modul yang diminta. Untuk melakukan ini kami mencegat definedan requirememohon dan memodifikasi daftar dependensi sebelum mereka diproses oleh metode pemuatan RequireJS yang asli. Ini sedikit rumit dan saya akan merekomendasikan untuk melihat implementasinya lib/web/mage/requirejs/mixins.jsjika Anda ingin cara kerjanya.

Debugging

Saya akan merekomendasikan langkah-langkah ini:

  • Pastikan konfigurasi untuk 'mixin!' plugin sebenarnya ada di sana .
  • Periksa apakah jalur ke modul sedang dimodifikasi . Yaitu berubah dari path/to/modulemenjadi mixins!path/to/module.

Dan yang terakhir tetapi tidak kalah pentingnya, requiresjs/mixins.jstidak ada hubungannya dengan modul main.jsatau script.jskarena mereka hanya dapat memperpanjang konfigurasi yang dilewatkan dari data-mage-initatribut:

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

Maksud saya, dua file sebelumnya tidak mengacaukan dengan nilai yang dikembalikan oleh modul, alih-alih mereka pra-proses konfigurasi instance.

Contoh Penggunaan

Untuk mulai dengan saya ingin meluruskan catatan yang disebut "mixin" (Anda benar tentang misnaming) sebenarnya memungkinkan untuk memodifikasi nilai yang diekspor dari modul dengan cara apa pun yang Anda inginkan. Saya akan mengatakan bahwa ini adalah mekanisme yang jauh lebih umum.

Berikut adalah contoh cepat menambahkan fungsionalitas ekstra ke fungsi yang diekspor oleh modul:

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

Anda dapat menerapkan mixin aktual untuk objek / fungsi apa pun yang dikembalikan oleh modul dan Anda tidak perlu bergantung pada extendmetode sama sekali.

Memperluas fungsi konstruktor:

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

Saya harap ini menjawab pertanyaan Anda.

Salam.

Denis Rul
sumber
Terima kasih! Apa yang saya cari - satu-satunya pertanyaan lain yang saya miliki adalah - apa yang dilakukan mixinskonfigurasi x-magento-initdan data-mage-initkonfigurasi? yaitu - pada Anda contoh di atas, apakah akan path/to/configuration-modifiermengembalikan panggilan balik yang dapat mengubah data konfigurasi? Atau sesuatu yang lain?
Alan Storm
Ya tepatnya! Seharusnya mengembalikan panggilan balik dari mana Anda dapat memodifikasi data konfigurasi.
Denis Rul
Anda tampaknya tahu jalan keluar tentang hal-hal ujung depan dengan cukup baik - ada wawasan tentang dua pertanyaan ini? magento.stackexchange.com/questions/147899/... magento.stackexchange.com/questions/147880/…
Alan Storm
4

Untuk melengkapi jawaban Denis Rul .

Jadi, jika Anda melihat halaman Magento, berikut adalah tiga <script/>tag yang memuat 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>

Ini adalah RequireJS sendiri ( require.js), mixins.jsplugin, dan konfigurasi RequireJS yang digabungkan ( requirejs-config.js).

Itu mixins.js berkas mendefinisikan RequireJS Plugin. Plugin ini bertanggung jawab untuk memuat dan memanggil modul RequireJS yang mendengarkan instantiasi modul RequireJS lainnya.

Plugin ini juga berisi program yang diperlukan setelah mendefinisikan plugin 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;
});

Program kedua ini memuat mixinsplugin yang baru saja didefinisikan sebagai dependensi, dan kemudian mendefinisikan kembali global require, definedan requirejsfungsinya. Redefinisi ini adalah apa yang memungkinkan sistem "not really a mixin" untuk menghubungkan ke instantiasi awal modul RequireJS sebelum meneruskan kembali ke fungsi biasa.

Alan Storm
sumber