Giới thiệu
Phát triển cho web dành cho thiết bị di động là một chủ đề nóng hổi hiện nay. Năm nay, lần đầu tiên, doanh số điện thoại thông minh vượt qua máy tính. Ngày càng có nhiều người dùng sử dụng thiết bị di động để duyệt web, tức là việc tối ưu hoá trang web cho trình duyệt di động đang trở nên quan trọng đối với nhà phát triển.
"Chiến trường" di động vẫn là vùng đất chưa được khai phá đối với nhiều nhà phát triển. Nhiều người có các trang web cũ hoàn toàn bỏ qua người dùng thiết bị di động. Thay vào đó, trang web được thiết kế chủ yếu để duyệt xem trên trình duyệt dành cho máy tính và không hoạt động tốt trên trình duyệt di động. Trang web này (html5rocks.com) cũng không ngoại lệ. Khi ra mắt, chúng tôi không đầu tư nhiều vào phiên bản trang web dành cho thiết bị di động.
Tạo trang html5rocks.com phù hợp với thiết bị di động
Để thực hành, tôi nghĩ sẽ rất thú vị nếu lấy html5rocks (một trang web HTML5 hiện có) và bổ sung một phiên bản phù hợp với thiết bị di động. Tôi chủ yếu quan tâm đến khối lượng công việc tối thiểu cần thiết để nhắm đến điện thoại thông minh. Mục tiêu của bài tập này không phải là tạo một trang web hoàn toàn mới cho thiết bị di động và duy trì hai cơ sở mã. Việc đó sẽ mất rất nhiều thời gian và lãng phí thời gian. Chúng ta đã xác định cấu trúc (mã đánh dấu) của trang web. Chúng tôi đã có giao diện (CSS). Chức năng cốt lõi (JS) đã có sẵn. Vấn đề là nhiều trang web cũng gặp phải tình trạng này.
Bài viết này tìm hiểu cách chúng tôi tạo phiên bản di động của html5rocks được tối ưu hoá cho thiết bị Android và thiết bị iOS. Chỉ cần tải html5rocks.com trên một thiết bị hỗ trợ một trong các hệ điều hành đó để thấy sự khác biệt. Không có lệnh chuyển hướng đến m.html5rocks.com hoặc các trò lừa đảo khác có tính chất tương tự. Bạn sẽ nhận được html5rocks như hiện tại… cùng với lợi ích bổ sung là một trang web trông đẹp mắt và hoạt động tốt trên thiết bị di động.
Truy vấn phương tiện CSS
HTML4 và CSS2 đã hỗ trợ biểu định kiểu phụ thuộc vào nội dung nghe nhìn trong một thời gian. Ví dụ:
<link rel="stylesheet" media="print" href="printer.css">
sẽ nhắm đến các thiết bị in và cung cấp kiểu cụ thể cho nội dung trang khi in. CSS3 tiến thêm một bước trong việc phát triển ý tưởng về các loại nội dung nghe nhìn và nâng cao chức năng của các loại nội dung nghe nhìn đó bằng truy vấn nội dung nghe nhìn. Truy vấn đa phương tiện mở rộng tính hữu ích của các loại nội dung đa phương tiện bằng cách cho phép gắn nhãn chính xác hơn cho các biểu định kiểu. Điều này cho phép tuỳ chỉnh cách trình bày nội dung cho một loạt thiết bị đầu ra cụ thể mà không cần thay đổi chính nội dung đó. Nghe có vẻ rất phù hợp với một bố cục hiện có cần sửa đổi!
Bạn có thể sử dụng truy vấn phương tiện trong thuộc tính media của biểu định kiểu bên ngoài để nhắm đến chiều rộng màn hình, chiều rộng thiết bị, hướng, v.v. Để xem danh sách đầy đủ, hãy xem quy cách truy vấn phương tiện W3C.
Nhắm mục tiêu theo kích thước màn hình
Trong ví dụ sau, phone.css sẽ áp dụng cho các thiết bị mà trình duyệt coi là "thiết bị cầm tay" hoặc thiết bị có màn hình rộng <= 320px.
<link rel='stylesheet'
media='handheld, only screen and (max-device-width: 320px)' href='phone.css'>
Việc thêm từ khoá "only" vào trước truy vấn phương tiện sẽ khiến các trình duyệt không tuân thủ CSS3 bỏ qua quy tắc này.
Đoạn mã sau đây sẽ nhắm đến kích thước màn hình từ 641px đến 800px:
<link rel='stylesheet'
media='only screen and (min-width: 641px) and (max-width: 800px)' href='ipad.css'>
Truy vấn phương tiện cũng có thể xuất hiện trong các thẻ <style> cùng dòng. Đoạn mã sau đây nhắm đến các loại nội dung nghe nhìn all khi ở hướng dọc:
<style>
@media only all and (orientation: portrait) { ... }
</style>
media="handheld"
Chúng ta cần dừng lại một chút và thảo luận về media="handheld".
Thực tế là Android và iOS bỏ qua media="handheld". Người ta cho rằng người dùng sẽ bỏ lỡ nội dung cao cấp do biểu định kiểu nhắm đến media="screen" cung cấp và nhà phát triển ít có khả năng duy trì phiên bản media="handheld" có chất lượng thấp hơn. Vì vậy, theo phương châm "web đầy đủ", hầu hết các trình duyệt điện thoại thông minh hiện đại chỉ đơn giản là bỏ qua các biểu định kiểu cầm tay.
Sử dụng tính năng này để nhắm đến thiết bị di động là lý tưởng nhất, nhưng nhiều trình duyệt đã triển khai tính năng này theo nhiều cách:
- Một số chỉ đọc biểu định kiểu cầm tay.
- Một số trình duyệt chỉ đọc biểu định kiểu cầm tay nếu có, nhưng mặc định là biểu định kiểu màn hình.
- Một số trình duyệt đọc cả biểu định kiểu cầm tay và biểu định kiểu màn hình.
- Một số trình đọc chỉ đọc biểu định kiểu màn hình.
Opera Mini không bỏ qua media="handheld". Mẹo để Windows Mobile nhận dạng media="handheld" là viết hoa giá trị thuộc tính nội dung nghe nhìn cho biểu định kiểu màn hình:
<!-- media="handheld" trick for Windows Mobile -->
<link rel="stylesheet" href="screen.css" media="Screen">
<link rel="stylesheet" href="mobile.css" media="handheld">
Cách html5rocks sử dụng truy vấn đa phương tiện
Truy vấn phương tiện được sử dụng rất nhiều trong html5rocks dành cho thiết bị di động. Chúng cho phép chúng tôi điều chỉnh bố cục mà không cần phải thực hiện những thay đổi đáng kể đối với mã đánh dấu mẫu Django… một giải pháp cứu cánh thực sự! Ngoài ra, khả năng hỗ trợ của các thuộc tính này trên nhiều trình duyệt là khá tốt.
Trong <head> của mỗi trang, bạn sẽ thấy các biểu định kiểu sau:
<link rel='stylesheet'
media='all' href='/static/css/base.min.css' />
<link rel='stylesheet'
media='only screen and (max-width: 800px)' href='/static/css/mobile.min.css' />
base.css luôn xác định giao diện chính của html5rocks.com, nhưng hiện tại, chúng tôi đang áp dụng các kiểu mới (mobile.css) cho chiều rộng màn hình dưới 800px. Truy vấn phương tiện của nó bao gồm điện thoại thông minh (~320px) và iPad (~768px).
Kết quả: chúng tôi đang dần ghi đè các kiểu trong base.css (chỉ khi cần thiết) để mọi thứ trông đẹp hơn trên thiết bị di động.
Một số thay đổi về kiểu mà mobile.css thực thi:
- Giảm khoảng trắng/khoảng đệm thừa trên trang web. Màn hình nhỏ đồng nghĩa với việc không gian là yếu tố quan trọng!
- Xoá trạng thái
:hover. Các chú giải công cụ này sẽ không bao giờ xuất hiện trên thiết bị cảm ứng. - Điều chỉnh bố cục thành một cột. Chúng ta sẽ nói thêm về điều này ở phần sau.
- Xoá
box-shadowxung quanh div vùng chứa chính của trang web. Bóng hộp lớn làm giảm hiệu suất trang. - Sử dụng mô hình hộp linh hoạt CSS
box-ordinal-groupđể thay đổi thứ tự của từng phần trên trang chủ. Bạn sẽ nhận thấy phần "LEARN BY MAJOR HTML5 FEATURE GROUPS" (TÌM HIỂU THEO CÁC NHÓM TÍNH NĂNG CHÍNH CỦA HTML5) xuất hiện trước phần "TUTORIALS" (HƯỚNG DẪN) trên trang chủ nhưng lại xuất hiện sau phần đó trên phiên bản di động. Thứ tự này hợp lý hơn cho thiết bị di động và không yêu cầu thay đổi mã đánh dấu. CSS flexbox FTW! - Xoá
opacitythay đổi. Việc thay đổi giá trị alpha sẽ ảnh hưởng đến hiệu suất trên thiết bị di động.
Thẻ meta dành cho thiết bị di động
WebKit dành cho thiết bị di động hỗ trợ một số tính năng hữu ích giúp người dùng có trải nghiệm duyệt web tốt hơn trên một số thiết bị.
Cài đặt khung nhìn
Chế độ cài đặt meta đầu tiên (và là chế độ bạn sẽ sử dụng thường xuyên nhất) là thuộc tính khung nhìn. Việc đặt khung nhìn sẽ cho trình duyệt biết cách nội dung hiển thị vừa vặn trên màn hình của thiết bị và thông báo cho trình duyệt rằng trang web đã được tối ưu hoá cho thiết bị di động. Ví dụ:
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
cho trình duyệt biết cách đặt khung nhìn theo chiều rộng của thiết bị với tỷ lệ ban đầu là 1. Ví dụ này cũng cho phép thu phóng, một tính năng có thể phù hợp với trang web nhưng không phù hợp với ứng dụng web. Chúng ta có thể ngăn việc thu phóng bằng user-scalable=no hoặc giới hạn tỷ lệ ở một mức nhất định:
<meta name=viewport
content="width=device-width, initial-scale=1.0, minimum-scale=0.5 maximum-scale=1.0">
Android mở rộng thẻ meta cửa sổ xem bằng cách cho phép nhà phát triển chỉ định độ phân giải màn hình mà trang web được phát triển:
<meta name="viewport" content="target-densitydpi=device-dpi">
Các giá trị có thể có cho target-densitydpi là device-dpi, high-dpi, medium-dpi, low-dpi.
Nếu bạn muốn sửa đổi trang web cho phù hợp với nhiều mật độ màn hình, hãy sử dụng truy vấn nội dung nghe nhìn CSS -webkit-device-pixel-ratio và/hoặc thuộc tính window.devicePixelRatio trong JavaScript, sau đó đặt thuộc tính meta target-densitydpi thành device-dpi. Thao tác này sẽ ngăn Android thực hiện việc điều chỉnh tỷ lệ trong trang web của bạn và cho phép bạn thực hiện các điều chỉnh cần thiết cho từng mật độ thông qua CSS và JavaScript.
Hãy xem tài liệu về WebView của Android để biết thêm thông tin về cách nhắm đến độ phân giải của thiết bị mục tiêu.
Duyệt xem toàn màn hình
Có hai giá trị meta khác là iOS-sfic. apple-mobile-web-app-capable và apple-mobile-web-app-status-bar-style sẽ hiển thị nội dung trang ở chế độ toàn màn hình giống như ứng dụng và làm cho thanh trạng thái mờ đi:
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
Để biết thêm thông tin về tất cả các tuỳ chọn meta có sẵn, hãy xem tài liệu tham khảo về Safari.
Biểu tượng trên màn hình chính
Thiết bị iOS và thiết bị Android cũng chấp nhận
rel="apple-touch-icon" (iOS) và rel="apple-touch-icon-precomposed" (Android)
cho đường liên kết. Các thẻ này tạo một biểu tượng bắt mắt giống như ứng dụng trên màn hình chính của người dùng khi họ đánh dấu trang web của bạn:
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
Cách html5rocks sử dụng thẻ meta dành cho thiết bị di động
Kết hợp tất cả lại với nhau, dưới đây là một đoạn mã từ phần <head> của html5rocks:
<head>
...
<meta name="viewport"
content="width=device-width, initial-scale=1.0, minimum-scale=1.0" />
<link rel="apple-touch-icon"
href="/static/images/identity/HTML5_Badge_64.png" />
<link rel="apple-touch-icon-precomposed"
href="/static/images/identity/HTML5_Badge_64.png" />
...
</head>
Bố cục dọc
Trên màn hình nhỏ, việc cuộn theo chiều dọc sẽ thuận tiện hơn nhiều so với cuộn theo chiều ngang. Bạn nên giữ nội dung trong bố cục dọc, một cột cho thiết bị di động. Đối với html5rocks, chúng tôi đã sử dụng truy vấn phương tiện CSS3 để tạo bố cục như vậy. Một lần nữa, không cần thay đổi mã đánh dấu.
Tối ưu hoá cho thiết bị di động
Hầu hết các hoạt động tối ưu hoá mà chúng tôi thực hiện đều là những việc đáng lẽ phải làm ngay từ đầu. Những việc như giảm số lượng yêu cầu mạng, nén JS/CSS, nén gzip (miễn phí trên App Engine) và giảm thiểu các thao tác DOM. Những kỹ thuật này là các phương pháp hay nhất phổ biến nhưng đôi khi bị bỏ qua khi vội vàng ra mắt trang web.
Tự động ẩn thanh địa chỉ
Trình duyệt di động không có không gian màn hình như trình duyệt trên máy tính. Tệ hơn nữa, trên các nền tảng khác nhau, đôi khi bạn sẽ thấy một thanh địa chỉ lớn ở đầu màn hình… ngay cả sau khi trang tải xong.
Một cách dễ dàng để xử lý vấn đề này là cuộn trang bằng JavaScript.
Ngay cả khi làm như vậy bằng một pixel, bạn cũng sẽ xử lý được thanh địa chỉ phiền toái.
Để buộc ẩn thanh địa chỉ trên html5rocks, tôi đã đính kèm một trình xử lý sự kiện onload vào đối tượng window và cuộn trang theo chiều dọc một pixel:
// Hides mobile browser's address bar when page is done loading.
window.addEventListener('load', function(e) {
setTimeout(function() { window.scrollTo(0, 1); }, 1);
}, false);
Chúng tôi cũng gói trình nghe này vào biến mẫu is_mobile vì không cần thiết trên máy tính.
Giảm yêu cầu mạng, tiết kiệm băng thông
Thực tế cho thấy việc giảm số lượng yêu cầu HTTP có thể cải thiện đáng kể hiệu suất. Thiết bị di động còn giới hạn số lượng kết nối đồng thời mà trình duyệt có thể thực hiện, vì vậy, các trang web dành cho thiết bị di động sẽ hưởng lợi nhiều hơn từ việc giảm các yêu cầu không liên quan này. Hơn nữa, việc giảm bớt từng byte là rất quan trọng vì băng thông thường bị giới hạn trên điện thoại. Bạn có thể đang khiến người dùng tốn tiền!
Sau đây là một số phương pháp chúng tôi đã áp dụng để giảm thiểu yêu cầu mạng và giảm băng thông trên html5rocks:
Xoá iframe – iframe rất chậm! Phần lớn độ trễ của chúng tôi là do các tiện ích chia sẻ của bên thứ ba (Buzz, Google Friend Connect, Twitter, Facebook) trên các trang hướng dẫn. Các API này được đưa vào thông qua thẻ
<script>và tạo ra các iframe làm giảm tốc độ của trang. Các tiện ích đã bị xoá khỏi thiết bị di động.display:none– Trong một số trường hợp, chúng tôi ẩn mã đánh dấu nếu mã đó không phù hợp với hồ sơ thiết bị di động. Một ví dụ điển hình là 4 hộp bo tròn ở đầu trang chủ:
Trang web dành cho thiết bị di động không có các thành phần này. Xin lưu ý rằng trình duyệt vẫn đưa ra yêu cầu cho từng biểu tượng, mặc dù vùng chứa của chúng bị ẩn bằng display:none. Do đó, chỉ ẩn các nút này là chưa đủ. Điều này không chỉ gây lãng phí băng thông mà người dùng thậm chí còn không thấy được kết quả của việc lãng phí băng thông đó! Giải pháp là tạo một boolean "is_mobile" trong mẫu Django để bỏ qua các phần HTML theo điều kiện.
Khi người dùng xem trang web trên thiết bị thông minh, các nút này sẽ bị bỏ qua.
Bộ nhớ đệm ứng dụng – Không chỉ hỗ trợ chế độ ngoại tuyến, bộ nhớ đệm ứng dụng còn giúp khởi động nhanh hơn.
Nén CSS/JS – chúng tôi đang sử dụng trình nén YUI thay vì trình biên dịch Closure chủ yếu vì trình nén này xử lý cả CSS và JS. Một vấn đề mà chúng tôi gặp phải là các truy vấn nội tuyến về nội dung nghe nhìn (các truy vấn về nội dung nghe nhìn xuất hiện bên trong một biểu định kiểu) bị lỗi trong trình nén YUI 2.4.2 (xem vấn đề này). Sử dụng YUI Compressor 2.4.4 trở lên để khắc phục vấn đề.
Sử dụng hình ảnh CSS sprite nếu có thể.
Sử dụng pngcrush để nén hình ảnh.
Sử dụng dataURI cho hình ảnh nhỏ. Mã hoá Base64 làm tăng khoảng 30%kích thước của hình ảnh nhưng giúp tiết kiệm yêu cầu mạng.
Tự động tải Tìm kiếm tuỳ chỉnh của Google bằng một thẻ tập lệnh duy nhất thay vì tải linh động bằng
google.load(). Cách sau sẽ tạo thêm một yêu cầu.
<script src="//www.google.com/jsapi?autoload={"modules":[{"name":"search","version":"1"}]}"> </script>
- Trình định dạng mã và Modernizr của chúng tôi được đưa vào mọi trang, ngay cả khi không bao giờ được sử dụng. Modernizr rất tuyệt, nhưng nó chạy một loạt các thử nghiệm trên mỗi lần tải. Một số thử nghiệm trong số đó thực hiện các sửa đổi tốn kém đối với DOM và làm chậm quá trình tải trang. Giờ đây, chúng tôi chỉ đưa các thư viện này vào những trang thực sự cần đến chúng. -2 yêu cầu :)
Những điểm điều chỉnh khác về hiệu suất:
- Di chuyển tất cả JS xuống cuối trang (nếu có thể).
- Đã xoá các thẻ
<style>cùng dòng. - Lưu vào bộ nhớ đệm các lượt tra cứu DOM và giảm thiểu các thao tác DOM – Mỗi khi bạn chạm vào DOM, trình duyệt sẽ thực hiện một thao tác chỉnh lại luồng. Quá trình dàn lại bố cục thậm chí còn tốn kém hơn trên thiết bị di động.
- Chuyển mã phía máy khách không cần thiết sang máy chủ. Cụ thể, hãy kiểm tra để xem cách đặt kiểu điều hướng của trang hiện tại:
js var lis = document.querySelectorAll('header nav li'); var i = lis.length; while (i--) { var a = lis[i].querySelector('a'); var section = a.getAttribute("data-section"); if (new RegExp(section).test(document.location.href)) { a.className = 'current'; } } - Các phần tử có chiều rộng cố định đã được thay thế bằng
width:100%hoặcwidth:autolinh hoạt.
Bộ nhớ đệm của ứng dụng
Phiên bản di động của html5rocks sử dụng Bộ nhớ đệm ứng dụng để tăng tốc độ tải ban đầu và cho phép người dùng đọc nội dung khi không có mạng.
Khi triển khai AppCache trên trang web, bạn cần lưu ý không lưu tệp kê khai vào bộ nhớ đệm (dù là lưu rõ ràng trong chính tệp kê khai hay lưu ngầm bằng các tiêu đề kiểm soát bộ nhớ đệm nặng). Nếu tệp kê khai của bạn được trình duyệt lưu vào bộ nhớ đệm, thì việc gỡ lỗi sẽ rất khó khăn. iOS và Android lưu tệp này vào bộ nhớ đệm rất hiệu quả nhưng không cung cấp cách thuận tiện để xoá bộ nhớ đệm như trình duyệt dành cho máy tính.
Để ngăn việc lưu vào bộ nhớ đệm cho trang web của mình, trước tiên, chúng tôi đặt App Engine không bao giờ lưu tệp kê khai vào bộ nhớ đệm:
- url: /(.*\.(appcache|manifest))
static_files: \1
mime_type: text/cache-manifest
upload: (.*\.(appcache|manifest))
expiration: "0s"
Thứ hai, chúng tôi đã sử dụng JS API để thông báo cho người dùng khi tệp kê khai mới tải xuống xong. Họ sẽ được nhắc làm mới trang:
window.applicationCache.addEventListener('updateready', function(e) {
if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
window.applicationCache.swapCache();
if (confirm('A new version of this site is available. Load it?')) {
window.location.reload();
}
}
}, false);
Để tiết kiệm lưu lượng truy cập mạng, hãy giữ cho tệp kê khai của bạn đơn giản. Nghĩa là, đừng gọi ra mọi trang trên trang web của bạn. Bạn chỉ cần liệt kê các tệp hình ảnh, CSS và JavaScript quan trọng. Điều cuối cùng bạn muốn làm là buộc trình duyệt di động
tải một số lượng lớn tài sản xuống mỗi khi cập nhật bộ nhớ đệm ứng dụng. Thay vào đó, hãy nhớ rằng trình duyệt sẽ ngầm lưu trang HTML vào bộ nhớ đệm khi người dùng truy cập (và trang đó có thuộc tính <html manifest="...">).