1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Các bài học lập trình ứng dụng RESTful ~ Chuyên đề phát triển Web - [Chương 4] Sử dụng RequireJS để chia module cho JavaScript

Last updated at Posted at 2015-10-14

:large_blue_circle: 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.

:warning: 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ó.

:large_blue_circle: 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

app.js
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.

main.js
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.
app_module.png

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.

:white_check_mark: 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

:large_blue_circle: Đ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.

default.ctp(trích)
	<!-- 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" .

router.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.

:large_blue_circle: 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.

AMDSample(todo-collection.js)
//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é.

:large_blue_circle: 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.
:warning: 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ị !

:large_blue_circle: Chuẩn bị

Đăng nhập bằng SSH

Hãy bắt đầu với việc đăng nhập bằng SSH

  • :white_check_mark: ssh -i [秘密鍵のパス] study@[サーバのPublicIP]

chuyển vào thư mục làm việc

  • :white_check_mark: 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 đó.

:warning: 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.

:white_check_mark: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.

:white_check_mark: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ó.
:warning: 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.

  • :white_check_mark: git branch để chắc chắn bạn đang ở branch master
  • :white_check_mark: Nếu bạn đang không ở master, nhập lệnh git checkout để checkout vào master

Tạo branch vol/03 bằng các lệnh dưới đây

  • :white_check_mark: git branch vol/04 Tạo branch
  • :white_check_mark: git checkout vol/04 chuyển đến branch vol/04 vừa tạo

Xác nhận

Chúng ta chỉ chắc chắn trong trường hợp

  • :white_check_mark: 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.
:warning: 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

:large_blue_circle: 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).

  • :white_check_mark: 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ó.

app/Controller/AppController.php
 class AppController extends Controller {
     public $components = array(
-        'DebugKit.Toolbar',
         'RequestHandler'
     );
 }
  • :white_check_mark: Mở app/Controller/AppController.php để chỉnh sửa.
  • :white_check_mark: Kiểm tra kết quả.
  • :white_check_mark: 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)

:large_blue_circle: 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.
  • 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.

default.ctp

 <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-config.js

Đây là file sẽ thiết lập sự phụ thuộc giữa mỗi file js.

require-config.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/' .
  • 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ỏ qua marionette 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.

Tại đây jquery, underscore, backbone, marionette sẽ được load

:warning: 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.

main.js(TrướcThayĐổi)
var app = app || {};

(function(app) {
	app.application = new app.Application();
	app.application.start();
})(app);
main.js(SauThayĐổi)
//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àm require, bạn có thể đọc lib/backbone.marionette.min.js.
  • Ở hàm callback sau khi đọc xong, chạy hàm require bạn sẽ load được file app.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ủa require-config.js đã định nghĩa ở biến. xác định file 'lib/backbone.marionette.min.js' của marionette được load.
    • Những path liên quan từ các path đã được xác định nằm ở baseUrl của require-config.js . Ở app thì đuôi mở rộng .js đã được quy ước, vì vậy app.js đã được load.
  • 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.

app.js(Trước)
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);
app.js(Sau)
//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 file routers/router.js, lấy được object Router.
    • 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.

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.

:warning: Sự khác nhau giữa requiredefine
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).

:warning: 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.

:warning: Một cách viết define khác
Hàm define có thể được viết như sau

app.js
//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ành routers/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ế:

VD(CáchTrên)
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.

VD(NhưĐãGiớiThiệuBanĐầu)
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.js
//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.js
//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
SựThayĐổiCủaHàm
 		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.
SựThayĐổiCủaHàmnextView
-		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

lesson1_log.png

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

require_flow.png

Ở mỗi js file, nó sẽ thực hiện theo trình tự sau

  1. Tự load chính nó
  2. load những file phụ thuộc
  3. 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

  • :white_check_mark: app/View/Layouts/default.ctp Sửa như mô tả trên.。
  • :white_check_mark: app/webroot/js/require-config.js Thêm mới với nội dung như trên.
  • :white_check_mark: app/webroot/js/main.js Sửa như mô tả trên.。
  • :white_check_mark: app/webroot/js/app.js Sửa như mô tả trên.。
  • :white_check_mark: app/webroot/js/routers/router.js Sửa như mô tả trên.。
  • :white_check_mark: app/webroot/js/routers/controller.js Sửa như mô tả trên.。
  • :white_check_mark: Kiểm tra kết quả
  • :white_check_mark: 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.

:large_blue_circle: 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.

  • :white_check_mark: app/webroot/js/collections/todo-collection.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/models/todo-model.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/views/todo-layout-view.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/views/todo-composite-view.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/views/todo-item-view.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/views/todo-detail-item-view.js Sửa như bài 1.
  • :white_check_mark: app/webroot/js/views/todo-detail-layout-view.js Sửa như bài 1.
  • :white_check_mark: Kiểm tra kết quả
  • :white_check_mark: 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.

:warning: Đâ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

Cảm ơn các bạn đã theo dõi bài viết, rất mong nhận được phản hồi quý báu từ các bạn.

1
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?