Tối ưu hóa khởi tạo dự án Backbone.js bằng Bower

Tối ưu hóa khởi tạo dự án Backbone.js bằng Bower

game bài đổi thưởng ios,nhận định bóng đá keonhacai

Lập trình RESTful với Backbone.js

Ngày cập nhật: 12-12-2015 | Số lần xem: 9568 | Danh mục: BackboneJS

Tìm kiếm

Gần đây, tôi đã tham gia vào một số dự án chủ yếu liên quan đến giao diện người dùng để hiển thị danh sách và thực hiện các thao tác CRUD (tạo, đọc, cập nhật, xóa). Vì vậy, tôi dành thời gian để tóm tắt lại những kinh nghiệm đã tích lũy được. Cắt cỏ trước khi chặt cây là điều nên làm.

Một trong những điều gây khó chịu khi sử dụng framework như Backbone.js là việc phải tự tay tải về từng thư viện phụ thuộc mỗi lần bắt đầu một dự án mới. Ví dụ, để chạy Backbone.js, bạn cần có:

  • Underscore
  • jQuery (được sử dụng trong Backbone.View để xử lý DOM)

Thông thường, người ta sẽ tải từng file .js của các thư viện này về và đặt chúng vào thư mục js/lib. Điều này mất khá nhiều thời gian – khoảng 5 phút cho một dự án mới. Điều đó khiến việc tạo ra một dự án mới trở nên không mấy hấp dẫn. Chính vì vậy, Bower đã ra đời.

Cài đặt Bower:

1
sudo npm install -g bower

Tạo cấu hình Bower:

1
bower init

Cài đặt Backbone.js:

1
bower install backbone --save

Sau khi cài đặt, bạn sẽ thấy xuất hiện một thư mục tên là bower_components, chứa toàn bộ các tập tin cần thiết. Kết quả cây thư mục sẽ như sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
bower_components/
├── backbone
│   ├── backbone.js
│   ├── backbone-min.js
│   ├── backbone-min.map
│   ├── bower.json
│   └── LICENSE
└── underscore
    ├── bower.json
    ├── LICENSE
    ├── README.md
    ├── underscore.js
    ├── underscore-min.js
    └── underscore-min.map

Tuy nhiên, thư viện jQuery vẫn chưa được tải xuống. Bạn cần chạy lệnh sau để thêm nó:

1
bower install jquery --save

Vấn đề tiếp theo nảy sinh là liệu có nên đưa thư mục bower_components vào quản lý git hay không? Một cuộc tranh luận trên Stack Overflow về vấn đề này cho thấy cả hai mặt:

Ưu điểm của việc thêm thư mục này vào git là giúp việc triển khai dễ dàng hơn khi không thể truy cập được kho lưu trữ bên thứ ba. Tuy nhiên, nhược điểm rõ ràng là kích thước kho git sẽ tăng lên đáng kể.

Sau khi cân nhắc kỹ lưỡng, tôi quyết định vẫn đưa thư mục bower_components vào quản lý git. Lý do là để tránh rủi ro khi triển khai. Việc viết thêm vài dòng cấu hình Gulp để xử lý riêng cho thư mục này sẽ làm phức tạp quá trình khởi tạo ban đầu.

Tổ chức mã nguồn Backbone.js kết hợp với RequireJS

Lý do tại sao cần tổ chức mã nguồn một cách hiệu quả? Để trả lời câu hỏi này, hãy cùng phân tích các chức năng mà một ứng dụng điển hình cần có. Ví dụ, trong hệ thống quản trị một website tuyển dụng, các chức năng cơ bản bao gồm:

  • Hiển thị danh sách công việc
  • Thêm công việc mới
  • Cập nhật thông tin công việc
  • Xóa công việc
  • Tìm kiếm công việc

Dù các chức năng này đơn giản, nhưng việc gom tất cả vào một file JavaScript là không khả thi. Bởi vì model đại diện cho công việc có thể được tái sử dụng ở nhiều nơi khác nhau – ví dụ như trang đăng bài tuyển dụng từ phía người dùng hoặc trang quản trị viên. Do đó, việc tách model thành một file độc lập là rất cần thiết.

Cấu trúc thư mục gợi ý như sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
├── collections
│   └── jobs.js
├── models
│   └── job.js
├── templates
│   ├── edit_job.html
│   ├── list_job.html
│   └── tr_job.html
└── views
    ├── edit_job.js
    ├── list_job.js
    ├── new_job.js
    └── tr_job.js
├── main.js  // Bootstrap, cấu hình requirejs, định nghĩa Router, khởi tạo router

Thảo luận: Trong ví dụ trên, tôi không tách riêng ra một file router.js, mà thay vào đó kết hợp nó vào main.js. Mục đích là để giảm thiểu số lượng file và giữ cho cấu trúc gọn nhẹ. Nếu bạn chọn cách tách riêng:

  • main.js: chỉ để cấu hình requirejs
  • app.js: dùng để load router
  • router.js: định nghĩa router

Cách này nghe có vẻ hợp lý, nhưng thực tế lại không cải thiện rõ rệt cấu trúc code. Ví dụ, app.js gần như không đóng góp gì giá trị cho ứng dụng. Nên thay vì thế, tôi chọn giải pháp đơn giản hơn: trong main.js, vừa định nghĩa vừa khởi tạo router trực tiếp. Về cơ bản, main.js cũng đóng vai trò là một app.js.

Tải trang từ phía server

Tạo một trang HTML mới:

1
2
3
<div id="container">
</div>
<script type="text/javascript" data-main="/static/src/js/main.js" src="/static/bower_components/requirejs/require.js"></script> 

Cấu trúc của file main.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
require.config({
  baseUrl: '/static/src/js/',
  paths: {
    jquery: '../../bower_components/jquery/dist/jquery.min',
    underscore: '../../bower_components/underscore/underscore-min',
    backbone: '../../bower_components/backbone/backbone-min',
    text: '../../bower_components/text/text',
    moment: '../../bower_components/moment/min/moment.min',
    admin_app: './admin_app'
  },
  shim: {
    underscore: {
      deps: [],
      exports: "_"
    },
    backbone: {
      deps: ['jquery', 'underscore'],
      exports: "Backbone"
    }
  },
});

define(function (require) {
  var Backbone = require('backbone');
  var JobListView = require('views/list_job');
  var NewJobView = require('views/new_job');
  var EditJobView = require('views/edit_job');
  var AdminRouter = Backbone.Router.extend({
    routes: {
      'new-job': 'newJob',
      'edit-job/:jobID':  'editJob',
      '*actions': 'index'
    },
    newJob: function() {
      var newJobView = new NewJobView();
      newJobView.render();
    },
    editJob: function(jobID) {
      var editJobView = new EditJobView(jobID);
      editJobView.render();
    },
    index: function() {
      var jobListView = new JobListView();
      jobListView.render();
    }
  });
  var router = new AdminRouter();
  Backbone.history.start();
});

Cấu trúc của file list_job.js

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
define(function(require) {
  var Backbone = require('backbone');
  var Jobs = require('collections/jobs');
  var JobView = require('views/tr_job');
  var jobListTemplate = require('text!templates/list_job.html');
  var JobListView = Backbone.View.extend({
    el: "#container",
    template: _.template(jobListTemplate),
    events: {
    },
    initialize: function () {
      this.jobs = new Jobs();
      this.listenTo(this.jobs, 'reset', this.render, this);
      this.jobs.fetch({
        reset: true,
        success: this.onDataHandler,
        error: this.onErrorHandler});
    },
    render: function () {
      this.$el.html(this.template());
      var ul = this.$el.find('#jobs-list');
      this.jobs.each(function(job) {
        var jobView = new JobView(job);
        ul.append(jobView.render().el);
      }, this);
    },
    onDataHandler: function() {
      console.log('onData');
      $('.am-progress-bar').width('100%');
      setTimeout(function() {
        $('.am-progress').hide();
      }, 1000);
    },
    onErrorHandler: function() {
      console.log('onError');
      $('.am-progress-bar').html('Lỗi mạng, không thể lấy dữ liệu. Vui lòng thử lại.');
    }
  });
  return JobListView;
});

Tài liệu

  • Bower
  • node.js - Làm thế nào để thay đổi thư mục mặc định của Bower? - Stack Overflow
  • git - Có nên bỏ qua thư mục bower_components không? - Stack Overflow
  • Phát triển ứng dụng Backbone.js
  • Tổ chức ứng dụng bằng Module (RequireJS) - cdnjs.com
  • Học lại Backbone.js – RequireJS (AMD và Shim) | BarDev
  • Thiết kế API RESTful bằng Python và Flask - miguelgrinberg.com
comments powered by Disqus
Built with Hugo
Theme Stack thiết kế bởi Jimmy