BackboneJS - xèng

/imgposts/3gkekfe8.jpg

Nếu bạn đang nghĩ đến việc sử dụng BackboneJS như một khung ứng dụng phía trước, thì tốt nhất là đừng. Nếu so sánh AngularJS với một khẩu súng trường, thì BackboneJS chỉ giống như một cành cây, hiệu suất tiêu diệt kẻ thù quá thấp. Chỉ khi kết hợp với các khung dựa trên BackboneJS (chẳng hạn như Marionette), năng suất làm việc mới được cải thiện.

Những thao tác nào cần đặt trong Models?

  • Thêm, xóa, sửa, tra cứu dữ liệu
  • Chuyển đổi kiểu dữ liệu
  • Kiểm soát quyền truy cập
  • Xác thực dữ liệu, ví dụ như xác thực định dạng email
  • Tạo dữ liệu để hiển thị, chẳng hạn như tạo tên đầy đủ từ họ và tên

Một số hàm cốt lõi được tích hợp sẵn trong Backbone.js Model:

  • extend: Kế thừa Backbone.Model và mở rộng nó
  • set: Đặt thuộc tính đồng thời kích hoạt sự kiện change. Lưu ý phổ biến mà tôi thường mắc phải là cố gắng đọc một thuộc tính bằng cách dùng model instance.add .property... luôn quên rằng cần dùng get.
  • validate: Xác thực dữ liệu, được gọi khi save() hoặc set() được thiết lập với {validate: true}

Có một tính năng ẩn: Khi this.model đã qua kiểm chứng, nó sẽ trả về đối tượng được thiết lập thành công; nếu không qua kiểm chứng, nó sẽ trả về false.

1var model = this.model.set(attrs, {validate: true});
2if (!model) {
3  console.log('invalid attrs');
4}

Xử lý kết quả của model.save, tham khảo How do I trigger the success callback on a model.save()?

1this.model.save(null, {
2  success: function (model, response) {
3    console.log("success");
4  },
5  error: function (model, response) [xèng](/blog/fix-social-account-icon-at-bottom-of-magento-not-display/)  {
6    console.log("error");
7  }
8});
  • Bước đầu tiên với Backbone.js: Xác thực Model

Đứng giữa template và collection, có thể nói là chất keo nối giữa dữ liệu phía sau và giao diện phía trước. Giống như người điều khiển rối trong trò rối vậy!

Những thao tác nào cần đặt trong Views?

  • Thông thường, collection add() được ràng buộc với render() của Views
  • Các thao tác tương ứng với cú nhấp chuột của người dùng, thao tác bàn phím
  • Tạo một DOM element mới hoặc thao tác trên một element đã tồn tại trong trang
  • el tương ứng với mã HTML
  • $el tương ứng với đối tượng jQuery

Trước khi render, cần:

1this.$el.empty()

Tham khảo Backbone.js collection view rendering duplicate items

Tình trạng lý tưởng nhất là sử dụng tagName, vì vậy không cần ràng buộc thông qua id của element hiện tại. Chỉ cần append view được tạo bởi tagName vào view cha, và khi model liên quan đến view có sự kiện change, chỉ cần render lại. Cách này có thể ép bạn tinh chỉnh view hơn.

Là tập hợp của Models

  • Cài đặt URL RESTFul phía sau
  • Thêm, xóa, sửa, tra cứu

Sử dụng phương pháp loại trừ, phân tích lich thi dau ngoai hang anh hom nay từng bước:

  • Gắn trong initialize của collection

Trong collection, chỉ định view cụ thể, nhưng nếu chỉ định view, collection đó sẽ không tái sử dụng được; truyền view qua tham số cũng không hợp lý, đặc biệt nếu có nhiều view cùng cấp trên một trang, không nên áp dụng.

  • Gắn trong collection view

Collection view khi khởi tạo cần truyền tham số là một collection, đối tượng collection toàn cục này có thể định nghĩa trong đối tượng app toàn cục hoặc trong app view. Hợp lý.

Nếu không sử dụng app view, thì đối tượng toàn cục app cần làm là tạo đối tượng collection, đối tượng collection view, và lấy dữ liệu từ collection. Hoàn toàn không cần dùng đến app view.

Create sẽ gọi add. Quy trình thực tế của create là:

  1. Gọi add thiết lập {wait: true} sẽ đợi lưu trữ server xong mới gọi add
  2. Gửi yêu cầu đến server

Add không liên quan đến gửi yêu cầu đến server.

Phân loại mục là tình huống phổ biến.

  • Cách nhóm backbone collection theo category của model, sau đó sắp xếp mỗi nhóm theo rank của model

Với ứng dụng todos, trước tiên hãy khởi tạo một collection với dữ liệu thử nghiệm, thay vì sử dụng collection.fetch() để lấy dữ liệu từ phía sau.

1var Todos = Backbone.Collection.extend({
2  model: Todo,
3  url: '/api/todos',
4});
5var todos = new Todos([
6  {title: 'coding'},
7  {title: 'running'}
8]);
9new TodosView(todos);

Khi interface phía sau hoàn thiện, chuyển sang:

1var todos = new Todos();
2todos.fetch();
3new TodosView(todos);

Nghe sự kiện Model trong View

1this.listenTo(model, 'change:name', this.changeName) // Một thuộc tính cụ thể của model bị thay đổi
2this.listenTo(model, 'change', this.change)  // Bất kỳ thuộc tính nào của model bị thay đổi

Nghe sự kiện Collection trong View

1this.listenTo(collection, 'change', this.addOne)  // Thuộc tính của model trong collection thay đổi
2this.listenTo(collection, 'add', this.addOne)  // Model được thêm vào collection
3this.listenTo(collection, 'remove', this.removeOne)  // Model bị xóa khỏi collection
4this.listenTo(collection, 'reset', this.render)   // collection.fetch({reset: true})

Sự kiện liên quan đến request (TODO: Test thất bại)

1request // Bắt đầu gửi request
2sync  // Request thành công
3error  // Request thất bại

Nghe sự kiện element trong template View

1events: {
2 "click #commitBtn": 'commitInfo',  
3 'mouseover .title': 'mouseoverTitle', 
4 'keypress #commitForm': 'commitInfoOnEnter',  // function(e) { if (e.which === 13) { this.commitInfo(); }}
5}

Tham khảo Events catalog - Tài liệu chính thức Backbone

Từ ngữ pháp, on gắn sự kiện của object hiện tại

object.on(event, callback, [context]) listenTo gắn sự kiện của object khác object.listenTo(other, event, callback)

Ứng dụng rất rõ ràng:

  • listenTo phù hợp để view gắn sự kiện thay đổi của collection, model
  • on phù hợp để model gắn sự kiện thay đổi thuộc tính tương ứng với xác thực và làm mới

Ví dụ, sự kiện gắn trong TodosView, tức là lắng nghe sự kiện của collections

1this.listenTo(app.todos, 'add', this.addOne);
2this.listenTo(app.todos, 'reset', this.addAll);
3this.listenTo(app.todos, 'change:completed', this.filterOne);
4this.listenTo(app.todos, 'filter', this.filterAll);
5this.listenTo(app.todos, 'all', this.render);

Nếu gắn trong collections, thì:

1this.on('change:completed', this.filterOne);

Tham khảo Collection add event listener in Backbone

Thông thường, các tài liệu hướng dẫn không đề cập đến cách phân chia views cho trang. Theo kinh nghiệm sử dụng AngularJS, càng phân chia view nhỏ càng tốt, giúp dễ dàng viết unit test. Dù là AngularJS hay BackboneJS, muốn tăng hiệu suất phát triển và giảm chi phí bảo trì, phần quan trọng nhất thực sự là việc phân chia view/controller. Làm thế nào để phân chia vùng giao diện trên thiết kế thành các view/controller độc lập. Backbone yêu cầu thêm thủ công ràng buộc DOM, so với AngularJS tăng thêm chi phí đặt id, class cho DOM, khá khó chịu. Hãy tìm cách đơn giản hóa. Essentially, mỗi View đều chịu trách nhiệm một loạt logic chức năng. Làm thế nào để tổ chức tốt logic này?

Để giúp tổ chức logic này, chúng ta sẽ sử dụng mẫu element controller. Mẫu element controller bao gồm hai views: một quản lý tập hợp các mục, trong khi cái kia xử lý từng mục riêng lẻ. Tôi gọi mẫu element controller là "mẫu giám sát viên", tức là giám sát viên lớn quản lý giám sát viên nhỏ, giám sát viên nhỏ quản lý thợ, thợ chỉ cần lo phần việc của mình. Trước đây tôi gọi là "mẫu phân chia view", nhưng sau đó thấy tên này không hình ảnh hóa rõ ràng cảnh tượng phân chia, nên gọi là "mẫu giám sát viên" phù hợp hơn. Với ví dụ thực tế, AppView là view ngoài cùng, gắn vào một element trên trang index thông qua el: {id}, bên trong AppView chứa các view chức năng cụ thể.

Các phương án khả thi:

  • Vẫn nên gắn sự kiện lên collection là hợp lý hơn (xem phần Events về listenTo)
  • Sử dụng sự kiện pub/sub toàn cục

Làm thế nào để tránh sử dụng biến toàn cục. Khác biệt lớn nhất giữa Backbonejs và AngularJS là tài liệu chính thức không nhấn mạnh tầm quan trọng của unit test và không bắt buộc cấu trúc tránh xuất hiện mã không thể test.

Modularization là gì? Có thể hiểu là các module chức năng được tích hợp sẵn trong Python, chẳng hạn như datetime, time, os. Tương đương với việc tách mỗi chức năng thành một file js riêng. Lợi ích của modularization so với cấu trúc truyền thống của js:

  • Có thể thấy rõ mối quan hệ phụ thuộc giữa các file js. Ví dụ, một trang web có hai file js, jQuery.js và forum.js, forum.js thực tế phụ thuộc vào jQuery.js, nhưng không thể nhìn ra từ forum.js.
  • Load theo nhu cầu. Không tải tất cả các file js như cách truyền thống, mà chỉ tải những cái cần thiết.
  • Không cần tự tổ chức thứ tự load file js. Vì khi các file js phụ thuộc lẫn nhau ngày càng nhiều, thứ tự load sẽ trở nên khó khăn hơn để làm rõ.

Do SeaJS được viết bởi người Trung Quốc, tài liệu tiếng Anh ít, nên tạm thời sử dụng RequireJS (gần như tất cả các tutorial Backbone.js đều nhắc đến RequireJS). Vậy frontend MVC sẽ dùng RequireJS như thế nào? Giống như Django's view.py sẽ tham chiếu đến model.py, view Backbone.js cũng sẽ dùng đến model, đây là một mối quan hệ phụ thuộc. (Cũng sẽ dùng đến jQuery, backbone, underscore).

Trong success hoặc done, error của ajax, sử dụng this.<method_name> luôn báo lỗi. Hiển thị rằng đối tượng này không có phương thức tương ứng. Lý do là this trong đây không đại diện cho đối tượng Backbone. Giải pháp là ở ngoài ajax sử dụng:

1var that = this;

Rồi trong nội bộ sử dụng that.<method_name>

The this reference within all callbacks is the object in the context option passed to $.ajax in the settings; if context is not specified, this is a reference to the Ajax settings themselves. Problems with Backbone.Model callback and THIS PS: Link này có ví dụ khá tốt. Giải thích rõ ràng cách sử dụng Backbone.

  • Javascript's this usage
  • JavaScript, 5 ways to call a function
  • Understanding _.each on backbone collections

TODO

AppView là Top level view, view ngoài cùng. Muốn hiểu rõ Router và AppView tồn tại cùng nhau như thế nào, trước hết cần xác định Router và AppView mỗi cái làm gì. Backbone là framework rất linh hoạt, cùng một chức năng có thể có nhiều cách thực hiện khác nhau, trước tiên hãy nói về cách hiểu của tôi về AppView. Ý nghĩa tồn tại của AppView là làm container cho toàn bộ ứng dụng, namespace, lưu trữ các biến toàn cục chung cho ứng dụng. Giả sử ứng dụng hiện tại có hai trang tương đối độc lập, HomeView và AboutView, mỗi cái có đường dẫn phía trước tương ứng #home #about, mặc định vào HomeView. Rõ ràng HomeView và AboutView đều là sub View của AppView. AppView cần lắng nghe sự kiện click để xác nhận cần chuyển đến sub View nào, rồi kích hoạt qua router.navigate(). Do đó, đặt router bên trong AppView là cách làm hợp lý. Vấn đề đặt ra, handler của mỗi đường dẫn phía trước trong router cần làm gì? Nếu là khởi tạo HomeView, AboutView, điều kiện tiên quyết là AppView đã render xong. Điều này không phải vấn đề, có thể render AppView trước, sau đó khởi tạo Router. Còn render HomeView, AboutView thì trong AppView cần tìm nơi mount. Nếu HomeView có 3 sub view cùng cấp? Trong handler của Router khó có thể thực hiện, vì 3 sub view này cần dựa trên trang mà HomeView đã render, có thể giới thiệu sub router? Sau khi suy nghĩ kỹ, không cần sub router vẫn có thể thực hiện được. Mỗi sub view khi khởi tạo cần kiểm tra xem instance của HomeView đã tồn tại chưa, nếu có thì bỏ qua, nếu không thì khởi tạo một cái. Quá trình này thực hiện trong handler của router. Đồng thời, hàm khởi tạo của HomeView cần hỗ trợ truyền tham số để xác định sub view mặc định là cái nào, để tránh trải nghiệm chớp nhoáng. Cách chuyển đến sub view khác từ sub view:

1// trigger thiết lập có kích hoạt handler tương ứng trong router hay không
2// replace thiết lập có ghi URL vào lịch sử duyệt web hay không
3appView.router.navigate("pay", {trigger: true, replace: true});

Hiện tại sử dụng template tích hợp sẵn Underscore chưa gặp chỗ nào không thuận tiện

Nếu sử dụng

1I am a useless link

sẽ can thiệp vào đường dẫn phía trước, thay thế bằng

1I am a useless link