BackboneJS
Ngày cập nhật: 2020-04-27 | Số lần xem: 21589 | Số từ: 2930 | Phân loại: BackboneJS
Tìm kiếm
Nếu bạn muốn sử dụng Backbone.js như một framework front-end chính, thì tốt nhất nên cân nhắc lại. Nếu ví AngularJS là một khẩu súng lục đầy sức mạnh và hiệu quả, thì Backbone.js chỉ có thể so sánh như một đoạn cành cây – không đủ sắc bén để chiến đấu hiệu quả. Bạn sẽ cần kết hợp nó với các framework xây dựng trên nền tảng BackboneJS (như Marionette) mới có thể nâng cao năng suất phát triển.
Models
Những thao tác nào nên đặt trong Model?
- Thêm, xóa, sửa, tìm kiếm 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ụ kiểm tra định dạng email
- Tạo ra dữ liệu dùng để hiển thị, ví dụ ghép tên từ họ và tên thành tên đầy đủ
Các hàm cốt lõi của Backbone.js Model
extend
: kế thừa từBackbone.Model
và mở rộng thêm các chức năngset
: thiết lập thuộc tính và đồng thời kích hoạt sự kiệnchange
. Lưu ý sai lầm thường gặp là cố gắng lấy giá trị bằng cách gõmodel.property
, nhưng đúng cách phải làmodel.get('property')
validate
: xác thực dữ liệu khi gọisave()
hoặcset({ validate: true })
Một chi tiết ẩn giấu là sau khi this.model
được xác thực thành công, phương thức sẽ trả về đối tượng đã được thiết lập; nếu thất bại, sẽ trả về false
|
|
Xử lý kết quả của model.save()
, tham khảo “Làm thế nào để kích hoạt callback success khi gọi model.save()
?”
|
|
- Bước đầu tiếp cận Backbone.js: Xác thực Model
Views
Views đóng vai trò như cầu nối giữa template và collection, là chất keo dính giữa dữ liệu backend và giao diện frontend. Có thể hình dung nó như người điều khiển trong vở kịch rối – giữ vai trò dẫn dắt mọi hành động diễn ra.
!](ảnh minh họa về nghệ thuật rối - Naruto)
Những thao tác nào nên đặt trong View?
- Khi
collection.add()
thường được liên kết vớirender()
của View - Xử lý các sự kiện người dùng như click chuột, nhấn phím
- Tạo mới DOM element hoặc thao tác trên các phần tử hiện có trên trang
Sự khác biệt giữa View.el
và View.$el
el
tương ứng với phần HTML thuần túy$el
là một đối tượng jQuery
Tránh việc render View bị trùng lặp
Trước khi render, hãy nhớ:
|
|
Tham khảo bài viết: [Vấn đề về render duplicate item trong Backbone.js Collection View
Memory leak do Subview gây ra
Dưới đây là một giải pháp được chia sẻ trên Google Group:
|
|
Khi nào nên dùng tagName
hay el
để tạo View, chúng khác nhau ở điểm nào?
Tối ưu nhất là sử dụng tagName
, vì như vậy bạn không cần quan tâm đến việc gắn ID cụ thể. Chỉ cần append vào View cha và khi Model có sự kiện change
, bạn render lại View đó. Cách này giúp bạn phân tách View một cách rõ ràng hơn.
Collections
Là tập hợp các Models
- Thiết lập URL RESTful cho backend
- Thực hiện các thao tác CRUD (thêm, xóa, sửa, tìm kiếm)
Nên đặt sự kiện thay đổi của Collection ở đâu?
Sử dụng phương pháp loại trừ để phân tích:
-
Đặt trong
initialize
của Collection
Nếu bạn gán View trực tiếp trong Collection, thì Collection đó sẽ khó tái sử dụng. Truyền View qua tham số cũng không phải là cách tối ưu, đặc biệt khi có nhiều View cùng cấp cần đồng bộ. → Không khả thi. -
Đặt trong Collection View
Khi khởi tạo Collection View, bạn truyền vào một instance Collection. Instance này có thể được định nghĩa trong toàn cục (app
) hoặc trongAppView
. → Khả thi.
Liệu có cần thiết phải có AppView?
Nếu bạn không sử dụng AppView, thì toàn bộ logic sẽ được quản lý bởi một object toàn cục, nơi chứa Collection, Collection View, và fetch dữ liệu từ backend. Việc này hoàn toàn khả thi mà không cần đến AppView.
Sự khác biệt giữa add
và create
Phương thức create
sẽ kích hoạt add
. Quy trình thực tế của create
như sau:
- Gọi
add
(nếu có{wait: true}
thìadd
sẽ được gọi sau khi server lưu trữ thành công) - Gửi yêu cầu lên server
Ngược lại, add
không gửi bất kỳ yêu cầu nào đến server.
Nhóm dữ liệu trong Collection
Việc phân nhóm theo category là phổ biến. Ví dụ: nhóm các Model trong Collection theo category, sau đó sắp xếp từng nhóm theo rank.
Làm thế nào để điền dữ liệu giả vào Collection trong giai đoạn đầu phát triển?
Với ứng dụng Todos, bạn có thể khởi tạo một Collection với dữ liệu mẫu trước khi fetch từ backend.
|
|
Sau khi API backend sẵn sàng, bạn chuyển sang:
|
|
Các sự kiện thường gặp
Trong View, lắng nghe sự kiện từ Model
|
|
Trong View, lắng nghe sự kiện từ Collection
|
|
Sự kiện liên quan đến HTTP request (chưa test thành công)
|
|
Lắng nghe sự kiện trên các phần tử trong template
|
|
Tham khảo: Danh mục sự kiện - Tài liệu chính thức Backbone
Sự khác biệt giữa on
và listenTo
Về mặt cú pháp:
on
dùng để lắng nghe sự kiện trên chính object đó
object.on(event, callback, [context])
listenTo
dùng để lắng nghe sự kiện từ object khác
object.listenTo(other, event, callback)
Ứng dụng:
listenTo
phù hợp khi View cần lắng nghe sự kiện từ Model/Collectionon
phù hợp khi Model cần tự lắng nghe sự kiện thay đổi của chính nó
Ví dụ trong TodosView
, ta có thể thấy:
|
|
Nếu lắng nghe sự kiện bên trong Collection, thì:
|
|
Tham khảo: Lắng nghe sự kiện Collection add trong Backbone
Phân tách View
Không có hướng dẫn cụ thể nào nói đến cách tách View trong Backbone. Theo kinh nghiệm từ AngularJS, việc tách nhỏ View càng nhiều càng tốt để dễ viết unit test.
Dù là AngularJS hay BackboneJS, yếu tố then chốt để tăng năng suất và giảm chi phí bảo trì là việc chia nhỏ View/controller. Mỗi khu vực trên thiết kế giao diện nên được tách riêng thành một View/controller độc lập.
So với AngularJS, BackboneJS đòi hỏi bạn phải thủ công thêm các ràng buộc DOM, làm tăng chi phí đặt ID, class cho các phần tử – khá phiền toái. Hãy thử tìm cách đơn giản hơn nếu có thể.
Tất cả các View đều chịu trách nhiệm cho một lượng logic nhất định. Làm sao tổ chức những thứ này một cách hợp lý?
Để tổ chức tốt logic này, ta có thể áp dụng mô hình Element Controller Pattern. Mô hình này gồm hai View: một View điều khiển tập hợp các phần tử, và một View xử lý từng phần tử riêng lẻ.
Tôi gọi mô hình này là “mô hình nhà thầu tổng”, tức là một nhà thầu lớn điều phối các nhà thầu nhỏ, mỗi nhà thầu nhỏ lại điều phối các kỹ thuật viên chuyên môn. Trước đây tôi vẫn gọi nó là “mô hình tách View”, nhưng cảm thấy cái tên này chưa đủ sinh động để phản ánh đúng bản chất phân cấp, nên đổi thành “mô hình nhà thầu tổng”.
Ví dụ thực tế: AppView
là View ngoài cùng, gắn vào một phần tử trong file index thông qua el: {id}
, và bên trong AppView
lại chứa các View con với chức năng cụ thể.
- BACKBONE.VIEW PATTERNS – CÁCH VÀ LÝ DO SỬ DỤNG SUBVIEW
- Cách render và append subview trong Backbone.js
Một View muốn kích hoạt sự kiện render của View khác, có nên nghe sự kiện trực tiếp không?
Các giải pháp có thể:
- Nên nghe sự kiện từ Collection (xem phần Events)
- Sử dụng cơ chế publish-subscribe toàn cục
Unit Testing
Làm thế nào để tránh dùng biến global?
Sự khác biệt lớn giữa BackboneJS và AngularJS nằm ở chỗ tài liệu chính thức của BackboneJS không nhấn mạnh tầm quan trọng của unit testing, và không có cơ chế kiến trúc ép buộc bạn viết code dễ test.
- Kiểm thử ứng dụng Backbone với Jasmine và Sinon – Phần 1
Sử dụng SeaJS / RequireJS để modular hóa mã Frontend MVC
Khái niệm modular là gì?
Bạn có thể hiểu như các module tiêu chuẩn trong Python như datetime
, time
, os
, tức là mỗi chức năng được tách riêng thành một file JS.
Ưu điểm của modular so với cách viết JS truyền thống:
- Rõ ràng về mối phụ thuộc giữa các file JS. Ví dụ: một trang web có 2 file JS là
jQuery.js
vàforum.js
, tuy nhiên không thể nhìn vàoforum.js
để biết nó phụ thuộc vàojQuery.js
. - Tải về theo nhu cầu, không tải tất cả như cách truyền thống.
- Không cần lo lắng về thứ tự load các file JS, vì khi số lượng file tăng lên, việc quản lý thứ tự trở nên phức tạp.
Vì SeaJS do người Việt phát triển, tài liệu tiếng Anh còn hạn chế, nên tạm thời sử dụng RequireJS (gần như tất cả tutorial BackboneJS đều giới thiệu).
Vậy frontend MVC sử dụng RequireJS như thế nào?
Giống như trong Django, views.py
sẽ import models.py
, thì trong BackboneJS, View
cũng sẽ import Model
, và đương nhiên cũng dùng tới jQuery
, backbone
, underscore
.
Vấn đề khi dùng AJAX trong Backbone
Khi sử dụng this.<method_name>
trong success
, done
, hoặc error
của AJAX luôn báo lỗi, rằng phương thức đó không tồn tại. Lý do là this
trong scope nội tại của AJAX không còn là Backbone Object nữa. Giải pháp là khai báo một biến that
bên ngoài:
|
|
Trong các callback của
$.ajax
,this
sẽ là object được truyền vào optioncontext
. Nếu không có,this
sẽ là settings của AJAX.
- Vấn đề callback trong Backbone.Model và THIS
- Cách sử dụng this trong JavaScript
- 5 cách gọi hàm trong JavaScript
- Hiểu về _.each trong Backbone Collection
Tại sao Render trong View không được gọi chủ động?
TODO
Router và AppView ngoài cùng hoạt động như thế nào?
AppView
là View cấp cao nhất, là container chính của toàn bộ ứng dụng. Để hiểu rõ cách Router và AppView hoạt động song song, cần nắm rõ vai trò của từng thành phần.
Backbone là một framework rất linh hoạt, cùng một chức năng có thể có nhiều cách triển khai khác nhau. Với tôi, AppView
là nơi chứa namespace, các biến toàn cục dùng chung, và là gốc rễ của toàn bộ ứng dụng.
Giả sử ứng dụng có 2 View là HomeView
và AboutView
, với đường dẫn tương ứng là #home
và #about
. Mặc định sẽ vào HomeView
. Cả hai View này đều là con của AppView
.
AppView
cần lắng nghe sự kiện click
để quyết định chuyển đến View nào, sau đó gọi router.navigate()
để điều hướng. Do đó, việc đặt Router bên trong AppView
là hợp lý.
Vấn đề nảy sinh là: Trong handler của Router, mình cần làm gì?
Nếu đang khởi tạo HomeView
hoặc AboutView
, điều kiện tiên quyết là AppView
đã render xong. Điều này không khó, bạn có thể render AppView
trước rồi mới khởi tạo Router. Còn việc render HomeView
và AboutView
sẽ dựa vào một vị trí cụ thể trong AppView
.
Nếu HomeView
chứa 3 subview ngang hàng, thì việc điều hướng trong handler của Router sẽ phức tạp hơn. Tuy nhiên, không cần dùng sub-router để xử lý điều này. Thay vào đó, mỗi subview có thể tự kiểm tra xem HomeView
đã được khởi tạo chưa. Nếu chưa, thì khởi tạo. Quá trình này có thể thực hiện trong handler của Router. Đồng thời, HomeView
cần hỗ trợ truyền tham số để xác định subview mặc định, nhằm tránh hiện tượng “giật”.
Cách điều hướng từ subview sang subview khác:
|
|
- Sử dụng Backbone Router để điều hướng giữa các View được modular bằng RequireJS
- Subroute theo module trong Backbone
Sử dụng Mustache hay Underscore Template của Backbone?
Hiện tại mình chưa gặp vấn đề gì khi sử dụng Underscore Template tích hợp sẵn trong Backbone.
- Template trong Backbone View
Vô hiệu hóa hiệu ứng chuyển trang của link
Nếu sử dụng:
|
|
sẽ ảnh hưởng đến routing của Backbone. Thay vào đó, nên dùng:
|
|
Tài liệu
- Developing Backbone.js Applications
- Trang chủ Backbone.js
- Backbone Views Using Mustache Templates
- Gợi ý sự kiện giữa các View trong Backbone.js
- Tips và Patterns trong Backbone.js
- Web Framework Backbone.Marionette