마 젠토 2 : 'getTemplate'녹아웃 함수 바운드는 어떻게 / 어디에 있습니까?


19

많은 Magento 백엔드 페이지는 소스 코드에 다음을 포함합니다.

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

<!-- ko templateKnockoutJS 컨테이너리스 템플릿 바인딩 인 것을 이해합니다 (또는 생각합니까?) .

나에게 분명하지 않은 것은- getTemplate()함수 가 어떤 맥락 에서 호출됩니까? 온라인에서 본 예제에는 일반적으로. 다음에 자바 스크립트 객체가 template:있습니다. getTemplate객체를 반환하는 자바 스크립트 함수 라고 가정 하지만 전역 자바 스크립트 함수는 없습니다 getTemplate.

getTemplate묶인 곳은 어디 입니까? 아니면 Magento 백엔드 페이지에서 KnockoutJS 응용 프로그램 바인딩이 어디서 발생합니까?

순수한 HTML / CSS / Javascript 관점에서 이것에 관심이 있습니다. Magento 2에는 많은 구성 추상화가 있으므로 이론적으로 개발자는 구현 세부 정보에 대해 걱정할 필요가 없습니다. 구현 세부 사항에 관심이 있습니다.

답변:


38

UI 구성 요소의 PHP 코드는 다음과 같은 자바 스크립트 초기화를 렌더링합니다.

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

이 페이지의이 코드는 Magento가 Magento_Ui/js/core/appRequireJS 모듈을 호출하여 콜백을 가져온 다음 {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/initializeRequireJS 라이브러리 도로드합니다 . 이 initialize모듈은 Magento의 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는 또한 ./template/engineRequireJS 모듈 에서 Knockout의 자바 스크립트 템플릿 엔진 기능을 확장합니다 .

마지막으로 Magento applyBindings()는 KnockoutJS 객체를 호출 합니다. 일반적으로 Knockout 프로그램은 뷰 모델을 HTML 페이지에 바인딩하지만 Magento 는 뷰 모델 applyBindings 없이 호출합니다 . 즉, 녹아웃은 페이지를보기로 처리하기 시작하지만 데이터가 바인딩되지 않습니다.

스톡 녹아웃 설정에서는 약간 어리 석습니다. 그러나 앞에서 언급 한 사용자 정의 녹아웃 바인딩으로 인해 녹아웃이 작업을 수행 할 수있는 많은 기회가 있습니다.

범위 바인딩에 관심이 있습니다. 이 HTML에서 PHP UI 구성 요소 시스템으로 렌더링 된 것을 볼 수 있습니다.

<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'">속성입니다. Magento가 시작 applyBindings되면 Knockout은이 사용자 정의 scope바인딩 을보고 ./bind/scopeRequireJS 모듈을 호출합니다 . 사용자 정의 바인딩을 적용하는 기능은 순수 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 객체를 생성 한 다음 div사용 된 원본의 모든 하위 요소에 적용합니다 data-bind=scope:.

그렇다면 이미 인스턴스화 된 구성 요소 객체는 무엇입니까? 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. 해당 모듈 인스턴스 를 자바 스크립트 생성자로 호출

  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>

출발점으로. scope값입니다 customer_listing.customer_listing.

text/x-magento-init초기화 에서 JSON 객체를 보면

{
    "*": {
        "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객체를, 그 구성 객체는이 component에 설정되어 개체를 uiComponent. uiComponent문자열은 RequireJS 모듈입니다. 실제로 Magento_Ui/js/lib/core/collection모듈에 해당하는 RequireJS 별명입니다 .

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

에서가 layout.js, 마 젠토는 다음에 해당되는 코드를 실행하고있다.

//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.jsobject를 레지스트리에 저장하십시오 . 이는 녹아웃이 바인딩 처리를 시작하고 사용자 정의 scope바인딩이 발생하는 시점을 의미합니다.

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

Magento는이 객체를 레지스트리에서 다시 가져 와서 객체 내부의 뷰 모델로 바인딩합니다 div. 즉, getTemplateKnockout이 태그없는 바인딩 ( <!-- ko template: getTemplate() --><!-- /ko -->)을 호출 할 때 호출 되는 getTemplate메소드는 new collection객체 의 메소드입니다 .


1
나는 당신의 대답에 '왜'라는 질문을하는 것을 싫어하기 때문에 더 집중된 질문은 M2가 KO 템플릿을 호출하기 위해 (이처럼 복잡한) 시스템을 사용함으로써 무엇을 얻습니까?
동그라미 ixix

1
@circlesix <uiComponents/>레이아웃 XML 시스템에서 렌더링하기위한 더 큰 시스템의 일부입니다 . 그들이 얻는 이점은 동일한 페이지에서 다른 태그 세트를 위해 뷰 모델을 교환 할 수 있다는 것입니다.
Alan Storm

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>

따라서 데이터 바인딩 곳이있어이 경우, 중첩 된 템플릿을 찾아하는 지시 Magento_Checkout/js/view/minicartvendor/magento/module-checkout/view/frontend/web/js/view/minicart.js논리에 대해 (또는 MV 녹아웃 모델 - 뷰 - 뷰 모델 시스템) 및 당신이 Magento_Checkout/minicart/content(또는 녹아웃 모델 - 뷰 - 뷰 모델에 V 템플릿 호출). 이 지점에서 가져 오는 템플릿은 vendor/magento/module-checkout/view/frontend/web/template/minicart/content.html입니다.

실제로 .xml을 살펴 보는 데 익숙해지면 어렵지 않습니다. 이 중 대부분은 깨진 영어를 지나칠 수 있다면 여기서 배웠습니다 . 그러나 지금까지 나는 녹아웃 통합이 M2에서 가장 적게 문서화 된 부분이라고 생각합니다.


2
유용한 정보이므로 +1이지만, 질문에 따라 Magento는이를 처리하기위한 추상화가 있음을 알고 있습니다. 그러나 구현 세부 사항 자체에 대해 궁금합니다. 즉, 해당 XML 파일에서 무언가를 구성 할 때 magento는 구성된 값 이 세 번째 일을 수행 하도록하기 위해 다른 일을 합니다. 나는 다른 것과 세 번째에 관심이 있습니다.
Alan Storm

4

나는 확실히 꽤있어 글로벌 getTemplate 당신이 아래에 정의되어 찾고있는 JS 방법 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 /web/js/lib/core/element/element.js#L262

전화를 걸었을 때 바인딩이 정확히 어떻게 수행되는지 알아내는 데 어려움을 겪고 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.