Mã PHP cho thành phần UI hiển thị khởi tạo javascript trông như thế này
<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app":{
"types":{...},
"components":{...},
}
}
}
</script>
Đoạn mã này trong trang có nghĩa là Magento sẽ gọi Magento_Ui/js/core/appmô-đun RequireJS để tìm nạp lại một cuộc gọi lại, sau đó gọi cuộc gọi lại đó đi qua trong {types:..., components:...}đối tượng JSON làm đối số ( databên dưới)
#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);
};
});
Đối tượng dữ liệu chứa tất cả dữ liệu cần thiết để kết xuất thành phần UI, cũng như cấu hình liên kết các chuỗi nhất định với các mô-đun Magento RequireJS nhất định. Ánh xạ đó xảy ra trong các mô-đun typesvà layoutRequireJS. Ứng dụng này cũng tải Magento_Ui/js/lib/ko/initializethư viện RequireJS. Các initializeđá mô-đun tắt tích hợp KnockoutJS Magento.
/**
* 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();
});
Mỗi bind/...mô-đun RequireJS riêng lẻ thiết lập một ràng buộc tùy chỉnh duy nhất cho Knockout.
Các extender/...mô-đun RequireJS thêm một số phương thức trợ giúp vào các đối tượng KnockoutJS riêng.
Magento cũng mở rộng chức năng của công cụ mẫu javascript của Knockout trong ./template/enginemô-đun RequireJS.
Cuối cùng, Magento gọi applyBindings()đối tượng KnockoutJS. Đây thường là nơi chương trình Knockout sẽ liên kết mô hình xem với trang HTML - tuy nhiên, Magento gọiapplyBindings mà không có mô hình xem. Điều này có nghĩa là Knockout sẽ bắt đầu xử lý trang dưới dạng xem, nhưng không bị ràng buộc dữ liệu.
Trong một thiết lập Knockout chứng khoán, điều này sẽ là một chút ngớ ngẩn. Tuy nhiên, do các ràng buộc Knockout tùy chỉnh đã đề cập trước đó, có rất nhiều cơ hội để Knockout thực hiện.
Chúng tôi quan tâm đến phạm vi ràng buộc. Bạn có thể thấy rằng trong HTML này, cũng được hiển thị bởi hệ thống Thành phần UI 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>
Cụ thể là data-bind="scope: 'customer_listing.customer_listing'">thuộc tính. Khi Magento khởi động applyBindings, Knockout sẽ thấy scoperàng buộc tùy chỉnh này và gọi ./bind/scopemô-đun RequireJS. Khả năng áp dụng một ràng buộc tùy chỉnh là KnockoutJS thuần túy. Việc thực hiện ràng buộc phạm vi là điều Magento Inc. đã làm.
Việc thực hiện ràng buộc phạm vi là tại
#File: vendor/magento/module-ui/view/base/web/js/lib/ko/bind/scope.js
Bit quan trọng trong tập tin này là ở đây
var component = valueAccessor(),
apply = applyComponents.bind(this, el, bindingContext);
if (typeof component === 'string') {
registry.get(component, apply);
} else if (typeof component === 'function') {
component(apply);
}
Không đi sâu vào chi tiết, registry.getphương thức sẽ lấy ra một đối tượng đã được tạo bằng cách sử dụng chuỗi trong componentbiến làm định danh và truyền nó cho applyComponentsphương thức làm tham số thứ ba. Mã định danh chuỗi là giá trị của scope:( customer_listing.customer_listingở trên)
Trong 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);
}
về cơ bản, lệnh gọi createChildContextsẽ tạo ra một đối tượng viewModel mới dựa trên đối tượng thành phần đã được khởi tạo và sau đó áp dụng nó cho tất cả các phần tử hậu duệ của bản gốc divđược sử dụng data-bind=scope:.
Vì vậy, các đối tượng thành phần đã được khởi tạo là gì? Nhớ cuộc gọi để layoutquay lại app.js?
#File: vendor/magento/module-ui/view/base/web/js/core/app.js
layout(data.components);
Các layoutchức năng / mô-đun sẽ rơi vào thông qua năm data.components(một lần nữa, số liệu này được lấy từ các đối tượng thông qua năm qua text/x-magento-init). Đối với mỗi đối tượng mà nó tìm thấy, nó sẽ tìm kiếm một configđối tượng và trong đối tượng cấu hình đó, nó sẽ tìm kiếm một componentkhóa. Nếu nó tìm thấy một khóa thành phần, nó sẽ
Sử dụng RequireJSđể trả về một thể hiện của mô-đun - như thể mô-đun được gọi trong requirejs/ definephụ thuộc.
Gọi ví dụ mô-đun đó như là một hàm tạo javascript
Lưu trữ đối tượng kết quả trong registryđối tượng / mô-đun
Vì vậy, đó là rất nhiều để tham gia. Đây là một đánh giá nhanh, sử dụng
<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>
như một điểm khởi đầu. Các scopegiá trị customer_listing.customer_listing.
Nếu chúng ta nhìn vào đối tượng JSON từ khi text/x-magento-initkhởi tạo
{
"*": {
"Magento_Ui/js/core/app": {
/* snip */
"components": {
"customer_listing": {
"children": {
"customer_listing": {
"type": "customer_listing",
"name": "customer_listing",
"children": /* snip */
"config": {
"component": "uiComponent"
}
},
/* snip */
}
}
}
}
}
}
Chúng ta thấy components.customer_listing.customer_listingđối tượng có một configđối tượng và đối tượng config đó có một componentđối tượng được đặt thành uiComponent. Các uiComponentchuỗi là một module RequireJS. Trong thực tế, bí danh RequireJS tương ứng với Magento_Ui/js/lib/core/collectionmô-đun.
vendor/magento/module-ui/view/base/requirejs-config.js
14: uiComponent: 'Magento_Ui/js/lib/core/collection',
Trong layout.js, Magento đã chạy mã tương đương như sau.
//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*/})
}
Đối với những người thực sự tò mò, nếu bạn xem mô hình bộ sưu tập và theo con đường thực thi của nó, bạn sẽ phát hiện ra đó collectionlà một đối tượng javascript được tăng cường cả bởi lib/core/element/elementmô-đun và lib/core/classmô-đun. Nghiên cứu các tùy chỉnh này nằm ngoài phạm vi của câu trả lời này.
Sau khi khởi tạo, layout.jslưu trữ này objecttrong sổ đăng ký. Điều này có nghĩa là khi Knockout bắt đầu xử lý các ràng buộc và gặp scoperàng buộc tùy chỉnh
<div class="admin__data-grid-outer-wrap" data-bind="scope: 'customer_listing.customer_listing'">
<!-- snip -->
<!-- ko template: getTemplate() --><!-- /ko -->
<!-- snip -->
</div>
Magento sẽ lấy đối tượng này ra khỏi sổ đăng ký và liên kết nó làm mô hình xem cho những thứ bên trong div. Nói cách khác, getTemplatephương thức được gọi khi Knockout gọi ràng buộc tagless ( <!-- ko template: getTemplate() --><!-- /ko -->) là getTemplatephương thức trên new collectionđối tượng.