Teknik HTML5 untuk mengoptimalkan performa seluler

Pengantar

Pemuatan ulang yang berputar, transisi halaman yang tidak lancar, dan penundaan berkala dalam peristiwa ketuk hanyalah beberapa masalah yang dihadapi di lingkungan web seluler saat ini. Developer berupaya menghadirkan semirip mungkin dengan aplikasi native, tetapi sering kali terhambat oleh peretasan, reset, dan framework yang kaku.

Dalam artikel ini, kita akan membahas hal-hal minimum yang diperlukan untuk membuat aplikasi web HTML5 seluler. Poin utamanya adalah untuk mengungkap kompleksitas tersembunyi yang coba disembunyikan oleh framework seluler saat ini. Anda akan melihat pendekatan minimalis (menggunakan API HTML5 inti) dan dasar-dasar dasar yang akan memungkinkan Anda menulis framework sendiri atau berkontribusi pada framework yang saat ini Anda gunakan.

Akselerasi hardware

Biasanya, GPU menangani diagram CAD atau pemodelan 3D yang mendetail, tetapi dalam hal ini, kita ingin gambar primitif (div, latar belakang, teks dengan drop shadow, gambar, dll.) terlihat halus dan menganimasikan dengan lancar melalui GPU. Yang disayangkan adalah sebagian besar developer frontend menyerahkan proses animasi ini ke framework pihak ketiga tanpa memperhatikan semantik, tetapi apakah fitur CSS3 inti ini harus disamarkan? Mari kita bahas beberapa alasan mengapa hal ini penting:

  1. Alokasi memori dan beban komputasi — Jika Anda mengomposisi setiap elemen di DOM hanya demi akselerasi hardware, orang berikutnya yang mengerjakan kode Anda mungkin akan mengejar dan memukul Anda dengan keras.

  2. Konsumsi Daya — Tentu saja, saat hardware aktif, baterai juga akan aktif. Saat mengembangkan untuk perangkat seluler, developer terpaksa mempertimbangkan berbagai batasan perangkat saat menulis aplikasi web seluler. Hal ini akan semakin umum karena pembuat browser mulai mengaktifkan akses ke lebih banyak hardware perangkat.

  3. Konflik — Saya mengalami perilaku error saat menerapkan akselerasi hardware ke bagian halaman yang sudah diakselerasi. Jadi, mengetahui apakah Anda memiliki akselerasi yang tumpang-tindih sangat penting.

Agar interaksi pengguna berjalan lancar dan sedekat mungkin dengan native, kita harus membuat browser berfungsi untuk kita. Idealnya, kita ingin CPU perangkat seluler menyiapkan animasi awal, lalu meminta GPU hanya bertanggung jawab untuk mengomposisi berbagai lapisan selama proses animasi. Inilah yang dilakukan translate3d, scale3d, dan translateZ — ketiganya memberi elemen animasi lapisannya sendiri, sehingga memungkinkan perangkat merender semuanya dengan lancar. Untuk mengetahui lebih lanjut tentang komposisi yang dipercepat dan cara kerja WebKit, Ariya Hidayat memiliki banyak info bagus di blognya.

Transisi halaman

Mari kita lihat tiga pendekatan interaksi pengguna yang paling umum saat mengembangkan aplikasi web seluler: efek geser, putar, dan rotasi.

Anda dapat melihat cara kerja kode ini di sini http://slidfast.appspot.com/slide-flip-rotate.html (Catatan: Demo ini dibuat untuk perangkat seluler, jadi aktifkan emulator, gunakan ponsel atau tablet, atau kurangi ukuran jendela browser Anda menjadi ~1024 piksel atau kurang).

Pertama, kita akan menguraikan transisi geser, putar, dan rotasi serta cara transisi tersebut dipercepat. Perhatikan bagaimana setiap animasi hanya membutuhkan tiga atau empat baris CSS dan JavaScript.

Geser

Dari ketiga pendekatan transisi, transisi halaman geser adalah yang paling umum dan meniru nuansa native aplikasi seluler. Transisi slide dipanggil untuk memasukkan area konten baru ke port tampilan.

Untuk efek geser, pertama-tama kami mendeklarasikan markup:

<div id="home-page" class="page">
  <h1>Home Page</h1>
</div>

<div id="products-page" class="page stage-right">
  <h1>Products Page</h1>
</div>

<div id="about-page" class="page stage-left">
  <h1>About Page</h1>
</div>

Perhatikan bagaimana kita memiliki konsep staging halaman di sebelah kiri atau kanan. Pada dasarnya bisa ke segala arah, tetapi ini yang paling umum.

Kita sekarang memiliki animasi plus akselerasi hardware hanya dengan beberapa baris CSS. Animasi yang sebenarnya terjadi saat kita menukar class pada elemen div halaman.

.page {
  position: absolute;
  width: 100%;
  height: 100%;
  /*activate the GPU for compositing each page */
  -webkit-transform: translate3d(0, 0, 0);
}

translate3d(0,0,0) dikenal sebagai pendekatan "peluru perak".

Saat pengguna mengklik elemen navigasi, kita akan mengeksekusi JavaScript berikut untuk menukar class. Tidak ada framework pihak ketiga yang digunakan, ini adalah JavaScript murni. ;)

function getElement(id) {
  return document.getElementById(id);
}

function slideTo(id) {
  //1.) the page we are bringing into focus dictates how
  // the current page will exit. So let's see what classes
  // our incoming page is using. We know it will have stage[right|left|etc...]
  var classes = getElement(id).className.split(' ');

  //2.) decide if the incoming page is assigned to right or left
  // (-1 if no match)
  var stageType = classes.indexOf('stage-left');

  //3.) on initial page load focusPage is null, so we need
  // to set the default page which we're currently seeing.
  if (FOCUS_PAGE == null) {
    // use home page
    FOCUS_PAGE = getElement('home-page');
  }

  //4.) decide how this focused page should exit.
  if (stageType > 0) {
    FOCUS_PAGE.className = 'page transition stage-right';
  } else {
    FOCUS_PAGE.className = 'page transition stage-left';
  }

  //5. refresh/set the global variable
  FOCUS_PAGE = getElement(id);

  //6. Bring in the new page.
  FOCUS_PAGE.className = 'page transition stage-center';
}

stage-left atau stage-right menjadi stage-center dan memaksa halaman bergeser ke area pandang tengah. Kita sepenuhnya bergantung pada CSS3 untuk melakukan pekerjaan berat.

.stage-left {
  left: -480px;
}

.stage-right {
  left: 480px;
}

.stage-center {
  top: 0;
  left: 0;
}

Selanjutnya, mari kita lihat CSS yang menangani deteksi dan orientasi perangkat seluler. Kita dapat menangani setiap perangkat dan setiap resolusi (lihat resolusi kueri media). Saya hanya menggunakan beberapa contoh sederhana dalam demo ini untuk mencakup sebagian besar tampilan potret dan lanskap di perangkat seluler. Hal ini juga berguna untuk menerapkan akselerasi hardware per perangkat. Misalnya, karena versi desktop WebKit mempercepat semua elemen yang diubah (terlepas apakah itu 2-D atau 3-D), masuk akal untuk membuat kueri media dan mengecualikan akselerasi pada level itu. Perlu diperhatikan bahwa trik akselerasi hardware tidak memberikan peningkatan kecepatan pada Android Froyo 2.2+. Semua komposisi dilakukan di dalam software.

/* iOS/android phone landscape screen width*/
@media screen and (max-device-width: 480px) and (orientation:landscape) {
  .stage-left {
    left: -480px;
  }

  .stage-right {
    left: 480px;
  }

  .page {
    width: 480px;
  }
}

Membalik

Di perangkat seluler, membalik dikenal sebagai menggeser halaman. Di sini, kita menggunakan beberapa JavaScript sederhana untuk menangani peristiwa ini di perangkat iOS dan Android (berbasis WebKit).

Lihat cara kerjanya http://slidfast.appspot.com/slide-flip-rotate.html.

Saat menangani peristiwa dan transisi sentuh, hal pertama yang Anda inginkan adalah mendapatkan kontrol atas posisi elemen saat ini. Lihat dokumen ini untuk mengetahui informasi selengkapnya tentang WebKitCSSMatrix.

function pageMove(event) {
  // get position after transform
  var curTransform = new WebKitCSSMatrix(window.getComputedStyle(page).webkitTransform);
  var pagePosition = curTransform.m41;
}

Karena kita menggunakan transisi ease-out CSS3 untuk membalik halaman, element.offsetLeft biasa tidak akan berfungsi.

Selanjutnya, kita ingin mengetahui arah yang dibalik pengguna dan menetapkan nilai minimum agar peristiwa (navigasi halaman) dapat terjadi.

if (pagePosition >= 0) {
 //moving current page to the right
 //so means we're flipping backwards
   if ((pagePosition > pageFlipThreshold) || (swipeTime < swipeThreshold)) {
     //user wants to go backward
     slideDirection = 'right';
   } else {
     slideDirection = null;
   }
} else {
  //current page is sliding to the left
  if ((swipeTime < swipeThreshold) || (pagePosition < pageFlipThreshold)) {
    //user wants to go forward
    slideDirection = 'left';
  } else {
    slideDirection = null;
  }
}

Anda juga akan melihat bahwa kami mengukur swipeTime dalam milidetik. Hal ini memungkinkan peristiwa navigasi diaktifkan jika pengguna menggeser layar dengan cepat untuk membalik halaman.

Untuk memosisikan halaman dan membuat animasi terlihat native saat jari menyentuh layar, kita menggunakan transisi CSS3 setelah setiap peristiwa diaktifkan.

function positionPage(end) {
  page.style.webkitTransform = 'translate3d('+ currentPos + 'px, 0, 0)';
  if (end) {
    page.style.WebkitTransition = 'all .4s ease-out';
    //page.style.WebkitTransition = 'all .4s cubic-bezier(0,.58,.58,1)'
  } else {
    page.style.WebkitTransition = 'all .2s ease-out';
  }
  page.style.WebkitUserSelect = 'none';
}

Saya mencoba bermain-main dengan cubic-bezier untuk memberikan nuansa native terbaik pada transisi, tetapi ease-out berhasil.

Terakhir, untuk membuat navigasi terjadi, kita harus memanggil metode slideTo() yang telah ditentukan sebelumnya yang kita gunakan dalam demo terakhir.

track.ontouchend = function(event) {
  pageMove(event);
  if (slideDirection == 'left') {
    slideTo('products-page');
  } else if (slideDirection == 'right') {
    slideTo('home-page');
  }
}

Memutar

Selanjutnya, mari kita lihat animasi rotasi yang digunakan dalam demo ini. Anda dapat memutar halaman yang sedang dilihat 180 derajat kapan saja untuk menampilkan sisi sebaliknya dengan mengetuk opsi menu “Kontak”. Sekali lagi, ini hanya memerlukan beberapa baris CSS dan beberapa JavaScript untuk menetapkan class transisi onclick. CATATAN: Transisi rotasi tidak dirender dengan benar pada sebagian besar versi Android karena tidak memiliki kemampuan transformasi 3D CSS. Sayangnya, alih-alih mengabaikan flip, Android membuat halaman "cartwheel" dengan memutar, bukan membalik. Sebaiknya gunakan transisi ini seperlunya hingga dukungannya ditingkatkan.

Markup (konsep dasar depan dan belakang):

<div id="front" class="normal">
...
</div>
<div id="back" class="flipped">
    <div id="contact-page" class="page">
        <h1>Contact Page</h1>
    </div>
</div>

JavaScript:

function flip(id) {
  // get a handle on the flippable region
  var front = getElement('front');
  var back = getElement('back');

  // again, just a simple way to see what the state is
  var classes = front.className.split(' ');
  var flipped = classes.indexOf('flipped');

  if (flipped >= 0) {
    // already flipped, so return to original
    front.className = 'normal';
    back.className = 'flipped';
    FLIPPED = false;
  } else {
    // do the flip
    front.className = 'flipped';
    back.className = 'normal';
    FLIPPED = true;
  }
}

CSS:

/*----------------------------flip transition */
#back,
#front {
  position: absolute;
  width: 100%;
  height: 100%;
  -webkit-backface-visibility: hidden;
  -webkit-transition-duration: .5s;
  -webkit-transform-style: preserve-3d;
}

.normal {
  -webkit-transform: rotateY(0deg);
}

.flipped {
  -webkit-user-select: element;
  -webkit-transform: rotateY(180deg);
}

Men-debug akselerasi hardware

Setelah membahas transisi dasar, mari kita lihat mekanisme cara kerjanya dan digabungkan.

Untuk mewujudkan sesi proses debug yang ajaib ini, mari kita aktifkan beberapa browser dan IDE pilihan Anda. Pertama-tama, mulai Safari dari command line untuk menggunakan beberapa variabel lingkungan proses debug. Saya menggunakan Mac, sehingga perintahnya mungkin berbeda berdasarkan OS Anda. Buka Terminal dan ketik perintah berikut:

  • $&gt; export CA_COLOR_OPAQUE=1
  • $> export CA_LOG_MEMORY_USAGE=1
  • $> /Applications/Safari.app/Contents/MacOS/Safari

Tindakan ini akan memulai Safari dengan beberapa helper proses debug. CA_color_OPAQUE menunjukkan kepada kita elemen mana yang benar-benar digabungkan atau dipercepat. CA_LOG_MEMORY_USAGE menunjukkan jumlah memori yang kita gunakan saat mengirimkan operasi gambar ke backing store. Ini memberi tahu Anda dengan tepat seberapa banyak ketegangan yang Anda alami pada perangkat seluler, dan mungkin memberikan petunjuk tentang bagaimana penggunaan GPU Anda mungkin menghabiskan baterai perangkat target.

Sekarang, mari kita aktifkan Chrome agar dapat melihat beberapa informasi frame per detik (FPS) yang baik:

  1. Buka browser web Google Chrome.
  2. Di kolom URL, ketik about:flags.
  3. Scroll ke bawah beberapa item, lalu klik “Aktifkan” untuk Penghitung FPS.

Jika melihat halaman ini di Chrome versi yang ditingkatkan, Anda akan melihat penghitung FPS berwarna merah di sudut kiri atas.

FPS Chrome

Ini adalah cara kami mengetahui bahwa akselerasi hardware diaktifkan. Hal ini juga memberi kita gambaran tentang bagaimana animasi berjalan dan apakah ada kebocoran (animasi yang berjalan terus-menerus yang harus dihentikan).

Cara lain untuk benar-benar memvisualisasikan akselerasi perangkat keras adalah jika Anda membuka halaman yang sama di Safari (dengan variabel lingkungan yang saya sebutkan di atas). Setiap elemen DOM yang dipercepat memiliki tint merah. Ini menunjukkan dengan tepat apa yang sedang disusun oleh lapisan. Perhatikan bahwa navigasi putih tidak berwarna merah karena tidak dipercepat.

Kontak Gabungan

Setelan serupa untuk Chrome juga tersedia di “Batas lapisan render gabungan” about:flags.

Cara lain yang bagus untuk melihat lapisan gabungan adalah dengan melihat demo daun jatuh WebKit saat mod ini diterapkan.

Daun yang Dikompos

Dan terakhir, untuk benar-benar memahami performa hardware grafis aplikasi kita, mari kita lihat bagaimana memori dikonsumsi. Di sini kita melihat bahwa kita mendorong 1,38 MB petunjuk gambar ke buffering CoreAnimation di Mac OS. Buffer memori Core Animation dibagikan antara OpenGL ES dan GPU untuk membuat piksel akhir yang Anda lihat di layar.

Animasi inti 1

Saat kita hanya mengubah ukuran atau memaksimalkan jendela browser, kita melihat memori juga bertambah.

Coreanimation 2

Hal ini memberi Anda gambaran tentang penggunaan memori di perangkat seluler hanya jika Anda mengubah ukuran browser ke dimensi yang benar. Jika Anda men-debug atau menguji lingkungan iPhone, ubah ukurannya menjadi 480x320 piksel. Sekarang kita memahami dengan tepat cara kerja akselerasi hardware dan hal yang diperlukan untuk men-debug. Membacanya adalah satu hal, tetapi melihat buffering memori GPU bekerja secara visual benar-benar memberikan perspektif.

Di Balik Layar: Mengambil dan Menyimpannya ke Cache

Sekarang saatnya meningkatkan kualitas penyimpanan dalam cache halaman dan resource. Sama seperti pendekatan yang digunakan JQuery Mobile dan framework serupa, kita akan melakukan pengambilan awal dan menyimpan halaman ke dalam cache dengan panggilan AJAX serentak.

Mari kita bahas beberapa masalah inti web seluler dan alasan mengapa kita perlu melakukannya:

  • Pengambilan: Dengan melakukan pengambilan halaman terlebih dahulu, pengguna dapat menggunakan aplikasi secara offline dan juga tidak perlu menunggu di antara tindakan navigasi. Tentu saja, kita tidak ingin membebani bandwidth perangkat saat perangkat terhubung ke internet, jadi kita harus menggunakan fitur ini seperlunya.
  • Penyimpanan dalam cache: Selanjutnya, kita menginginkan pendekatan serentak atau asinkron saat mengambil dan menyimpan halaman ini dalam cache. Kita juga perlu menggunakan localStorage (karena didukung dengan baik di antara perangkat) yang sayangnya tidak asinkron.
  • AJAX dan penguraian respons: Menggunakan innerHTML() untuk menyisipkan respons AJAX ke dalam DOM berbahaya (dan tidak dapat diandalkan?). Sebagai gantinya, kita menggunakan mekanisme yang andal untuk penyisipan respons AJAX dan menangani panggilan serentak. Kami juga memanfaatkan beberapa fitur baru HTML5 untuk mengurai xhr.responseText.

Dengan kode dari demo Geser, Balik, dan Putar, kita mulai dengan menambahkan beberapa halaman sekunder dan menautkan ke halaman tersebut. Kemudian, kita akan mengurai link dan membuat transisi secara langsung.

Beranda iPhone

Lihat demo Ambil dan Cache di sini.

Seperti yang dapat Anda lihat, kita memanfaatkan markup semantik di sini. Hanya link ke halaman lain. Halaman turunan mengikuti struktur node/class yang sama dengan induknya. Kita dapat melakukannya lebih jauh dan menggunakan atribut data-* untuk node “halaman”, dll. Berikut adalah halaman detail (turunan) yang terletak di file html terpisah (/demo2/home-detail.html) yang akan dimuat, di-cache, dan disiapkan untuk transisi saat aplikasi dimuat.

<div id="home-page" class="page">
  <h1>Home Page</h1>
  <a href="demo2/home-detail.html" class="fetch">Find out more about the home page!</a>
</div>

Sekarang mari kita lihat JavaScript. Untuk mempermudah, saya membiarkan bantuan atau pengoptimalan apa pun di luar kode. Yang kita lakukan di sini adalah melakukan loop melalui array node DOM tertentu untuk mencari link yang akan diambil dan di-cache. Catatan—Untuk demo ini, metode fetchAndCache() ini dipanggil saat halaman dimuat. Kami mengerjakannya ulang di bagian berikutnya saat kami mendeteksi koneksi jaringan dan menentukan kapan harus dipanggil.

var fetchAndCache = function() {
  // iterate through all nodes in this DOM to find all mobile pages we care about
  var pages = document.getElementsByClassName('page');

  for (var i = 0; i < pages.length; i++) {
    // find all links
    var pageLinks = pages[i].getElementsByTagName('a');

    for (var j = 0; j < pageLinks.length; j++) {
      var link = pageLinks[j];

      if (link.hasAttribute('href') &amp;&amp;
      //'#' in the href tells us that this page is already loaded in the DOM - and
      // that it links to a mobile transition/page
         !(/[\#]/g).test(link.href) &amp;&amp;
        //check for an explicit class name setting to fetch this link
        (link.className.indexOf('fetch') >= 0))  {
         //fetch each url concurrently
         var ai = new ajax(link,function(text,url){
              //insert the new mobile page into the DOM
             insertPages(text,url);
         });
         ai.doGet();
      }
    }
  }
};

Kami memastikan pascapemrosesan asinkron yang tepat melalui penggunaan objek “AJAX”. Ada penjelasan yang lebih lanjut tentang penggunaan localStorage dalam panggilan AJAX di Bekerja di Luar Jaringan dengan HTML5 Offline. Dalam contoh ini, Anda melihat penggunaan dasar caching pada setiap permintaan dan menyediakan objek yang di-cache saat server mengembalikan respons apa pun selain respons yang berhasil (200).

function processRequest () {
  if (req.readyState == 4) {
    if (req.status == 200) {
      if (supports_local_storage()) {
        localStorage[url] = req.responseText;
      }
      if (callback) callback(req.responseText,url);
    } else {
      // There is an error of some kind, use our cached copy (if available).
      if (!!localStorage[url]) {
        // We have some data cached, return that to the callback.
        callback(localStorage[url],url);
        return;
      }
    }
  }
}

Sayangnya, karena localStorage menggunakan UTF-16 untuk encoding karakter, setiap byte disimpan sebagai 2 byte sehingga batas penyimpanan kita menjadi total 2,6 MB dari 5 MB. Seluruh alasan pengambilan dan penyimpanan halaman/markup ini dalam cache di luar cakupan cache aplikasi akan dijelaskan di bagian berikutnya.

Dengan kemajuan terbaru pada elemen iframe dengan HTML5, kini kita memiliki cara yang sederhana dan efektif untuk mengurai responseText yang kita dapatkan dari panggilan AJAX. Ada banyak parser JavaScript 3.000 baris dan ekspresi reguler yang menghapus tag skrip dan sebagainya. Namun, mengapa tidak membiarkan browser melakukan hal terbaiknya? Dalam contoh ini, kita akan menulis responseText ke dalam iframe tersembunyi sementara. Kami menggunakan atribut “sandbox” HTML5 yang menonaktifkan skrip dan menawarkan banyak fitur keamanan…

Dari spesifikasi: Jika ditentukan, atribut sandbox akan mengaktifkan serangkaian batasan tambahan pada konten yang dihosting oleh iframe. Nilainya harus berupa kumpulan token unik yang dipisahkan spasi dan tidak peka huruf besar/kecil ASCII. Nilai yang diizinkan adalah allow-forms, allow-same-origin, allow-scripts, dan allow-top-navigation. Saat atribut ditetapkan, konten akan diperlakukan sebagai berasal dari origin unik, formulir dan skrip akan dinonaktifkan, link akan dicegah untuk menargetkan konteks penjelajahan lainnya, dan plugin akan dinonaktifkan.

var insertPages = function(text, originalLink) {
  var frame = getFrame();
  //write the ajax response text to the frame and let
  //the browser do the work
  frame.write(text);

  //now we have a DOM to work with
  var incomingPages = frame.getElementsByClassName('page');

  var pageCount = incomingPages.length;
  for (var i = 0; i < pageCount; i++) {
    //the new page will always be at index 0 because
    //the last one just got popped off the stack with appendChild (below)
    var newPage = incomingPages[0];

    //stage the new pages to the left by default
    newPage.className = 'page stage-left';

    //find out where to insert
    var location = newPage.parentNode.id == 'back' ? 'back' : 'front';

    try {
      // mobile safari will not allow nodes to be transferred from one DOM to another so
      // we must use adoptNode()
      document.getElementById(location).appendChild(document.adoptNode(newPage));
    } catch(e) {
      // todo graceful degradation?
    }
  }
};

Safari dengan benar menolak untuk memindahkan node secara implisit dari satu dokumen ke dokumen lain. Error akan muncul jika node turunan baru dibuat dalam dokumen yang berbeda. Jadi di sini kita menggunakan adoptNode dan semuanya berjalan lancar.

Jadi mengapa iframe? Mengapa tidak menggunakan innerHTML saja? Meskipun innerHTML kini menjadi bagian dari spesifikasi HTML5, memasukkan respons dari server (baik atau buruk) ke area yang tidak dicentang adalah praktik yang berbahaya. Selama menulis artikel ini, saya tidak dapat menemukan siapa pun yang menggunakan apa pun selain innerHTML. Saya tahu JQuery menggunakannya di intinya dengan penggantian tambahan pada pengecualian saja. JQuery Mobile juga menggunakannya. Namun, saya belum melakukan pengujian berat terkait innerHTML “berhenti berfungsi secara acak”, tetapi akan sangat menarik untuk melihat semua platform yang terpengaruh. Menarik juga untuk melihat pendekatan mana yang berperforma lebih baik... Saya juga pernah mendengar klaim dari kedua belah pihak.

Deteksi, penanganan, dan pembuatan profil jenis jaringan

Setelah memiliki kemampuan untuk melakukan buffering (atau cache prediktif) aplikasi web, kita harus menyediakan fitur deteksi koneksi yang tepat yang membuat aplikasi kita lebih cerdas. Di sinilah pengembangan aplikasi seluler menjadi sangat sensitif terhadap mode online/offline dan kecepatan koneksi. Masukkan Network Information API. Setiap kali saya menampilkan fitur ini dalam presentasi, seseorang di antara audiens akan mengangkat tangannya dan bertanya, “Untuk apa saya menggunakannya?”. Jadi, berikut adalah kemungkinan cara untuk menyiapkan aplikasi web seluler yang sangat cerdas.

Skenario yang membosankan terlebih dahulu... Saat berinteraksi dengan Web dari perangkat seluler di kereta berkecepatan tinggi, jaringan bisa saja hilang pada berbagai momen dan wilayah geografis yang berbeda mungkin mendukung kecepatan transmisi yang berbeda (misalnya, HSPA atau 3G mungkin tersedia di beberapa area perkotaan, tetapi area terpencil mungkin mendukung teknologi 2G yang jauh lebih lambat). Kode berikut menangani sebagian besar skenario koneksi.

Kode berikut memberikan:

  • Akses offline melalui applicationCache.
  • Mendeteksi apakah di-bookmark dan offline.
  • Mendeteksi saat beralih dari offline ke online dan sebaliknya.
  • Mendeteksi koneksi lambat dan mengambil konten berdasarkan jenis jaringan.

Sekali lagi, semua fitur ini memerlukan sangat sedikit kode. Pertama, kita mendeteksi peristiwa dan skenario pemuatan:

window.addEventListener('load', function(e) {
 if (navigator.onLine) {
  // new page load
  processOnline();
 } else {
   // the app is probably already cached and (maybe) bookmarked...
   processOffline();
 }
}, false);

window.addEventListener("offline", function(e) {
  // we just lost our connection and entered offline mode, disable eternal link
  processOffline(e.type);
}, false);

window.addEventListener("online", function(e) {
  // just came back online, enable links
  processOnline(e.type);
}, false);

Di EventListener di atas, kita harus memberi tahu kode apakah kode tersebut dipanggil dari peristiwa atau permintaan atau muat ulang halaman yang sebenarnya. Alasan utamanya adalah karena peristiwa onload isi tidak akan diaktifkan saat beralih antara mode online dan offline.

Selanjutnya, kita memiliki pemeriksaan sederhana untuk peristiwa ononline atau onload. Kode ini mereset link yang dinonaktifkan saat beralih dari offline ke online, tetapi jika aplikasi ini lebih canggih, Anda dapat menyisipkan logika yang akan melanjutkan pengambilan konten atau menangani UX untuk koneksi yang terputus-putus.

function processOnline(eventType) {

  setupApp();
  checkAppCache();

  // reset our once disabled offline links
  if (eventType) {
    for (var i = 0; i < disabledLinks.length; i++) {
      disabledLinks[i].onclick = null;
    }
  }
}

Hal yang sama berlaku untuk processOffline(). Di sini, Anda akan memanipulasi aplikasi untuk mode offline dan mencoba memulihkan transaksi yang terjadi di balik layar. Kode di bawah ini akan mencari semua link eksternal dan menonaktifkannya—membuat pengguna terjebak di aplikasi offline kita selamanya, muahahaha!

function processOffline() {
  setupApp();

  // disable external links until we come back - setting the bounds of app
  disabledLinks = getUnconvertedLinks(document);

  // helper for onlcick below
  var onclickHelper = function(e) {
    return function(f) {
      alert('This app is currently offline and cannot access the hotness');return false;
    }
  };

  for (var i = 0; i < disabledLinks.length; i++) {
    if (disabledLinks[i].onclick == null) {
      //alert user we're not online
      disabledLinks[i].onclick = onclickHelper(disabledLinks[i].href);

    }
  }
}

Oke, sekarang kita lanjut ke hal yang menarik. Setelah aplikasi mengetahui status koneksinya, kita juga dapat memeriksa jenis koneksi saat online dan menyesuaikannya. Saya telah mencantumkan download dan latensi penyedia Amerika Utara yang umum di komentar untuk setiap koneksi.

function setupApp(){
  // create a custom object if navigator.connection isn't available
  var connection = navigator.connection || {'type':'0'};
  if (connection.type == 2 || connection.type == 1) {
      //wifi/ethernet
      //Coffee Wifi latency: ~75ms-200ms
      //Home Wifi latency: ~25-35ms
      //Coffee Wifi DL speed: ~550kbps-650kbps
      //Home Wifi DL speed: ~1000kbps-2000kbps
      fetchAndCache(true);
  } else if (connection.type == 3) {
  //edge
      //ATT Edge latency: ~400-600ms
      //ATT Edge DL speed: ~2-10kbps
      fetchAndCache(false);
  } else if (connection.type == 2) {
      //3g
      //ATT 3G latency: ~400ms
      //Verizon 3G latency: ~150-250ms
      //ATT 3G DL speed: ~60-100kbps
      //Verizon 3G DL speed: ~20-70kbps
      fetchAndCache(false);
  } else {
  //unknown
      fetchAndCache(true);
  }
}

Ada banyak penyesuaian yang dapat kita lakukan pada proses retrieveAndCache, tetapi yang saya lakukan di sini adalah memintanya untuk mengambil resource asinkron (true) atau sinkron (false) untuk koneksi tertentu.

Linimasa Permintaan Edge (Sinkron)

Sinkronisasi Edge

Linimasa Permintaan WIFI (Asinkron)

Asinkron Wi-Fi

Hal ini memungkinkan setidaknya beberapa metode penyesuaian pengalaman pengguna berdasarkan koneksi yang lambat atau cepat. Ini bukanlah solusi yang paling tepat. Daftar tugas lainnya adalah menampilkan modal pemuatan saat link diklik (pada koneksi lambat) saat aplikasi mungkin masih mengambil halaman link tersebut di latar belakang. Hal terpenting di sini adalah mengurangi latensi sekaligus memanfaatkan kemampuan penuh koneksi pengguna dengan HTML5 terbaru dan terbaik yang ditawarkan. Lihat demo deteksi jaringan di sini.

Kesimpulan

Perjalanan di jalan aplikasi HTML5 seluler baru saja dimulai. Sekarang Anda melihat dasar-dasar “framework” seluler yang sangat sederhana dan mendasar yang dibuat hanya di sekitar HTML5 dan teknologi pendukungnya. Menurut saya, penting bagi developer untuk menggunakan dan menangani fitur-fitur ini pada intinya serta tidak disamarkan oleh wrapper.