Những điều mà Magento 2 gọi là Mix mixins được triển khai như thế nào?


16

Các hệ thống đối tượng dựa trên RequireJS của Magento 2 chứa một tính năng gọi là "mixins". Một mixin Magento 2 không phải là thứ mà một kỹ sư phần mềm thường nghĩ là một hỗn hợp / đặc điểm . Thay vào đó, mixin Magento 2 cho phép bạn sửa đổi đối tượng / giá trị được trả về bởi mô-đun RequireJS trước khi đối tượng / giá trị đó được sử dụng bởi chương trình chính. Bạn định cấu hình một mixin Magento 2 như thế này (thông qua tệp requestjs-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
            }
        }
    }
};

Sau đó, bạn cần phải có hook.js(hoặc bất kỳ mô-đun RequireJS nào bạn đã định cấu hình),

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

trả về một hàm Magento sẽ gọi chức năng này, chuyển tham chiếu đến "mô-đun" mà bạn muốn sửa đổi. Trong ví dụ của chúng tôi, đây sẽ là đối tượng được trả về bởi mô-đun RequireJS Magento_Checkout/js/view/form/element/email. Đây cũng có thể là một hàm hoặc thậm chí là một giá trị tỷ lệ (tùy thuộc vào những gì mô-đun RequireJS trả về).

Hệ thống này dường như được gọi mixinsvì nó cho phép bạn tạo hành vi giống như mixin nếu đối tượng được trả về bởi mô-đun RequireJS ban đầu hỗ trợ extendphương thức.

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

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

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


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

Tuy nhiên, bản thân hệ thống chỉ là một cách để nối vào việc tạo đối tượng mô-đun.

Preamble xong - không ai biết cách Magento đã thực hiện chức năng này? Trang web RequireJS dường như không đề cập đến mixins (mặc dù Google nghĩ rằng bạn có thể muốn trang plugin của RequireJS ).

Ngoài các requirejs-config.jstệp, javascript lõi của Magento 2 chỉ đề cập đến mixinsba tệp

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

Các mixins.jstập tin dường như là một RequireJS cắm (dựa trên !...đề cập đến trong các ý kiến - là quyền này) nhưng nó không phải là 100% rõ ràng khi main.jshay scripts.jsđược gọi bởi Magento, hoặc làm thế nào các tùy chỉnh mixinscấu hình làm cho nó từ requirejs-config.jsvào người nghe hệ thống / móc miêu tả trên.

Có ai có một lời giải thích cho cách hệ thống này được / được thực hiện / kiến ​​trúc, với một mắt hướng tới khả năng gỡ lỗi tại sao một "mixin" có thể hoặc không thể được áp dụng?

Câu trả lời:


18

Tôi muốn đi thẳng vào câu hỏi của bạn và sau đó tôi sẽ cố gắng làm rõ những gì bạn thực sự có thể làm với plugin mixins . Vì vậy, những điều đầu tiên đầu tiên.

Thực hiện

Điều chính ở đây là khả năng của bất kỳ plugin RequireJS nào hoàn toàn đảm nhận quá trình tải một số tệp nhất định. Điều này cho phép sửa đổi giá trị xuất của một mô-đun trước khi nó được thông qua dưới dạng phụ thuộc được giải quyết.

Hãy xem triển khai sơ sài này về plugin mixins tùy chỉnh Magento thực sự là gì:

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

Phần cuối cùng và cũng là phần thử thách nhất là tự động chuẩn bị 'sampleMixinPlugin!' chuỗi con đến các mô-đun được yêu cầu. Để làm điều này, chúng tôi chặn definerequiregọi và sửa đổi danh sách các phụ thuộc trước khi chúng được xử lý bằng phương thức tải RequireJS ban đầu. Đó là một chút khó khăn và tôi khuyên bạn nên xem xét việc thực hiện lib/web/mage/requirejs/mixins.jsnếu bạn muốn nó hoạt động như thế nào.

Gỡ lỗi

Tôi muốn giới thiệu các bước này:

  • Đảm bảo rằng cấu hình cho 'mixins!' Plugin thực sự là .
  • Kiểm tra xem đường dẫn đến một mô-đun đang được sửa đổi . Tức là nó chuyển từ path/to/modulesang mixins!path/to/module.

Và cuối cùng nhưng không kém, requiresjs/mixins.jskhông có gì để làm với main.jshoặc script.jsmô-đun như họ chỉ có thể mở rộng cấu hình được thông qua từ data-mage-initthuộc tính:

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

Ý tôi là hai tệp trước không gây rối với giá trị được trả về bởi một mô-đun, thay vào đó chúng xử lý trước cấu hình của một thể hiện.

Ví dụ sử dụng

Để bắt đầu, tôi muốn thiết lập bản ghi thẳng gọi là "mixins" (bạn nói đúng về cách đặt tên sai) thực sự cho phép sửa đổi giá trị xuất của mô-đun theo bất kỳ cách nào bạn muốn. Tôi muốn nói rằng đây là một cách cơ chế chung hơn.

Dưới đây là một mẫu nhanh về việc thêm chức năng bổ sung cho chức năng đang được xuất bởi một mô-đun:

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

Bạn có thể triển khai một mixin thực tế cho bất kỳ đối tượng / hàm nào được trả về bởi một mô-đun và bạn không cần phải phụ thuộc vào extendphương thức nào cả.

Mở rộng hàm xây dựng:

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

Tôi hy vọng rằng điều này trả lời câu hỏi của bạn.

Trân trọng.


Cảm ơn bạn! Chỉ là những gì tôi đang tìm kiếm - câu hỏi duy nhất khác tôi có là - mixinscấu hình làm gì x-magento-initdata-mage-initcấu hình? tức là - trong ví dụ trên của bạn, path/to/configuration-modifiercũng sẽ trả về một cuộc gọi lại có thể sửa đổi dữ liệu cấu hình? Hay cái gì khác?
Alan Storm

Vâng, chính xác! Bạn phải trả lại một cuộc gọi lại từ đó bạn có thể sửa đổi dữ liệu cấu hình.
Denis Rul

bạn dường như biết cách của bạn xung quanh công cụ mặt trước khá tốt - bất kỳ cái nhìn sâu sắc về hai câu hỏi này? magento.stackexchange.com/questions/147899/... magento.stackexchange.com/questions/147880/...
Alan bão

4

Để làm tròn câu trả lời của Denis Rul .

Vì vậy, nếu bạn xem một trang Magento, đây là ba <script/>thẻ tải 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>

Đây là RequireJS chính nó ( require.js), mixins.jsplugin và cấu hình RequireJS ( requirejs-config.js) đã hợp nhất.

Các mixins.jstập tin định nghĩa một RequireJS cắm. Plugin này chịu trách nhiệm tải và gọi các mô-đun RequireJS lắng nghe việc khởi tạo mô-đun RequireJS khác.

Plugin này cũng chứa một chương trình requestjs sau khi nó xác định 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;
});

Thứ hai này tải chương trình chỉ định nghĩa mixinsplugin như một sự phụ thuộc, và sau đó định nghĩa lại toàn cầu require, definerequirejscác chức năng. Định nghĩa lại này là những gì cho phép hệ thống "không thực sự là một mixin" kết nối với khởi tạo ban đầu của mô-đun RequireJS trước khi chuyển mọi thứ trở lại các chức năng thông thường.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.