Phương pháp không thích ứng để xây dựng ứng dụng web trên nhiều thiết bị

Truy vấn phương tiện rất tuyệt, nhưng…

Truy vấn nội dung đa phương tiện là một tính năng tuyệt vời, một món quà trời cho dành cho các nhà phát triển trang web muốn thực hiện một số điều chỉnh nhỏ đối với các tệp kiểu để mang lại trải nghiệm tốt hơn cho người dùng trên các thiết bị có nhiều kích thước. Về cơ bản, truy vấn phương tiện cho phép bạn tùy chỉnh CSS của trang web tuỳ theo kích thước màn hình. Trước khi đi sâu vào bài viết này, hãy tìm hiểu thêm về thiết kế thích ứng và xem một số ví dụ điển hình về cách sử dụng truy vấn nội dung nghe nhìn tại đây: mediaqueri.es.

Như Brad Frost đã chỉ ra trong một bài viết trước, việc thay đổi giao diện chỉ là một trong nhiều điều cần cân nhắc khi xây dựng cho web dành cho thiết bị di động. Nếu việc duy nhất bạn làm khi tạo trang web dành cho thiết bị di động là tuỳ chỉnh bố cục bằng truy vấn nội dung đa phương tiện, thì chúng ta sẽ có trường hợp sau:

  • Tất cả các thiết bị đều nhận được cùng một JavaScript, CSS và các thành phần (hình ảnh, video), dẫn đến thời gian tải lâu hơn mức cần thiết.
  • Tất cả thiết bị đều nhận được cùng một DOM ban đầu, có thể buộc nhà phát triển phải viết CSS quá phức tạp.
  • Không linh hoạt trong việc chỉ định các hoạt động tương tác tuỳ chỉnh phù hợp với từng thiết bị.

Ứng dụng web cần nhiều hơn truy vấn nội dung đa phương tiện

Đừng hiểu lầm ý tôi. Tôi không ghét thiết kế thích ứng thông qua truy vấn nội dung đa phương tiện và chắc chắn rằng thiết kế này có chỗ đứng trong thế giới. Hơn nữa, một số vấn đề được đề cập ở trên có thể được giải quyết bằng các phương pháp như hình ảnh thích ứng, tải tập lệnh động, v.v. Tuy nhiên, tại một thời điểm nhất định, bạn có thể thấy mình thực hiện quá nhiều điều chỉnh gia tăng và tốt hơn hết là phân phát nhiều phiên bản.

Khi giao diện người dùng mà bạn xây dựng ngày càng phức tạp và bạn chuyển hướng sang các ứng dụng web một trang, bạn sẽ muốn làm nhiều việc hơn để tuỳ chỉnh giao diện người dùng cho từng loại thiết bị. Bài viết này sẽ hướng dẫn bạn cách thực hiện các tuỳ chỉnh này với ít nỗ lực nhất. Phương pháp chung là phân loại thiết bị của khách truy cập vào lớp thiết bị phù hợp và phân phát phiên bản thích hợp cho thiết bị đó, đồng thời tối đa hoá việc sử dụng lại mã giữa các phiên bản.

Bạn đang nhắm đến những lớp thiết bị nào?

Có rất nhiều thiết bị kết nối Internet và gần như tất cả thiết bị đều có trình duyệt. Sự phức tạp nằm ở sự đa dạng của các thiết bị: máy tính xách tay Mac, máy trạm Windows, iPhone, iPad, điện thoại Android có phương thức nhập bằng cảm ứng, con lăn cuộn, bàn phím, phương thức nhập bằng giọng nói, thiết bị có độ nhạy áp lực, đồng hồ thông minh, máy nướng bánh mì và tủ lạnh, v.v. Một số thiết bị trong số này phổ biến, trong khi một số thiết bị khác rất hiếm.

Nhiều loại thiết bị.
Nhiều thiết bị (source).

Để tạo ra trải nghiệm tốt cho người dùng, bạn cần biết người dùng của mình là ai và họ đang sử dụng thiết bị nào. Nếu bạn tạo giao diện người dùng cho người dùng máy tính có chuột và bàn phím rồi cung cấp giao diện đó cho người dùng điện thoại thông minh, thì giao diện của bạn sẽ gây khó chịu vì được thiết kế cho kích thước màn hình và phương thức nhập khác.

Có hai thái cực trong phổ các phương pháp:

  1. Xây dựng một phiên bản hoạt động trên tất cả thiết bị. Do đó, trải nghiệm người dùng sẽ bị ảnh hưởng vì các thiết bị khác nhau có những điểm cần cân nhắc về thiết kế khác nhau.

  2. Tạo một phiên bản cho mỗi thiết bị mà bạn muốn hỗ trợ. Quá trình này sẽ diễn ra mãi mãi vì bạn sẽ tạo quá nhiều phiên bản ứng dụng. Ngoài ra, khi điện thoại thông minh mới tiếp theo ra mắt (việc này diễn ra gần như hằng tuần), bạn sẽ buộc phải tạo thêm một phiên bản khác.

Có một sự đánh đổi cơ bản ở đây: bạn càng có nhiều danh mục thiết bị thì bạn càng có thể mang lại trải nghiệm tốt hơn cho người dùng, nhưng bạn càng phải làm nhiều việc để thiết kế, triển khai và duy trì.

Bạn nên tạo một phiên bản riêng cho từng lớp thiết bị mà bạn quyết định vì lý do hiệu suất hoặc nếu các phiên bản bạn muốn phân phát cho các lớp thiết bị khác nhau có sự khác biệt lớn. Nếu không, thiết kế web thích ứng là một phương pháp hoàn toàn hợp lý.

Giải pháp tiềm năng

Sau đây là một giải pháp dung hoà: phân loại thiết bị thành các danh mục và thiết kế trải nghiệm tốt nhất có thể cho từng danh mục. Danh mục bạn chọn phụ thuộc vào sản phẩm và người dùng mục tiêu. Dưới đây là một cách phân loại mẫu rất phù hợp với các thiết bị có khả năng truy cập web phổ biến hiện nay.

  1. màn hình nhỏ + cảm ứng (chủ yếu là điện thoại)
  2. màn hình lớn + cảm ứng (chủ yếu là máy tính bảng)
  3. màn hình lớn + bàn phím/chuột (chủ yếu là máy tính để bàn/máy tính xách tay)

Đây chỉ là một trong nhiều bảng chi tiết có thể có, nhưng là một bảng chi tiết rất hợp lý tại thời điểm viết bài. Danh sách trên thiếu các thiết bị di động không có màn hình cảm ứng (ví dụ: điện thoại đa năng, một số trình đọc sách điện tử chuyên dụng). Tuy nhiên, hầu hết các thiết bị này đều được cài đặt phần mềm điều hướng bằng bàn phím hoặc trình đọc màn hình. Các thiết bị này sẽ hoạt động tốt nếu bạn xây dựng trang web của mình với tính năng hỗ trợ tiếp cận.

Ví dụ về ứng dụng web dành riêng cho kiểu dáng

Có nhiều ví dụ về các tài sản web phân phát các phiên bản hoàn toàn khác nhau cho các kiểu dáng khác nhau. Google Tìm kiếm cũng như Facebook đều làm việc này. Những điều cần cân nhắc cho việc này bao gồm cả hiệu suất (tìm nạp tài sản, hiển thị trang) và trải nghiệm người dùng chung hơn.

Trong thế giới ứng dụng gốc, nhiều nhà phát triển chọn điều chỉnh trải nghiệm của họ cho một lớp thiết bị. Ví dụ: Flipboard dành cho iPad có giao diện người dùng rất khác so với Flipboard trên iPhone. Phiên bản máy tính bảng được tối ưu hoá để sử dụng bằng hai tay và lật theo chiều ngang, trong khi phiên bản điện thoại dành cho hoạt động tương tác bằng một tay và lật theo chiều dọc. Nhiều ứng dụng iOS khác cũng cung cấp phiên bản dành cho điện thoại và máy tính bảng khác nhau đáng kể, chẳng hạn như Things (danh sách việc cần làm) và Showyou (video xã hội), được giới thiệu dưới đây:

Tuỳ chỉnh giao diện người dùng đáng kể cho điện thoại và máy tính bảng.
Tính năng tuỳ chỉnh giao diện người dùng đáng kể cho điện thoại và máy tính bảng.

Phương pháp 1: Phát hiện phía máy chủ

Trên máy chủ, chúng ta hiểu biết hạn chế hơn nhiều về thiết bị mà chúng ta đang xử lý. Có lẽ gợi ý hữu ích nhất hiện có là chuỗi tác nhân người dùng, được cung cấp thông qua tiêu đề User-Agent trên mọi yêu cầu. Do đó, phương pháp phát hiện UA tương tự sẽ hoạt động ở đây. Trên thực tế, các dự án DeviceAtlas và WURFL đã thực hiện việc này (và cung cấp rất nhiều thông tin bổ sung về thiết bị).

Tuy nhiên, mỗi phương pháp đều có những thách thức riêng. WURFL rất lớn, chứa 20 MB XML, có thể gây ra hao tổn đáng kể ở phía máy chủ cho mỗi yêu cầu. Có những dự án phân tách XML vì lý do hiệu suất. DeviceAtlas không phải là nguồn mở và bạn cần có giấy phép trả phí để sử dụng.

Ngoài ra, còn có các giải pháp thay thế miễn phí và đơn giản hơn, chẳng hạn như dự án Phát hiện trình duyệt điện thoại di động. Tất nhiên, hạn chế là việc phát hiện thiết bị sẽ không thể toàn diện. Ngoài ra, lớp này chỉ phân biệt giữa thiết bị di động và thiết bị không phải di động, chỉ hỗ trợ máy tính bảng ở mức hạn chế thông qua một nhóm các bản sửa đổi đặc biệt.

Phương pháp 2: Phát hiện phía máy khách

Chúng ta có thể tìm hiểu nhiều thông tin về trình duyệt và thiết bị của người dùng bằng cách sử dụng tính năng phát hiện tính năng. Những điều chính chúng ta cần xác định là liệu thiết bị có chức năng cảm ứng hay không và màn hình lớn hay nhỏ.

Chúng ta cần vẽ một đường ở đâu đó để phân biệt các thiết bị cảm ứng nhỏ và lớn. Còn các trường hợp đặc biệt như Galaxy Note 5 inch thì sao? Hình ảnh sau đây cho thấy một loạt thiết bị Android và iOS phổ biến được xếp chồng lên nhau (với độ phân giải màn hình tương ứng). Dấu hoa thị cho biết thiết bị có hoặc có thể có mật độ gấp đôi. Mặc dù mật độ điểm ảnh có thể tăng gấp đôi, nhưng CSS vẫn báo cáo các kích thước như nhau.

Một điểm cần lưu ý nhanh về pixel trong CSS: pixel CSS trên web dành cho thiết bị di động không giống với pixel màn hình. Các thiết bị iOS retina đã giới thiệu phương pháp tăng gấp đôi mật độ pixel (ví dụ: iPhone 3GS so với 4, iPad 2 so với 3). UA Safari dành cho thiết bị di động có màn hình Retina vẫn báo cáo cùng một chiều rộng thiết bị để tránh làm hỏng trang web. Như các thiết bị khác (ví dụ: Android) có màn hình có độ phân giải cao hơn, chúng cũng đang thực hiện cùng một thủ thuật về chiều rộng thiết bị.

Độ phân giải của thiết bị (tính bằng pixel).
Độ phân giải của thiết bị (tính bằng pixel).

Tuy nhiên, quyết định này sẽ phức tạp hơn khi bạn cần cân nhắc cả chế độ dọc và ngang. Chúng ta không muốn tải lại trang hoặc tải các tập lệnh bổ sung mỗi khi định hướng lại thiết bị, mặc dù chúng ta có thể muốn hiển thị trang theo cách khác.

Trong sơ đồ sau, các hình vuông đại diện cho kích thước tối đa của từng thiết bị, là kết quả của việc phủ lên đường viền dọc và ngang (và hoàn thành hình vuông):

Độ phân giải dọc + ngang (tính bằng pixel)
Độ phân giải dọc + ngang (tính bằng pixel)

Bằng cách đặt ngưỡng thành 650px, chúng ta phân loại iPhone, Galaxy Nexus là thiết bị cảm ứng nhỏ và iPad, Galaxy Tab là "máy tính bảng". Trong trường hợp này, Galaxy Note vừa là điện thoại vừa là máy tính bảng được phân loại là "điện thoại" và sẽ có bố cục điện thoại.

Do đó, một chiến lược hợp lý có thể như sau:

if (hasTouch) {
  if (isSmall) {
    device = PHONE;
  } else {
    device = TABLET;
  }
} else {
  device = DESKTOP;
}

Xem một mẫu tối thiểu của phương pháp phát hiện tính năng đang hoạt động.

Phương pháp thay thế ở đây là sử dụng tính năng phát hiện tác nhân người dùng để phát hiện loại thiết bị. Về cơ bản, bạn tạo một tập hợp các phương pháp phỏng đoán và so khớp các phương pháp đó với navigator.userAgent của người dùng. Mã giả sẽ có dạng như sau:

var ua = navigator.userAgent;
for (var re in RULES) {
  if (ua.match(re)) {
    device = RULES[re];
    return;
  }
}

Xem ví dụ về phương pháp phát hiện UA đang hoạt động.

Lưu ý về việc tải phía máy khách

Nếu đang phát hiện UA trên máy chủ, bạn có thể quyết định phân phát CSS, JavaScript và DOM nào khi nhận được yêu cầu mới. Tuy nhiên, nếu bạn đang phát hiện ở phía máy khách, tình huống sẽ phức tạp hơn. Bạn có một số lựa chọn:

  1. Chuyển hướng đến một URL dành riêng cho loại thiết bị chứa phiên bản dành cho loại thiết bị này.
  2. Tải động các thành phần dành riêng cho loại thiết bị.

Phương pháp đầu tiên rất đơn giản, yêu cầu một lệnh chuyển hướng như window.location.href = '/tablet'. Tuy nhiên, vị trí hiện sẽ có thêm thông tin về loại thiết bị này. Vì vậy, bạn nên sử dụng History API để dọn dẹp URL. Rất tiếc, phương pháp này liên quan đến một lệnh chuyển hướng có thể bị chậm, đặc biệt là trên thiết bị di động.

Phương pháp thứ hai phức tạp hơn một chút để triển khai. Bạn cần một cơ chế để tải CSS và JS một cách linh động và (tuỳ thuộc vào trình duyệt), bạn có thể không thể thực hiện các thao tác như tuỳ chỉnh <meta viewport>. Ngoài ra, vì không có lệnh chuyển hướng nên bạn sẽ bị mắc kẹt với HTML ban đầu được phân phát. Tất nhiên, bạn có thể thao tác với nó bằng JavaScript, nhưng việc này có thể chậm và/hoặc không tinh tế, tuỳ thuộc vào ứng dụng của bạn.

Quyết định máy khách hoặc máy chủ

Sau đây là những điểm cần cân nhắc giữa các phương pháp:

Ứng dụng Pro:

  • Có khả năng thích ứng với tương lai hơn vì dựa trên kích thước/chức năng màn hình thay vì UA.
  • Không cần phải liên tục cập nhật danh sách UA.

Máy chủ Pro:

  • Toàn quyền kiểm soát phiên bản nào sẽ phân phát cho thiết bị nào.
  • Hiệu suất tốt hơn: không cần chuyển hướng ứng dụng hoặc tải động.

Theo ý kiến cá nhân, tôi muốn bắt đầu với device.js và tính năng phát hiện ở phía máy khách. Khi ứng dụng phát triển, nếu thấy lệnh chuyển hướng phía máy khách là một điểm yếu đáng kể về hiệu suất, bạn có thể dễ dàng xoá tập lệnh device.js và triển khai tính năng phát hiện UA trên máy chủ.

Giới thiệu device.js

Device.js là điểm xuất phát để phát hiện thiết bị dựa trên truy vấn nội dung đa phương tiện, ngữ nghĩa mà không cần cấu hình đặc biệt phía máy chủ, giúp tiết kiệm thời gian và công sức cần thiết để phân tích cú pháp chuỗi tác nhân người dùng.

Ý tưởng là bạn cung cấp mã đánh dấu thân thiện với công cụ tìm kiếm (link rel=alternate) ở đầu <head>, cho biết bạn muốn cung cấp phiên bản trang web nào.

<link rel="alternate" href="http://foo.com" id="desktop"
    media="only screen and (touch-enabled: 0)">

Tiếp theo, bạn có thể tự phát hiện UA phía máy chủ và xử lý lệnh chuyển hướng phiên bản hoặc sử dụng tập lệnh device.js để thực hiện lệnh chuyển hướng phía máy khách dựa trên tính năng.

Để biết thêm thông tin, hãy xem trang dự án device.js và một ứng dụng giả mạo sử dụng device.js để chuyển hướng phía máy khách.

Đề xuất: MVC với các thành phần hiển thị dành riêng cho kiểu dáng

Giờ đây, có thể bạn đang nghĩ rằng tôi đang yêu cầu bạn tạo ba ứng dụng hoàn toàn riêng biệt, mỗi ứng dụng cho một loại thiết bị. Không! Chia sẻ mã là yếu tố then chốt.

Hy vọng bạn đã sử dụng một khung giống MVC, chẳng hạn như Backbone, Ember, v.v. Nếu đã sử dụng, bạn sẽ quen thuộc với nguyên tắc phân tách các mối quan tâm, cụ thể là giao diện người dùng (lớp thành phần hiển thị) phải được tách khỏi logic (lớp mô hình). Nếu bạn chưa biết đến MVC, hãy bắt đầu bằng một số tài nguyên về MVCMVC trong JavaScript.

Câu chuyện trên nhiều thiết bị phù hợp với khung MVC hiện có của bạn. Bạn có thể dễ dàng di chuyển các thành phần hiển thị vào các tệp riêng biệt, tạo một thành phần hiển thị tuỳ chỉnh cho từng loại thiết bị. Sau đó, bạn có thể phân phát cùng một mã cho tất cả thiết bị, ngoại trừ lớp thành phần hiển thị.

MVC trên nhiều thiết bị.
MVC trên nhiều thiết bị.

Dự án của bạn có thể có cấu trúc như sau (tất nhiên, bạn có thể chọn cấu trúc phù hợp nhất tuỳ thuộc vào ứng dụng của mình):

models/ (mô hình dùng chung) item.js item-collection.js

controllers/ (shared controllers) item-controller.js

versions/ (nội dung dành riêng cho thiết bị) tablet/ desktop/ phone/ (mã dành riêng cho điện thoại) style.css index.html views/ item.js item-list.js

Loại cấu trúc này cho phép bạn kiểm soát toàn bộ thành phần mà mỗi phiên bản tải, vì bạn có HTML, CSS và JavaScript tuỳ chỉnh cho từng thiết bị. Đây là một tính năng rất mạnh mẽ và có thể dẫn đến cách phát triển hiệu quả nhất, gọn gàng nhất cho web trên nhiều thiết bị mà không cần dựa vào các thủ thuật như hình ảnh thích ứng.

Sau khi chạy công cụ xây dựng yêu thích, bạn sẽ nối và rút gọn tất cả các tệp JavaScript và CSS thành các tệp đơn để tải nhanh hơn, với HTML sản xuất có dạng như sau (đối với điện thoại, sử dụng device.js):

<!doctype html>
<head>
  <title>Mobile Web Rocks! (Phone Edition)</title>

  <!-- Every version of your webapp should include a list of all
        versions. -->
  <link rel="alternate" href="http://foo.com" id="desktop"
      media="only screen and (touch-enabled: 0)">
  <link rel="alternate" href="http://m.foo.com" id="phone"
      media="only screen and (max-device-width: 650px)">
  <link rel="alternate" href="http://tablet.foo.com" id="tablet"
      media="only screen and (min-device-width: 650px)">

  <!-- Viewport is very important, since it affects results of media
        query matching. -->
  <meta name="viewport" content="width=device-width">

  <!-- Include device.js in each version for redirection. -->
  <script src="device.js"></script>

  <link rel="style" href="phone.min.css">
</head>
<body>
  <script src="phone.min.js"></script>
</body>

Xin lưu ý rằng truy vấn nội dung đa phương tiện (touch-enabled: 0) không theo tiêu chuẩn (chỉ được triển khai trong Firefox sau tiền tố nhà cung cấp moz), nhưng được device.js xử lý chính xác (nhờ Modernizr.touch).

Ghi đè phiên bản

Đôi khi, tính năng phát hiện thiết bị có thể không hoạt động đúng cách và trong một số trường hợp, người dùng có thể muốn xem bố cục dành cho máy tính bảng trên điện thoại (có thể họ đang sử dụng Galaxy Note). Vì vậy, điều quan trọng là bạn phải cho phép người dùng chọn phiên bản trang web nào để sử dụng nếu họ muốn ghi đè theo cách thủ công.

Phương pháp thông thường là cung cấp đường liên kết đến phiên bản dành cho máy tính từ phiên bản dành cho thiết bị di động. Việc này khá dễ triển khai, nhưng device.js hỗ trợ chức năng này bằng tham số GET device.

Kết luận

Tóm lại, khi xây dựng giao diện người dùng một trang trên nhiều thiết bị, không phù hợp với thiết kế thích ứng, hãy làm như sau:

  1. Chọn một nhóm các lớp thiết bị để hỗ trợ và tiêu chí để phân loại thiết bị thành các lớp.
  2. Xây dựng ứng dụng MVC với khả năng phân tách mạnh mẽ các mối quan tâm, tách các thành phần hiển thị khỏi phần còn lại của cơ sở mã.
  3. Sử dụng device.js để phát hiện lớp thiết bị phía máy khách.
  4. Khi bạn đã sẵn sàng, hãy đóng gói tập lệnh và các tệp kiểu vào một trong các lớp thiết bị.
  5. Nếu hiệu suất chuyển hướng phía máy khách gặp vấn đề, hãy bỏ qua device.js và chuyển sang phát hiện UA phía máy chủ.