Magento 2: ฟังก์ชั่น Knockout ของฟังก์ชั่น Knockout 'getTemplate' อยู่ที่ใด?


19

หน้าแบ็กเอนด์ของวีโอไอพีหลายหน้ามีดังต่อไปนี้ในซอร์สโค้ด

<!-- ko template: getTemplate() --><!-- /ko -->

ผมเข้าใจ (หรือคิดว่าฉันทำอะไร?) ที่<!-- ko templateเป็น KnockoutJS ภาชนะน้อยแม่แบบที่มีผลผูกพัน

อะไรคือสิ่งที่ไม่ชัดเจนสำหรับฉัน - getTemplate()ฟังก์ชั่นที่ใช้เรียกบริบทคืออะไร template:ในตัวอย่างที่ผมเคยเห็นทั่วไปมักจะมีวัตถุจาวาสคริปต์หลังจากที่ ฉันสมมติว่าgetTemplateเป็นฟังก์ชั่นจาวาสคริปต์ที่ผลตอบแทนวัตถุ - แต่ไม่มีโลกgetTemplateฟังก์ชั่นจาวาสคริปต์ชื่อ

ที่ถูกgetTemplateผูกไว้? หรืออาจเป็นคำถามที่ดีกว่าการผูกแอปพลิเคชั่น KnockoutJS เกิดขึ้นที่หน้าแบ็กเอนด์ของวีโอไอพีที่ไหน

ฉันสนใจสิ่งนี้จากมุมมอง HTML / CSS / Javascript แท้ๆ ฉันรู้ว่าวีโอไอพี 2 มีรูปแบบการกำหนดค่ามากมายดังนั้นในทางทฤษฎีผู้พัฒนาไม่จำเป็นต้องกังวลเกี่ยวกับรายละเอียดการติดตั้ง ฉันสนใจในรายละเอียดการใช้งาน

คำตอบ:


38

รหัส PHP สำหรับองค์ประกอบ UI แสดงผลการเริ่มต้นจาวาสคริปต์ที่มีลักษณะเช่นนี้

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Ui/js/core/app":{
                "types":{...},
                "components":{...},
            }
        }
    }
</script>       

โค้ดบิตนี้ในหน้าหมายความว่าวีโอไอพีจะเรียกใช้Magento_Ui/js/core/appโมดูล RequireJS เพื่อดึงข้อมูลการโทรกลับแล้วเรียกว่าการติดต่อกลับนั้นผ่าน{types:..., components:...}วัตถุ JSON เป็นอาร์กิวเมนต์ (dataด้านล่าง)

#File: vendor/magento/module-ui/view/base/web/js/core/app.js
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);
    };
});

วัตถุข้อมูลมีข้อมูลทั้งหมดที่จำเป็นในการแสดงผลองค์ประกอบ UI เช่นเดียวกับการกำหนดค่าที่เชื่อมโยงสายบางอย่างกับโมดูล Magento RequireJS บางอย่าง การจับคู่นั้นเกิดขึ้นในโมดูลtypesและlayoutRequireJS แอปพลิเคชันยังโหลดMagento_Ui/js/lib/ko/initializeไลบรารี RequireJS initializeโมดูล kicks ปิดบูรณาการ KnockoutJS วีโอไอพี

/**
 * Copyright © 2016 Magento. All rights reserved.
 * See COPYING.txt for license details.
 */
/** Loads all available knockout bindings, sets custom template engine, initializes knockout on page */

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/initialize.js
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();
});

แต่ละbind/...โมดูล RequireJS แต่ละชุดตั้งค่าเดียวเชื่อมโยงที่กำหนดเองสำหรับการทำให้ลง

extender/...โมดูล RequireJS เพิ่มวิธีการช่วยเหลือบางอย่างเพื่อวัตถุ KnockoutJS พื้นเมือง

Magento ยังขยายฟังก์ชันการทำงานของเอ็นจิ้น javascript แม่แบบของ Knockout ใน ./template/engineเอ็นจิ้นโมดูล RequireJS

ในที่สุดวีโอไอพีก็เรียกapplyBindings()หาวัตถุ KnockoutJS นี่เป็นเรื่องปกติที่โปรแกรม Knockout จะผูกโมเดลมุมมองเข้ากับหน้า HTML อย่างไรก็ตาม Magento จะเรียกapplyBindings โดยไม่มีโมเดลมุมมอง ซึ่งหมายความว่าสิ่งที่น่าพิศวงจะเริ่มประมวลผลหน้าเว็บเป็นมุมมอง แต่ไม่มีการผูกข้อมูล

ในการตั้งค่าสิ่งที่น่าพิศวงหุ้นนี้จะโง่เล็กน้อย อย่างไรก็ตามเนื่องจากการผูกน็อกสิ่งที่น่าพิศวงตามที่กล่าวไว้ก่อนหน้านี้

เราสนใจขอบเขตการเชื่อมโยง คุณจะเห็นว่าใน HTML นี้แสดงผลโดยระบบ PHP UI Component

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
    <script type="text/x-magento-init">
    </script>
</div>

โดยเฉพาะdata-bind="scope: 'customer_listing.customer_listing'">แอตทริบิวต์ เมื่อวีโอไอพีเริ่มต้นapplyBindingsใหม่สิ่งที่น่าพิศวงจะเห็นการscopeผูกแบบกำหนดเองนี้และเรียกใช้./bind/scopeโมดูล RequireJS ความสามารถในการใช้การผูกแบบกำหนดเองนั้นบริสุทธิ์ KnockoutJS การบังคับใช้ขอบเขตนั้นเป็นสิ่งที่ Magento Inc. ได้ทำไว้

การดำเนินการของขอบเขตการรวมอยู่ที่

#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js

บิตสำคัญในไฟล์นี้อยู่ที่นี่

var component = valueAccessor(),
    apply = applyComponents.bind(this, el, bindingContext);

if (typeof component === 'string') {
    registry.get(component, apply);
} else if (typeof component === 'function') {
    component(apply);
}

โดยไม่ได้รับรายละเอียดมากเกินไปregistry.getวิธีการจะดึงวัตถุที่สร้างขึ้นแล้วโดยใช้สตริงในcomponentตัวแปรเป็นตัวระบุและส่งผ่านไปยังapplyComponentsวิธีการเป็นพารามิเตอร์ที่สาม ตัวระบุสตริงคือค่าของscope:( customer_listing.customer_listingด้านบน)

ใน applyComponents

function applyComponents(el, bindingContext, component) {
    component = bindingContext.createChildContext(component);

    ko.utils.extend(component, {
        $t: i18n
    });

    ko.utils.arrayForEach(el.childNodes, ko.cleanNode);

    ko.applyBindingsToDescendants(component, el);
}

การเรียกร้องให้createChildContextจะสร้างสิ่งที่เป็นหลักวัตถุ Viewmodel ใหม่บนพื้นฐานของวัตถุส่วนประกอบ instantiated แล้วและจากนั้นนำไปใช้กับทุกองค์ประกอบที่ลูกหลานของเดิมที่ใช้divdata-bind=scope:

ดังนั้นวัตถุองค์ประกอบinstantiated แล้วคืออะไร? จำสายที่จะlayoutกลับมาapp.js?

#File: vendor/magento/module-ui/view/base/web/js/core/app.js

layout(data.components);

layoutฟังก์ชั่น / โมดูลจะลงไปยังที่ผ่านdata.components(อีกครั้งข้อมูลนี้มาจากวัตถุที่ส่งผ่านทางtext/x-magento-init) สำหรับแต่ละวัตถุที่พบมันจะค้นหาconfigวัตถุและในวัตถุการกำหนดค่านั้นจะค้นหาcomponentคีย์ หากพบคีย์ส่วนประกอบมันจะ

  1. ใช้RequireJSเพื่อส่งคืนอินสแตนซ์โมดูล - ราวกับว่าโมดูลนั้นถูกเรียกใช้ในrequirejs/ การdefineพึ่งพา

  2. เรียกอินสแตนซ์ของโมดูลนั้นเป็นตัวสร้าง javascript

  3. เก็บวัตถุที่เป็นผลลัพธ์ในregistryวัตถุ / โมดูล

ดังนั้นนี่เป็นเรื่องที่ควรพิจารณาก่อนการใช้งาน

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <div data-role="spinner" data-component="customer_listing.customer_listing.customer_columns" class="admin__data-grid-loading-mask">
        <div class="spinner">
            <span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span>
        </div>
    </div>
    <!-- ko template: getTemplate() --><!-- /ko -->
    <script type="text/x-magento-init">
    </script>
</div>

เป็นจุดเริ่มต้น ค่า scopecustomer_listing.customer_listing

ถ้าเราดูที่วัตถุ JSON จากการtext/x-magento-initเริ่มต้น

{
    "*": {
        "Magento_Ui/js/core/app": {
            /* snip */
            "components": {
                "customer_listing": {
                    "children": {
                        "customer_listing": {
                            "type": "customer_listing",
                            "name": "customer_listing",
                            "children": /* snip */
                            "config": {
                                "component": "uiComponent"
                            }
                        },
                        /* snip */
                    }
                }
            }
        }
    }
}

เราเห็นcomponents.customer_listing.customer_listingวัตถุมีconfigวัตถุและวัตถุ config ที่มีวัตถุที่ตั้งค่าให้component สตริงเป็น RequireJS โมดูล ในความเป็นจริงมันเป็นนามแฝง RequireJS ที่สอดคล้องกับโมดูลuiComponentuiComponentMagento_Ui/js/lib/core/collection

vendor/magento/module-ui/view/base/requirejs-config.js
14:            uiComponent:    'Magento_Ui/js/lib/core/collection',

ในlayout.jsMagento มีการเรียกใช้รหัสที่เทียบเท่าดังต่อไปนี้

//The actual code is a bit more complicated because it
//involves jQuery's promises. This is already a complicated 
//enough explanation without heading down that path

require(['Magento_Ui/js/lib/core/collection'], function (collection) {    
    object = new collection({/*data from x-magento-init*/})
}

สำหรับสิ่งที่อยากรู้อยากเห็นอย่างแท้จริงหากคุณดูในรูปแบบของคอลเลกชันและปฏิบัติตามเส้นทางการดำเนินการคุณจะพบว่าcollectionเป็นวัตถุจาวาสคริปต์ที่ได้รับการปรับปรุงทั้งโดยlib/core/element/elementโมดูลและlib/core/classโมดูล การค้นคว้าการปรับแต่งเหล่านี้อยู่นอกเหนือขอบเขตของคำตอบนี้

ทันทีที่อินสแตนซ์layout.jsเก็บobjectไว้ในรีจิสทรี ซึ่งหมายความว่าเมื่อการทำให้ล้มลงเริ่มประมวลผลการรวมและพบการscopeผูกแบบกำหนดเอง

<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
    <!-- snip -->
    <!-- ko template: getTemplate() --><!-- /ko -->
    <!-- snip -->
</div>

divวีโอไอพีจะเรียกกลับวัตถุนี้ออกจากรีจิสทรีและผูกเป็นรูปแบบมุมมองสำหรับสิ่งที่อยู่ภายใน กล่าวอีกนัยหนึ่งgetTemplateวิธีการที่เรียกว่าเมื่อการทำให้ล้มลงเรียกใช้การรวมแท็ก ( <!-- ko template: getTemplate() --><!-- /ko -->) เป็นgetTemplateวิธีบนnew collectionวัตถุ


1
ฉันเกลียดที่จะถามคำถามว่า 'ทำไม' สำหรับคำตอบของคุณดังนั้นคำถามที่มุ่งเน้นมากขึ้นคืออะไร M2 ได้รับประโยชน์อะไรจากการใช้ระบบนี้ (ดูซับซ้อน) เพื่อเรียกแม่แบบ KO?
Circlesix

1
@circlesix เป็นส่วนหนึ่งของระบบที่ใหญ่ขึ้นสำหรับการแสดงผล<uiComponents/>จากระบบ XML ของโครงร่าง ประโยชน์ที่ได้รับคือความสามารถในการสลับมุมมองโมเดลในหน้าเดียวกันสำหรับชุดแท็กที่แตกต่างกัน
อลันสตอร์ม

16
ฉันไม่รู้ว่าจะหัวเราะหรือร้องไห้! ช่างเป็นระเบียบ
koosa

8
ฉันคิดว่าพวกเขากำลังขุดหลุมฝังศพของตัวเอง หากพวกเขายังคงมีปัญหาเช่นนี้ บริษัท จะหยุดใช้เพราะค่าใช้จ่ายในการพัฒนา
Marián Zeke Šedaj

2
ฉันใช้เวลาประมาณ 5 ชั่วโมงในการพยายามหาวิธีผูกพฤติกรรมที่กำหนดเองกับรูปแบบที่แสดงโดย "เวทมนต์" ทั้งหมดนี้ ส่วนหนึ่งของปัญหาคือกรอบทั่วไปที่สูงนี้ต้องการให้คุณผ่านชั้นมากมายจนกว่าคุณจะมีโอกาสเข้าใจวิธีการทำสิ่งต่าง ๆ การติดตามว่าการกำหนดค่าบางอย่างมาจากไหนจะกลายเป็นเรื่องน่าเบื่ออย่างไม่น่าเชื่อ
greenone83

12

การโยงเทมเพลต JS ที่น่าพิศวงเกิดขึ้นในไฟล์. xml ของโมดูล การใช้โมดูล Checkout เป็นตัวอย่างคุณสามารถค้นหาการกำหนดค่าสำหรับcontentเทมเพลตได้vendor/magento/module-checkout/view/frontend/layout/default.xml

<block class="Magento\Checkout\Block\Cart\Sidebar" name="minicart" as="minicart" after="logo" template="cart/minicart.phtml">
    <arguments>
        <argument name="jsLayout" xsi:type="array">
            <item name="types" xsi:type="array"/>
                <item name="components" xsi:type="array">
                    <item name="minicart_content" xsi:type="array">
                        <item name="component" xsi:type="string">Magento_Checkout/js/view/minicart</item>
                            <item name="config" xsi:type="array">
                                <item name="template" xsi:type="string">Magento_Checkout/minicart/content</item>
                            </item>

ในไฟล์นี้คุณจะเห็นว่าคลาสบล็อกมีโหนดที่กำหนด "jsLayout" และเรียกใช้ <item name="minicart_content" xsi:type="array">และโทรออก มันเป็นตรรกะรอบ ๆ ตัวเล็กน้อย แต่ถ้าคุณอยู่ในนั้นvendor/magento/module-checkout/view/frontend/templates/cart/minicart.phtmlคุณจะเห็นบรรทัดนี้:

<div id="minicart-content-wrapper" data-bind="scope: 'minicart_content'">
    <!-- ko template: getTemplate() --><!-- /ko -->
</div>

ดังนั้น data-bind จะกำหนดตำแหน่งที่จะค้นหาเทมเพลตที่ซ้อนกันใด ๆ ในกรณีนี้มันคือ Magento_Checkout/js/view/minicartของvendor/magento/module-checkout/view/frontend/web/js/view/minicart.jsตรรกะ (หรือ MV ใน knockouts ระบบ Model-View-View Model) และคุณมีMagento_Checkout/minicart/content(หรือ V ใน knockouts Model-View-View Model ระบบ) สำหรับการเรียกแม่แบบ vendor/magento/module-checkout/view/frontend/web/template/minicart/content.htmlดังนั้นแม่แบบที่จะถูกดึงในจุดนี้คือ

จริงๆแล้วมันไม่ยากที่จะคิดออกเมื่อคุณคุ้นเคยกับการค้นหาใน. xml ของ ส่วนใหญ่ฉันเรียนรู้ที่นี่ถ้าคุณสามารถผ่านพ้นภาษาอังกฤษที่แตกสลายได้ แต่จนถึงตอนนี้ฉันรู้สึกว่าการรวมที่น่าพิศวงเป็นส่วนน้อยที่สุดของ M2


2
ข้อมูลที่เป็นประโยชน์ดังนั้น +1 แต่ตามคำถามฉันรู้ว่าวีโอไอพีมี abstractions ที่จะจัดการกับเรื่องนี้ - แต่ฉันอยากรู้เกี่ยวกับรายละเอียดการใช้งานตัวเอง เช่น - เมื่อคุณกำหนดค่าบางอย่างในไฟล์ XML ที่วีโอไอพีไม่อย่างอื่นเพื่อให้แน่ใจว่าค่าการกำหนดค่าของคุณไม่ได้สิ่งที่สาม ฉันสนใจในสิ่งอื่นและสิ่งที่สาม
Alan Storm

4

ฉันค่อนข้างมั่นใจว่าวิธี JS ทั่วโลกที่ getTemplateคุณกำลังมองหามีการกำหนดไว้ภายใต้app/code/Magento/Ui/view/base/web/js/lib/core/element/element.jsคุณสามารถค้นหาได้ที่นี่: https://github.com/magento/magento2/blob/4d71bb4780625dce23274c90e45788a72f345dd9/app/code/Magento/Ui/view/base/Magento /web/js/lib/core/element/element.js#L262

ขณะที่ฉันกำลังใช้โทรศัพท์ฉันมีเวลายากที่จะทราบว่าการเชื่อมโยงจะเสร็จสิ้นอย่างไร

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.