Xin chào các bạn
Như vậy là sau 3 bài đầu tiên của series lập trình ứng dụng RESTful, các bạn đã nắm được khá nhiều về CakePHP, Backbone hay Marionette, bài 4 này được dịch từ RequireJSを使用したJavaScriptのモジュール化 - AWS上で構築するRESTfulアプリ勉強会~Web開発ワークショップ~【第4回】マニュアル của tác giả @k_shimoji , hi vọng sẽ tiếp tục nhận được sự ủng hộ của các bạn, dù tiếng Nhật mình không được tốt cho lắm.
Tài liệu tham khảo
Commit hoàn chỉnh của bài viết này(GitHub) và slide (SlideShare), riêng slide bằng Tiếng Nhật nên bạn nào không giỏi ngôn ngữ này có thể bỏ qua.
Bây giờ chúng ta làm quen với thư viện mới: Require.js.
Hãy làm theo những gì mình thêm vào các module của Javaript, qua việc thực hành các bạn sẽ hiểu cách hoạt động của nó.
JavaScript module là gì ?
Sẽ có rất nhiều cách để định nghĩa nó theo sách vở, nhưng để dễ hình dung, bạn có thể hiểu như sau:
- Đây là các "khối" với những chức năng độc lập với các thành phần khác của ứng dụng.
- Mọi thứ cần phải được đọc bất cứ khi nào chúng ta cần
Tính đến thời điểm này , chương trình mà chúng ta đã viết ở các bài trước như sau
var app = app || {};
//bắt đầu
(function(app) {
app.Application = Backbone.Marionette.Application.extend({
initialize : function(){
new app.TodoRouter();
},
onStart : function(){
Backbone.history.start();
},
regions : {
mainRegion : '#main'
}
});
})(app);
Ở global object app
sẽ lưu trữ object được thừa kế từ Backbone.Marionette.Application
như là Application
object.
Chúng ta đã tạo ra một file tên là main.js, và mình sẽ xem thành phần hiện tại của của global object app
bằng log của chương trình mà mình đã tạo ở bài trước.
var app = app || {};
//bắt đầu
(function(app) {
app.application = new app.Application();
app.application.start();
console.log(app); // ****** kết quả log của app ở đây******
})(app);
Chúng ta đang ở main.js
, và sau khi tất cả các file js đã được load, kết quả nhận được của log sẽ như sau. Các định nghĩa của object trong mỗi file js đã đều được khai báo tương ứng.
Như các bạn thấy, Model, và cả View đã trở thành riêng biệt giống như những objet.
Theo cách định nghĩa cũ thì mình đã coi các file js là các thành phần có chức năng độc lập với các file js khác, tuy nhiên nó chưa đủ cụ thể vì có 1 số file js, thư viện... cần phụ thuộc vào một file js khác ( trước khi load nó thì phải load file kia đã ), như tôi đã nói ở trên "Mọi thứ cần phải được đọc bất cứ khi nào chúng ta cần". Mình sẽ sử dụng "Require.js" để quản lý việc điều này.
Hãy kiểm tra và chỉnh sửa với log của branch vol/03
đã tạo ở bài trước.
Cụ thể phải làm gì thì chúng ta hãy xem theo thứ tự dưới đây
Điều khiển các mối quan hệ giữa các module
Giữa các module luôn có một "sự phụ thuộc" nhất định.
Ví dụ: view phụ thuộc vào model và collection để hoạt động.
Làm cách nào để chúng ta quản lý được sự phụ thuộc đó?
Dưới đây là nội dung file default.ctp
hiện tại.
<!-- js(library) -->
<script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
<script src="js/lib/underscore-min.js" type="text/javascript"></script>
<script src="js/lib/backbone-min.js" type="text/javascript"></script>
<script src="js/lib/backbone.marionette.min.js" type="text/javascript"></script>
<!-- js(application) -->
<!-- model -->
<script src="js/models/todo-model.js" type="text/javascript"></script>
<!-- collection -->
<script src="js/collections/todo-collection.js" type="text/javascript"></script>
<!-- view -->
<script src="js/views/todo-item-view.js" type="text/javascript"></script>
<script src="js/views/todo-detail-item-view.js" type="text/javascript"></script>
<script src="js/views/todo-detail-layout-view.js" type="text/javascript"></script>
<script src="js/views/todo-composite-view.js" type="text/javascript"></script>
<script src="js/views/todo-layout-view.js" type="text/javascript"></script>
<!-- controller -->
<script src="js/routers/controller.js" type="text/javascript"></script>
<!-- router -->
<script src="js/routers/router.js" type="text/javascript"></script>
<!-- application -->
<script src="js/app.js" type="text/javascript"></script>
<!-- entry point -->
<script src="js/main.js" type="text/javascript"></script>
Chúng ta cần phải giải quyết sự phụ thuộc của các file js, nhất là về mặt "thứ tự đọc", file bị phụ thuộc cần phải được đọc sau file mà nó phụ thuộc.
Ví dụ: "route.js" như dưới đây rất cần object TodoController
được định nghĩa trong file controller.js
như trên, vì vậy mình cần load file controller.js
trước khi load "route.js" .
var app = app || {};
//router
(function(app) {
app.TodoRouter = Backbone.Marionette.AppRouter.extend({
// Khai báo controller
controller: new app.TodoController(), //***** Nó nằm trong controller.js
//Cấu hình routing
appRoutes : {
'' : 'todoLists',
'todo-lists' : 'todoLists',
'todo-lists/:id' : 'todoDetail'
},
});
})(app);
jquery, underscore, backbone, bắt buộc phải liệt kê theo thứ tự như trên.
Việc các file được khai báo độc lập sẽ rất khó để quản lý khi số lượng file js ngày một tăng, và như vậy khiến ta khó có thể hiểu module nào thực sự phụ thuộc vào module nào.
Không sao vì chúng ta đã có một thư viện rất hữu ích là require.js, giúp chúng ta định nghĩa được các file phụ thuộc một cách dễ dàng và mình sẽ không cần quan tâm thứ tự load nữa.
Require.js là gì ?
Chia chương trình JavaScript thành các module theo cấu trúc của AMD
, dựa vào việc định nghĩa của các file độc lập ngay khi chạy, thư viện javascipt sẽ hỗ trợ chúng ta với cơ chế "quản lý thời gian để load lần lượt các file cần thiết"
Nếu bạn xác định được "bây giờ cần hoge module", "hoge module" lại yêu cầu "fuga module", "fuga module" lại yêu cầu "piyo module",... và định nghĩa trước sự phụ thuộc của chúng sẽ giúp chúng ta có thể tự động load chúng dựa trên những mối quan hệ này.
AMD là gì?
AMD là viết tắt của Asynchronous Module Definition, là một qui chuẩn của javascript dành cho việc load các script/module và các dependency của chúng từ ngoài vào một cách không đồng bộ (asynchronously).
require.js là một sự thực thi của AMD này.
Định nghĩa Module bởi AMD các bạn hãy xem ví dụ sau, những thông tin khác tôi sẽ bổ sung sau.
//TodoList collection
define(function(require) {
var TodoModel = require('models/todo-model');
var TodoCollection = Backbone.Collection.extend({
url : '/rest-study/todo_lists.json',
model : TodoModel,
parse : function(response) {
//parse collection
console.log("parse collection");
return response;
}
});
return TodoCollection;
});
Nhiệm vụ của Require.js
- Đọc đoạn js của module được trình bày theo AMD format để tạo object
- Nếu bạn đã xác định được phần cần đọc của file được require và nó không phụ thuộc thì các bạn cũng có thể khai báo nó độc lập.
OK mình bắt tay vào làm cho dễ hiểu hơn nhé.
Nội dung
Chương trình mà mình đã tạo ở bài trước chuẩn format của AMN, vì vậy bạn có thể đọc được trên Require.js.
Giao diện vẫn thế, chỉ cách thực hiện thay đổi
Đầu tiên là quá trình chuẩn bị !
Chuẩn bị
Đăng nhập bằng SSH
Hãy bắt đầu với việc đăng nhập bằng SSH
-
ssh -i [秘密鍵のパス] study@[サーバのPublicIP]
chuyển vào thư mục làm việc
-
cd /var/www/study/rest-study
Sao lưu branch của git
Ở chương trước, chúng ta làm việc ở branch vol/03
. Sau khi hoàn thành bài học , branch vol/03
được push lên Github, vì vậy việc đầu tiên bạn cần làm là gộp branch này với branch master
để nhận kết quả cuối cùng. Để giữ phiên bản cũ hoạt động, bạn nên sửa đường dẫn của nó bằng cách tạo một folder mới và lưu nó vào đó.
Nội dung của phần 1 là bạn phải hoàn thành hết các điều kiện trên, hãy ghi nhớ.
- Đầu tiên bạn cần phải vào branch
master
.
git checkout master
Gộp branch master
với branch vol/03
để nhận kết quả của bài viết trước.
git merge vol/03
Đã gộp xong !
Tạo branch cho phiên làm việc này.
Vẫn là những thao tác như cũ.
Chúng ta tạo branch vol/04
và checkout nó.
Một khi đã tạo branch tên vol/04
từ branch master
, đầu tiên hãy chắc chắn bạn đang ở branch master
.
-
git branch
để chắc chắn bạn đang ở branchmaster
-
Nếu bạn đang không ở
master
, nhập lệnhgit checkout
để checkout vàomaster
Tạo branch vol/03
bằng các lệnh dưới đây
-
git branch vol/04
Tạo branch -
git checkout vol/04
chuyển đến branchvol/04
vừa tạo
Xác nhận
Chúng ta chỉ chắc chắn trong trường hợp
-
hãy kiểm tra log các commit bằng
git log
Kết quả sẽ ok nếu hiển thị commit ngay trước đó.
Sự chuẩn bị về branch đã xong.
git branch -a
và bạn xem tất cả các branch sẽ thấy hiện vol/04-finish
chứa tất cả các source của các giai đoạn nội dung đã hoàn thành, bạn hãy tham khảo để xem sự commit của mỗi bài học qua URL sau
Commits · suzukishouten-study/rest-study
Tải thư viện Require.js
Tải về từ Trang chủ Require.js.
Link download là http://requirejs.org/docs/release/2.1.17/minified/require.js
. Các bạn có thể đăng nhập vào SSH và chạy lệnh wget với URL này.
Cách tải thư viện bằng lệnh wget các bạn có thể xem lại bài này vì chúng tôi đã đề cập rồi (http://qiita.com/Takumi_Kasuga/items/adea7d28c2c8b541b29a#t%E1%BA%A3i-v%E1%BB%81-c%C3%A1c-th%C6%B0-vi%E1%BB%87n).
-
Các bạn lưu thư viện này tại
/var/www/study/rest-study/app/webroot/js/lib
cd /var/www/study/rest-study/app/webroot/js/lib
wget http://requirejs.org/docs/release/2.1.17/minified/require.js
Mọi thứ đã sẵn sàng, chúng ta bắt đầu luôn bài 1
Trước tiên...
Tại app/Controller/AppController.php
đã có sẵn một thành phần gọi là DebugKit, nhưng chúng ta không dùng đến nó.
Đó chỉ là một phiên bản khác của DebugKit mà Jquery đã có.
class AppController extends Controller {
public $components = array(
- 'DebugKit.Toolbar',
'RequestHandler'
);
}
-
Mở
app/Controller/AppController.php
để chỉnh sửa. - Kiểm tra kết quả.
- Commit lên Git (Xóa dòng có chứa "DebugKit" đi)。
Tham khảo kết quả chỉnh sửa tại (GitHub)
Chúng ta sẽ cùng làm theo hướng dẫn ở bài 1 cho đến application.start()
Đây là file bạn sẽ phải viết, khi làm theo những tiến trình này với require.js, bạn sẽ hiểu chính xác nó chạy như thế nào.
- Định nghĩa cho các sự phụ thuộc
- Tạo file
require-config.js
, tại đây bạn có thể định nghĩa sự phụ thuộc của các file.
- Tạo file
- Xác định Entry point
- Để xác định File Js sẽ trở thành entry point tôi sẽ dùng require.js.
- Tôi sẽ làm theo trình tự cho đến khi Controller được chạy
- Những người mới sẽ hiểu trình tự bài học cho đến khi Controller thực thi.
Danh sách file cần chỉnh sửa
Thao tác | file | Mô tả |
---|---|---|
Sửa | app/View/Layouts/default.ctp | HTML templateト |
Sửa | app/webroot/js/require-config.js | set các sự phụ thuộc |
Sửa | app/webroot/js/main.js | Khởi đầu của ứng dụng |
Sửa | app/webroot/js/app.js | Ứng dụng (Marionettte.Application) |
Sửa | app/webroot/js/routers/router.js | Router (Marionette.AppRouter) |
Sửa | app/webroot/js/routers/controller.js | Controller |
default.ctp
Đọc các phần của mỗi file js sẽ thay đổi một cách cẩn thận, để ý dấu + và - như đã quy ước ở các bài trước nhé bạn.
<html>
<!-- Hiển thị template của màn hình detail -->
<script type="text/template" id="todo-detail-item-template">
<h2>Todo #<%- id %></h2>
<div>
<textarea style="width:300px;height:50px" id="edit-todo" autofocus placeholder="Todo?"><%- todo %></textarea>
<input type="button" id="updateTodo" value="Update"></input>
<input type="button" id="updateCancel" value="Cancel"></input>
</div>
</script>
- <!-- js(library) -->
- <script src="js/lib/jquery-2.1.3.min.js" type="text/javascript"></script>
- <script src="js/lib/underscore-min.js" type="text/javascript"></script>
- <script src="js/lib/backbone-min.js" type="text/javascript"></script>
- <script src="js/lib/backbone.marionette.min.js" type="text/javascript"></script>
-
- <!-- js(application) -->
- <!-- model -->
- <script src="js/models/todo-model.js" type="text/javascript"></script>
- <!-- collection -->
- <script src="js/collections/todo-collection.js" type="text/javascript"></script>
- <!-- view -->
- <script src="js/views/todo-item-view.js" type="text/javascript"></script>
- <script src="js/views/todo-detail-item-view.js" type="text/javascript"></script>
- <script src="js/views/todo-detail-layout-view.js" type="text/javascript"></script>
- <script src="js/views/todo-composite-view.js" type="text/javascript"></script>
- <script src="js/views/todo-layout-view.js" type="text/javascript"></script>
- <!-- controller -->
- <script src="js/routers/controller.js" type="text/javascript"></script>
- <!-- router -->
- <script src="js/routers/router.js" type="text/javascript"></script>
- <!-- application -->
- <script src="js/app.js" type="text/javascript"></script>
- <!-- entry point -->
- <script src="js/main.js" type="text/javascript"></script>
+ <!-- require -->
+ <script type="text/javascript" src="js/require-config.js"></script>
+ <script type="text/javascript" src="js/lib/require.js" data-main="main.js"></script>
</body>
</html>
- Xóa tất cả các phần khai báo file js ngoài
- Khai báo file
require-config.js
- Thiết lập xong các sự phụ thuộc
- Khai báo
require.js
- require.js sẽ được đọc ở body. Sau khi đọc xong, với require.js, các file cụ thể trong thành phần
data-main
sẽ được thực thi, đây là entry point của chương trình này.
- require.js sẽ được đọc ở body. Sau khi đọc xong, với require.js, các file cụ thể trong thành phần
require-config.js
Đây là file sẽ thiết lập sự phụ thuộc giữa mỗi file js.
// cấu hình require
var require = {
// Chặn cache
urlArgs: "v=" + (new Date()).getTime(),
// Xác định base URL mà module sẽ đọc
baseUrl: '/rest-study/js/',
// Xác định path của từng file
paths : {
'jquery' : 'lib/jquery-2.1.3.min',
'underscore' : 'lib/underscore-min',
'backbone' : 'lib/backbone-min',
'marionette' : 'lib/backbone.marionette.min',
},
// Xác định sự phụ thuộc
shim : {
'jquery' : {
exports : '$'
},
'underscore' : {
deps : ['jquery'],
exports : '_'
},
'backbone' : {
deps : ['jquery', 'underscore'],
exports : 'Backbone'
},
'marionette' : {
deps : ['backbone'],
exports : 'Marionette'
},
}
};
- Biến baseUrl
- Mỗi module sẽ tiếp tục xác định path cho mỗi file JS, nhưng là path liền sau path hiện tại, và trong trường hợp này nó sẽ xác định là
'/rest-study/js/'
.
- Mỗi module sẽ tiếp tục xác định path cho mỗi file JS, nhưng là path liền sau path hiện tại, và trong trường hợp này nó sẽ xác định là
- Biến paths
- Chỉ ra thư mục mà requirejs sẽ tìm load các file với phần mở rộng là (.js), đã được thừa nhận nên bạn không cần ghi đuôi mở rộng nữa.
- Biến shim
- Nó sẽ định nghĩa sự phụ thuộc của thư viện mà không tương thích với AMD.
- Biến deps sẽ xác định path của thư viện phụ thuộc ( theo tên mà bạn đã xác định trong biến path ), như trên có nghĩa là khi load
marionette
, chương trình sẽ tự hiểu là cần đọc backbone trước ( trong deps đã ghi backbone ) vì thế nó tạm bỏ quamarionette
chờ đọc xong thư viện mà nó phụ thuộc đã, và khi nó nhảy lên đọc backbone thì lại thấy backbone phụ thuộc vào 2 thư viện trên nó làjquery
và cảunderscore
, nó tiếp tục đọc tiếp 2 thư viện kia trước đã...cứ thế... - Biến exports sẽ xác định tên của object mà thư viện đã định nghĩa, với việc ghi ở đây thì các bạn đỡ phải require luôn cả đường dẫn.
- Biến urlArgs
- Sử dụng để tắt cache. Module này sẽ đọc trong URL kiểu như
v=XXX
. Tại đây, URL sẽ được thay đổi bởi thêm những giá trị thời gian qua đó mình sẽ chặn việc trình duyệt không tải những file mới mình đã chỉnh sửa mà load các file cũ từ cache.
- Sử dụng để tắt cache. Module này sẽ đọc trong URL kiểu như
Tại đây jquery, underscore, backbone, marionette sẽ được load
Thật sự thì shim setting sẽ không thay đổi phiên bản của cả backbone lẫn marionette vì nó đã được tương ứng với AMD, nó sẽ di chuyển ngay khi biến path được thiết lập.
main.js
Đây là entry point, tất cả đều phải bắt đầu từ hàm require
.
Từ file này, sẽ được đọc từ require.js, Để kiểm tra tiến trình của việc thực thi, chúng ta cần đặt một đoạn console log (console.log()
).
Tôi sẽ đặt cả phần trước và sau thay đổi tại đây.
var app = app || {};
(function(app) {
app.application = new app.Application();
app.application.start();
})(app);
//Bắt đầu
console.log('load main');
require([
'marionette'
],
function(){
console.log('run main');
require(['app'], function(Application){
console.log('run main2');
window.application = new Application();
window.application.start();
console.log('app start');
});
});
- Đầu tiên, chúng ta hoàn toàn có thể xác định được
marionette
bằng hàmrequire
, bạn có thể đọclib/backbone.marionette.min.js
. - Ở hàm callback sau khi đọc xong, chạy hàm
require
bạn sẽ load được fileapp.js
. - Tiếp tục trong hàm callback sau khi đọc xong, nó sẽ chạy hàm
application.start()
. - Sau cùng để tiếp tục theo dõi tiến trình, bạn đặt 2 cái log ở đầu và cuối của hàm.
Các hàm sẽ chạy như sau.
- Ở đối số đầu tiên, nó sẽ xác định file javascript để đọc. Có thể xác định như sau đây:
- tên
paths
củarequire-config.js
đã định nghĩa ở biến. xác định file'lib/backbone.marionette.min.js'
củamarionette
được load. - Những path liên quan từ các path đã được xác định nằm ở
baseUrl
củarequire-config.js
. Ởapp
thì đuôi mở rộng .js đã được quy ước, vì vậyapp.js
đã được load.
- tên
- Sau khi đọc xong, các hàm được xác định ở đối số thứ 2 đã được thực thi, các đối số của hàm sẽ được định nghĩa ở
app.js
Sửa chương trình trước, Global scope của app
object giữ đường dẫn của tất cả object được tạo ra. app
có thể truy cập vào một object để sử dụng khi cần.
Sau khi sửa, Bởi vì chỉ có duy nhất application
nên object mà bạn muốn đặt vào global scope chúng ta sẽ đặt trực tiếp vào object windows
.
app.js
Hàm define
để định nghĩa các object.
Sau đó router và controller sẽ xác định view của object bằng hàm define
trong tất cả các file.
var app = app || {};
//bắt đầu
(function(app) {
app.Application = Backbone.Marionette.Application.extend({
initialize : function(){
new app.TodoRouter();
},
onStart : function(){
Backbone.history.start();
},
regions : {
mainRegion : '#main'
}
});
})(app);
//Application
console.log('load app');
define(function(require){
console.log('run app');
var Router = require('routers/router');
var Application = Marionette.Application.extend({
initialize : function(){
console.log('app.initialize');
new Router();
},
onStart : function(){
Backbone.history.start();
},
regions : {
mainRegion : '#main'
}
});
return Application;
});
- Chức năng và Hoạt động của hàm
define
như sau.- Tại hàm
require
, đọc filerouters/router.js
, lấy được objectRouter
. - Như tiến trình của file trước khi thay đổi, chúng ta tạo ra các object của ứng dụng.
- Trở lại việc tạo Application
- Tương tự như vậy thì main.js - app object của global scope không được sử dụng. Bằng việc return, nó sẽ được dùng từ người gọi.
- Tại hàm
Các file khác mình cũng sẽ sửa chúng như vậy.
- Sử dụng hàm define để định nghĩa object.
- Sử dụng hàm require ở phần đầu để tiếp tục load các module cần thiết.
- Khi sử dụng module, return để tạo module mới.
Sự khác nhau giữa require
và define
Các quá trình hầu hết là giống nhau, nhưng có vài sự khác biệt sau đây:
- require: Module không được định nghĩa, chỉ có sự thực thi của tiến trình ( tôi đang sử dụng module ở main.js vì
require
chưa định nghĩa). - define: Để định nghĩa module (ở app.js, nó sẽ định nghĩa các object thừa kế từ
Marionette.Application
).
Tôi sẽ miêu tả sau, nó require những hàm cũng được sử dụng để đọc các file view khi chuyển đổi giữa view động và view tĩnh.
Một cách viết define khác
Hàm define có thể được viết như sau
//Application
console.log('load app');
define([
'routers/router'
],
function(Router){
console.log('run app');
var Application = Marionette.Application.extend({
initialize : function(){
console.log('app.initialize');
new Router();
},
onStart : function(){
Backbone.history.start();
},
regions : {
mainRegion : '#main'
}
});
return Application;
});
- Ở đối số đầu tiên
routers/router
được xác định ( với việc đuôi mở rộng .js được quy ước), chúng ta sẽ đọc thànhrouters/router.js
. - Sau khi đọc xong, hàm sẽ được xác định ở đối số thứ 2 được thực thi, đối số của hàm sẽ là sự định nghĩa của ofject trong
routers/router.js
.
Cũng ở format này, nhưng, nhưng để "đọc bằng việc sử dụng hàm require ngay khi bắt đầu file chức năng" như đã được giới thiệu trên đầu thường sẽ là cái file đọc cao hơn trong số lượng file để đọc. Ví dụ, đây là một chương trình như thế:
define([
'foo',
'bar',
'baz',
'hoge,
'fuga',
'piyo'
],
function(Foo,
Bar,
Baz,
Hoge,
Fuga,
Piyo){
var module = define-module;
return module;
});
Và đường dẫn của file cần đọc rất khó để đọc những biến tương ứng được lưu trữ ở module đó đã sinh ra bởi file này.
Trong khi với cách đầu tiên.
define(function(require){
var Foo = require('foo');
var Bar = require('bar');
var Baz = require('baz');
var Hoge = require('hoge');
var Fuga = require('fuga');
var Piyo = require('piyo');
var module = Xác-Định-Module;
return module;
});
Ở cách này, chỉ 1 dòng tại thời điểm require được viết ở đầu hàm, sẽ rất dễ dàng để thấy và cũng dễ bật các file và biến.
#Include
na ná import
trong ngôn ngữ jave hay C, bạn có thể để nó gọn gàng ở đầu file.
Thời diểm này nó sẽ được định nghĩa ở method mô tả.
router.js
Không có gì đặc biệt ở việc chỉnh sửa file này cả nên hãy sửa giống những gì bạn đã làm với app.js.
Như thường lệ tôi sẽ đặt lod để kiểm tra tiến trình.
//router
console.log('load router');
define(function(require) {
console.log('run router');
var TodoController = require('routers/controller');
var TodoRouter = Marionette.AppRouter.extend({
//khởi tạo controller
controller: new TodoController(),
//Cấu hình routing
appRoutes : {
'' : 'todoLists',
'todo-lists' : 'todoLists',
'todo-lists/:id' : 'todoDetail'
},
});
return TodoRouter;
});
controller.js
Cũng được sửa theo cách này nhưng co vai thự thay đổi ở phần đọc method của view.
//controller
console.log('load controller');
define(function() {
console.log('run controller');
var TodoController = Marionette.Controller.extend({
todoLists : function() {
//Routing tới view cho Todo layout
this.nextView('views/todo-layout-view');
},
todoDetail : function(id) {
this.nextView('views/todo-detail-layout-view', {modelId : id});
},
nextView : function(viewPath, option) {
require([viewPath], function(View){
window.application.mainRegion.show(new View(option));
});
},
});
return TodoController;
});
- Mô tả sự thay đổi của phần đọc của view
todoLists : function() {
//Routing tới view của Todo layout
- this.nextView(app.TodoLayoutView);
+ this.nextView('views/todo-layout-view');
},
- Trước khi chỉnh sửa, nó vẫn chưa xác định được view lưu trữ của object
app
. - Sau khi thay đổi, bạn cần xác định path tới file js của view.
- nextView : function(View, option) {
- app.application.mainRegion.show(new View(option));
+ nextView : function(viewPath, option) {
+ require([viewPath], function(View){
+ window.application.mainRegion.show(new View(option));
+ });
},
- Trước khi chỉnh sửa, tôi chỉ show phần view của region.
- Sau khi sửa xong, chạy hàm
require
, đọc file js và hàm callback sau khi đọc hoàn tất, view nhận được sẽ là argument của region mà chúng ta show.
Kiểm tra trình tự bằng Log
Truy cập http:PublicIP
/rest-study
Output của log như sau
Mô tả từng dòng
Log | Giải thích |
---|---|
load main | bắt đầu chạy main.js |
run main | Bắt đầu chạy các file đã được xác định ở phần require của main.js |
load app | Bắt đầu chạy app.js |
load router | Chạy route.js |
load controller | Chạy controller.js |
run controller | Chạy những hàm đã được xác định trong phần define của controller.js |
run router | Chạy những hàm đã được xác định trong phần define của router.js |
run app | Chạy những hàm đã được xác định trong phần define của app.js |
run main2 | tại main.js, việc thực thi các hàm mà bạn xác định trong phần require bắt đầu. |
app.initialize | Hàm khởi tạo bắt đầu chạy trong Marionette.Application object |
app start | Bắt đầu chạy application.start() (Hoàn tất main.js) |
図Ảnh mình họa như sau
Ở mỗi js file, nó sẽ thực hiện theo trình tự sau
- Tự load chính nó
- load những file phụ thuộc
- Sau khi hoàn thành load bắt đầu chạy các hàm
( Hiện tại thì chương trình vẫn chưa chạy được vì vẫn còn những file khác chưa được cấu hình theo require.js )
Tóm tắt bài học
Đây là các bước thực hiện của chương trình
-
app/View/Layouts/default.ctp
Sửa như mô tả trên.。 -
app/webroot/js/require-config.js
Thêm mới với nội dung như trên. -
app/webroot/js/main.js
Sửa như mô tả trên.。 -
app/webroot/js/app.js
Sửa như mô tả trên.。 -
app/webroot/js/routers/router.js
Sửa như mô tả trên.。 -
app/webroot/js/routers/controller.js
Sửa như mô tả trên.。 - Kiểm tra kết quả
- Commit lên Git
Tham khảo kết quả trên Git
1 application.start() theo dõi tiến trình · suzukishouten-study/rest-study@acc0b1d
Sang bài 2.
Bài 2: Các phần còn lại của require.js
Mỗi file đều tương ứng với require.js. Vì vậy bạn có thể sửa nội dung như những file đã giới thiệu trong bài 1, không có gì mới.
Tôi sẽ chỉ liệt kê link đến các file cần sửa, các bạn có thể xem link tham khảo ở dưới để xem cách sửa 1 vài file và tự thao tác với các file còn lại để nhớ cú pháp.
-
app/webroot/js/collections/todo-collection.js
Sửa như bài 1. -
app/webroot/js/models/todo-model.js
Sửa như bài 1. -
app/webroot/js/views/todo-layout-view.js
Sửa như bài 1. -
app/webroot/js/views/todo-composite-view.js
Sửa như bài 1. -
app/webroot/js/views/todo-item-view.js
Sửa như bài 1. -
app/webroot/js/views/todo-detail-item-view.js
Sửa như bài 1. -
app/webroot/js/views/todo-detail-layout-view.js
Sửa như bài 1. - Kiểm tra kết quả
- Commit lên Git
Tài liệu tham khảo (GitHub)
2 Những file tương ứng còn lại của require.js · suzukishouten-study/rest-study@1e590d4
Các theme
###「Teamplate được viết vào default.ctp là một file riêng biệt, vì vậy nó sẽ được đọc tĩnh trong thời gian chuyển trang」
Nếu có gì thắc mắc hay góp ý, vui lòng gửi cho tôi những comment hay feedback.
Đây là bản commit hoàn chỉnh của bài học này chúng tôi đã upload lên Github, các bạn có thể xem để hình dung ra cách thực hiện, nhưng mong bạn chỉ coi để tham khảo ⇢ Dringling Dev template in RequireJS · suzukishouten-study/rest-study@28d2fb5