クラむアント偎からのサヌビスを数回高速化したす。 Ajax +バックグラりンドプリロヌド+ホバヌ時

デヌタベヌスずサヌバヌを構成するず、ク゚リが最適化され、すべおのキャッシュがオンになりたすが、サヌビスを高速化するために残ったオプションは䜕ですか 抜象化の最埌のレベルであるナヌザヌむンタヌフェむスにより、実質的に䜕もない状態から速床を䞊げるこずができたす。 これらの3぀の簡単なレシピは、サむトを数回高速化するのに圹立぀だけでなく、䟿利なパンをいく぀か远加したす。 最初の郚分では、䞀緒に䞍必芁な困難なしにサむトをプリミティブなajaxナビゲヌションに移行したす。 2番目の方法では、マりスをバックグラりンドでホバヌしたずきにプリロヌドペヌゞを远加したす。 音速を克服したすか ゞャンプしたす









むンスタントリタヌンでペヌゞ間のAjax遷移



ほんの数日前、このトピックに関する蚘事がありたした 。 私の蚘事はその時点ですでに準備ができおいたので、ajaxに぀いおの郚分は私の同僚を郚分的に繰り返したす。 それはさらに別の経隓であり、それがいかにシンプルであるかの蚌拠ず考えおください。 さらに、私の代替案は、同僚によっお考慮されなかったいく぀かの重芁な偎面に觊れ、実甚的な郚分をより深く掘り䞋げ、蚘事のコヌドに基づいおナビゲヌションを最初から完党に実装できるようにしたす。



Ajaxトランゞションを簡単な方法で実装するこずはそれほど難しくありたせん。 すぐに予玄したしょう

確かに、これをさらに簡単か぀正確にする既補のラむブラリがありたす。 この蚘事の目的は、これが手で簡単に行えるこずを瀺すこずです。 なんで たずえば、スクリプトをサむトに合わせお調敎する堎合。 この蚘事の䟋では、ajax移行のすべおの偎面たずえば、履歎APIを䜿甚しない䜜業は圱響を受けたせんこのようなブラりザヌでは通垞のブラりゞングが保持されたす、たたはスクリプトのバヌゞョン管理はたったく取り䞊げたせん。 すべおの偎面が蚘事に収たるわけではありたせんが、蚀及されたす。



倖囜人が話しおいるこずを簡単に説明しおください。
ペヌゞを切り替えるずきに停止するこずなく動䜜するVKontakteオヌディオプレヌダヌを芋たこずがありたすか これは、ペヌゞがリロヌドされないためです。各リンクをクリックするず、javascriptがサヌバヌにリク゚ストを送信し、応答でペヌゞを受信し、珟圚のペヌゞのコンテンツをそれに眮き換えたす。 このアプロヌチの明らかな利点継続的なプレヌダヌを䜜成する機胜に加えおサむトのコンポヌネントぞのより深い断片化により、ペヌゞの読み蟌み速床を䞊げるこずができたずえば、サむトの静的ヘッダヌを再読み蟌みできたせん、既に読み蟌たれたスクリプトを再読み蟌みする必芁がなく、リク゚ストを保存したす速床の向䞊たずえば、プラグむンのサヌドパヌティのダむナミックラむブラリの堎合、ブラりザのキャッシュが垞にこの問題を解決するずは限りたせん。 私の堎合、ajaxぞの移行は、Googleチャヌトをキャッシュできないこずによっお決定されたす。Googleチャヌトのjsは各移行で1秒以䞊䞎えられ、キャッシュされたせんでした。



サヌバヌ偎の準備。



MVC、JSON、ペヌゞのコンテンツをパッケヌゞ化したす。
それでもロゞックをビュヌから分離しおいない堎合は、それを行う時間です。 ajaxを介しおペヌゞを提䟛するには、そのコンテンツを行ずしお取埗する必芁がありたす。 したがっお、コヌドが䞭間デヌタの出力ず亀代する堎合、深刻なリファクタリングが必芁ですそれは䟝然ずしお必芁であり、将来の生掻が楜になりたすテンプレヌトを䜿甚しお、出力のすべおのデヌタが最埌に生成されるこずを確認する必芁がありたす文字列を返す通垞の関数でも圹割を実行できたす互いに独立しお準備された動的デヌタを持぀倉数の転送先。



リンクをクリックするず、ペヌゞがJavaScriptでレンダリングされるようになるため、䜕らかの圢匏でパッケヌゞ化する必芁がありたす。 簡単にするために、JSONを䜿甚したす。

デヌタをコンポヌネントに分割しおみたしょうhtmlコヌド、むンラむンスクリプト、ペヌゞタむトル、リダむレクトに関する情報。 これらのコンポヌネントを入力ずしお受け取り、リンクをクリックしたずきにJSにリク゚ストの結果を返す関数を䜜成したす擬䌌コヌドの䞋では、基本はphpですが、別の蚀語を持っおいる可胜性がありたす。



function response(html, options = null) { define res; //     JSON res['html'] = html; // html  html- . //      html,   – error. if (options) { if (options['script']) { res['script'] = options['script']; } if (options['redirect']) { res['redirect'] = options['redirect']; } if (options['title']) { res['title'] = options['title']; } } //       json   UTF-8 header('Content-Type: application/json; charset=UTF-8'); print json_encode($res); //  res  json     http. exit(); //       json . }
      
      







ここで、ajaxの移行䞭にペヌゞを衚瀺するには、response関数を呌び出すだけで十分です。

たずえば、phpの堎合、次のようになりたす。

 function wrapAbout($employees) { return "<div>   2012 .    : {$employees}</div>"; } $employees = getEmployeesNumber(); $html = wrapAbout($employees); response($html, array('title' => ' '));
      
      







゚ラヌ凊理。
ペヌゞを返すこずが垞に可胜であるずは限りたせん。 ゚ラヌ情報は個別に凊理したす。

これを行うには、関数を䜜成したす。

 function error(num, msg) { define res; //,     JSON define res['error']; //     res['error']['num'] = $num; //  res['error']['msg'] = $msg; //          print json_encode(res); exit(); }
      
      







䟋倖が発生した堎合、たたは誀ったデヌタが送信された堎合、次のように察応したす。

 if ($_POST['password'] != $password) { error(1, 'Invalid password'); }
      
      







盎接リク゚ストのサポヌト。
しかし、ナヌザヌがリンクをたどらずに盎接アクセスした堎合、たたはF5キヌを抌した堎合はどうなりたすか そのような堎合も凊理する必芁がありたす。 このため、htmlを含む行が返されるようにコヌドを最適化したした。 このhtmlを盎接䞎えるだけで十分で、リク゚ストがajaxからではない堎合のjsonの圢匏ではありたせん。 これを理解する方法は ajaxからリク゚ストする堎合、远加のパラメヌタヌajax = 1を枡すので、リク゚ストの発信元ず結果を返す方法を垞に区別できたす。



クラむアント郚



クラむアント偎の準備
ここでは、少しのリファクタリングも埅っおいたす。 「ペヌゞ状態」などの抂念が衚瀺されたす。 ペヌゞ間を移動するこずはなくなり、すべおが1぀のペヌゞで行われるため、状態を分離する必芁がありたす。 戻るボタンを䜿甚しお、すぐにペヌゞの前の状態に戻り、同時にすべおの倉数をjavascript環境に入れたいず思いたす。 これを行うには、珟圚の状態党䜓を保存する1぀の゚ンティティに状態を保存したす。 怖いですね、ささいに芋えたす

 cur = {somevar: true};
      
      





すべおのペヌゞ倉数をcur内に保存したす-その埌、その状態を簡単に保存できたす。

前のペヌゞのコンテンツが数ステップ前に保存される補助キャッシュを開始したしょう。

 var globalPageHtml = [];
      
      







リンク凊理


リンクはもはやリンクではなく、クリックするず、ajaxを介しお別のペヌゞをロヌドするJavaScriptを実行するボタンです。 同時に、ajaxナビゲヌションが利甚できない堎合たずえば、履歎APIがサポヌトされおおらず、URLを倉曎できないが、ハッシュナビゲヌションを䜿甚したくない堎合、通垞の移行の可胜性を維持する必芁がありたす。 たた、Ctrlキヌを抌しながらクリックしお新しいタブでリンクを開く必芁がありたす。 この方法ですべおのリンクをやり盎したす。

 <a onclick="go('/someurl', null, event); return false;" href="/someurl">Some link</a>
      
      



どうしたの これは最も重芁な方法です。ナビゲヌションを扱うのは圌です。それに぀いおは以䞋で説明したす。



URLず履歎を操䜜する


ナビゲヌション履歎を操䜜するには、䞀般的なhistory.jsラむブラリを䜿甚したす。 これにより、将来的にハッシュナビゲヌションを同時にサポヌトできるようになりたすが、珟圚のずころ、プリミティブなAjaxナビゲヌションを構築しおいたす。぀たり、進行状況に遅れをずるブラりザヌを船倖に残すこずができたす。

[戻る]ボタンを䜿甚した䜜業をサポヌトするために、履歎で䜜業を初期化したすプロゞェクトでjqueryを䜿甚するこずを前提ずしおいたす。



 var History = window.History; History.enabled = !!(window.history && history.pushState); //Temp turn off html4 if (History.enabled) { $(window).bind('statechange', function() { var State = History.getState(); //replacingState -  ,    ajax-  .      URL   ,      -   back     .           ajax-     . if (!cur.replacingState) { cur = State.data; //,   ,   history api,      history.js if (globalPageHtml[State.url]) { //       ? $('#content').html(globalPageHtml[State.url].html); // ,       content,    html     . Html    . if (globalPageHtml[State.url].scripts) {//     ,    ,   , jquery       DOM. var fragment = document.createDocumentFragment(); globalPageHtml[State.url].scripts.each( function(nodeIndex, scriptNode) { fragment.appendChild(scriptNode); //   ,            -    DOM. }); document.getElementById('content').appendChild(fragment); //    DOM  ,     . } window.scrollTo(globalPageHtml[State.url].scroll[0], globalPageHtml[State.url].scroll[1]); //        ,    . delete globalPageHtml[State.url]; //  } else { go(State.url, null, null, true); //    "" ,      (,         URL  ).       ,      . } } cur.replacingState = false; }); }
      
      







Ajaxトランゞションの基本的なJavaScript


それで、最も重芁なこずに着きたした。 リンクがクリックされるず、go関数が呌び出されたす。

 function go(url, query, event, back) { if (!History.enabled || (event && (event.ctrlKey || event.metaKey))) { //  ctrl ( command  )   history api var requestString = url; if (query) { requestString += query; } if (!History.enabled) { window.location = requestString; //   history api,      } else { window.open(requestString, '_blank'); //    ctrl,      /,   ajax } return; } var query = query || {}; var urlString = (url.length > 0) ? url : $(location).attr('href').split('?', 1); // URL   ( ),    if (jQuery.param(query).length > 0) { urlString += '?' + jQuery.param(query); //    (        ) } urlString = window.decodeURI(urlString); //  .          URL  history api.   , ,   rewrite,       N     /post/N,  - /post?item=N -        ajax . $.extend(query, {ajax: 1}); //       ajax  (   ,   url    ) $.ajax({ url: url, global: false, type: "GET", data: query, dataType: "json", success: function (res) { if (res.html) { //ajax     html,         ,      if (!back) { //  ,      ,      var currentUrl = $(location).attr('href'); globalPageHtml[currentUrl] = new Object(); //    ,    url globalPageHtml[currentUrl].html = $('#content').html(); globalPageHtml[currentUrl].scroll = getScrollXY(); //     globalPageHtml[currentUrl].scripts = $('script:not([src])'); //   .      .          cur.   ,  -       (,      ),     -  ,      (disclaimer:       ).   inline  ,                 (    ). //   state, history api,   cur.replacingState = true; History.replaceState(cur, $(document).attr('title') || null, window.decodeURI(currentUrl) || null); //       cur,  ,       back,   cur.replacingState = true; History.pushState(null, res.title || null, urlString); // URL     } window.scrollTo(0, 0); //      document.getElementById('content').innerHTML = res.html; //, ,    cur = {}; $('script:not([src])').each( //  (      innerHTML,   html()  jquery        ) function(nodeIndex, scriptNode) { try {eval(scriptNode.innerHTML);} catch(e) {/*Do nothing*/} }); } else { errorAlert(res.error || 'Unable to load page ' + url); //errorAlert - ,     ,    } }, error: function (res) { errorAlert(res.error || 'Unable to load page ' + url); } }); }
      
      





それだけです Ajaxナビゲヌションの準備ができたした。



このアプロヌチでは考慮されない偎面に関する重芁な泚意事項


より深刻なajaxナビゲヌションでは、IEのハッシュ䜜業が考慮されたすが、システムの制限はこれに限定されたせん。



ペヌゞごずに異なるスクリプトをロヌドする
珟圚の圢匏では、すべおのJSラむブラリは、ペヌゞに初めおアクセスしたずきにロヌドされるず想定されおいたす。 これは、小芏暡なサヌビスがある堎合に適しおいたすが、倧芏暡なプロゞェクトでは、サむトのこのセクションにのみ関連するjsのみをダりンロヌドしたいず思いたす。 したがっお、より深刻なajaxナビゲヌションは、サヌドパヌティのjsモゞュヌルをロヌドおよびアンロヌドできるはずです。 実装は簡単です。サヌバヌからダりンロヌドする必芁があるスクリプトのURLのリストも転送し、既補のロヌダヌを䜿甚し、次のajax移行時にダりンロヌドしたコヌドを砎棄するだけで十分です。 



スクリプトバヌゞョン
jsコヌドにバグがあり、新しいバヌゞョンをリリヌスしたばかりですか 問題Ajaxナビゲヌション䞭に、スクリプトが数日間曎新されない堎合がありたす-ブラりザヌで開かれたペヌゞでは、リク゚ストごずにスクリプトがダりンロヌドされるわけではありたせんが、長い間メモリ内にあるため、ナヌザヌは曎新を受信できたせん。 解決策がありたす。各ajax遷移でjsモゞュヌルのバヌゞョン番号をサヌバヌから送信したす。 モゞュヌルが曎新されおいる堎合は、メモリから以前のバヌゞョンを眮き換えお、新しいバヌゞョンをダりンロヌドしたす。 同じこずがcssにも圓おはたりたす。



Ajaxグロヌバルキャッシュ
より深刻なAjaxナビゲヌションシステムでは、グロヌバルリク゚ストキャッシュを実装しお、䞀郚のリク゚ストずペヌゞを手動でキャッシュするず䟿利です。 これはホバリング時のプリロヌドに圹立ちたす。 実際、今すぐプリロヌドに進みたす。



ペヌゞのプリロヌド

Ajaxナビゲヌションを䜜成したら、シンプルだが超機胜的な機胜を䜿甚しおサヌビスを高速化できたす。 これらの単玔なトリックの効果は、予想をはるかに超えおいたす。サむトの䜜業は即座に行われたす。 プリロヌドはこれに圹立ちたす。 バックグラりンドでのプリロヌドずホバヌ時の2぀の根本的に異なる方法に分けるこずができたす。 1぀目は、珟圚のペヌゞに関連付けられおいるサむトの少数の固定セクションで䜿甚できたす。 たずえば、[蚭定]ペヌゞに5぀のタブがある堎合、メむンペヌゞをバックグラりンドで読み蟌んだ埌、これらのタブを読み蟌むず、クリックするずすぐに開きたす。 この手法は、補品のリストおよび通垞はリンクのリストでは機胜したせん-リストが5芁玠を超える堎合、怜玢結果から各補品のペヌゞを読み蟌むこずはできたせん。 これは、マりスをホバヌするずきにプリロヌドするのに圹立ちたす。



バックグラりンドプリロヌド



VKontakteの手盎しされたコヌドに基づいた䟋を考えおみたしょう。 いく぀かのタブがあるず仮定したすタブの代わりに、開いおいるペヌゞですぐに開くのに適したセクションやリンクがありたす。 私たちは圌らの䞀人です。 ペヌゞの読み蟌みの最埌にpreloadTabscurrentTabメ゜ッドを実行するだけです。 珟圚のタブを枡しお、北が既に開いおいるタブを読み蟌たずに、欠萜しおいるタブに関する情報のみを返すようにしたす。 アプロヌチの䞀般的なポむント時間がかかる隣接タブに関する情報をすぐにダりンロヌドする代わりに、タブを1぀だけロヌドし、開いおいるタブの準備が敎うずすぐに残りをバックグラりンドでロヌドしたす。 効果は玠晎らしいです。 䟋を芋おみたしょう

 function preloadTabs(current) { cur.preloading = 1; //   ajax    (     ajax      ,      ),    .  ,    callback   (  createElement   DOM-,         -    ,    ). function(data, tab1Html, tab2Html, tab3Html) { var page = $('#content'); if (cur.section != 'tab1') { page.append(createElement('div', {innerHTML: tab1Html})); } if (cur.section != 'tab2') { page.append(createElement('div', {innerHTML: tab2Html})); } if (cur.section != 'tab3') { page.append(createElement('div', {innerHTML: tab3Html})); } cur.preloading = 2; //0 -   , 1 -      , 2 -   }
      
      







リンククリックハンドラヌ内にあり、実際にタブを切り替える関数

 function switchTab(section) { if (cur.section == section) { //    return; } var doSwitch = function(section) { hideCurrentSection(); //   id     cur.section = section; // id  showCurrentSection(); //  ,    ,     append ( display:none )  DOM   //      history  ,    }; if (!cur.preloading || cur.preloading == 1) { //     $('#load_progress').show(); // ,     ,    if (cur.preloading != 1) { preloadTabs(cur.section); } var waitPreload = setInterval(function() { if (cur.preloaded == 2) { //  $('#load_progress').hide(); //    clearInterval(waitPreload); doSwitch(section); } }, 100); } else { doSwitch(section); } }
      
      





このコヌドの䟿利な点は、タブがただロヌドされおいない堎合でも動䜜するこずです誰かが非垞に迅速にリンクをクリックした。 ダりンロヌドがただ開始されおいない堎合は、それを起動しお埅機むンゞケヌタヌを衚瀺し、タブの読み蟌みが開始されおいる堎合は、ダりンロヌドが完了するたで埅機しおから、タブを衚瀺したす。 非垞にシンプルで、サヌビスが開始されたす。



マりスオヌバヌプリロヌド



ほずんどすべおのクリックで、クリック自䜓ずカヌ゜ルがリンク䞊にある期間ずの間に倧幅なミリ秒単䜍の遅延がありたす。 これにより、クリックする前にリンクをダりンロヌドしお、トランゞションをむンスタントに倉えるこずができたす。 ここで泚意する必芁がありたすナヌザヌがカヌ゜ルをポむントAからBに移動し、リンクが道路に沿っお移動する堎合そしお同時に数十の他のリンク-カヌ゜ルがリンクをクリックしただけでブラりザヌずajaxサヌビスをリク゚ストで爆撃したせん クリックする前にカヌ゜ルがリンク䞊に留たり、クリック間の最小時間範囲を蚈算できたすが、ペヌゞ䞊にロヌドできるリンク䞊にカヌ゜ルを眮いた埌、クリックする意図を正確に理解するために十分な時間埌にダりンロヌドが開始されたすただ通過するだけではありたせん。 さらに、1぀のリンクを厳密にキャッシュしたす。新しいリンクをプリロヌドするず、叀いリンクがメモリから消去されたす。



カヌ゜ルの䞊にカヌ゜ルを眮いた埌、関数を呌び出したすたずえば、ナヌザヌリストの堎合、ナヌザヌプロファむルを読み蟌みたす。

 function preloadUser(user_id) { cur.pendingPreloadUid = user_id; cur.pendingPreload = setTimeout(doPreloadUser, 50); //50ms -         }
      
      







mouseoutむベントカヌ゜ルがリンクから離されたによっお、次を呌び出したす

 function cancelPreloadUser() { clearTimeout(cur.pendingPreload); }
      
      







そしお、50ミリ秒埅機するこずができた堎合、リンクをプリロヌドしたす。

 function doPreloadUser() { var uid = cur.pendingPreloadUid; if (cur.cachedUser && (uid != cur.cachedUser.uid)) { // -   ,     ,        unloadUser(cur.cachedUser.uid); } else { cur.cachedUser = new Object(); } cur.cachedUser.uid = uid; //  ajax ,      .     ,     ,           ,          ,      ,        .       cachedUser,    . }
      
      





新しいリンクをプリロヌドする芁求が到着した堎合は、リンクをアンロヌドするこずを忘れないでください。unloadUseruid関数で、グロヌバルキャッシュをクリアする必芁がありたす。







あなたがこの堎所を英雄的に読んだり、この蚘事を準備する䜜業を評䟡しおくれおありがずう。 蚘茉されおいるすべおの方法は、プロゞェクトのサむズに応じお、数時間から数日間実装されたす。 この資料が、実際にWebサヌビスを䜕床も高速化するのに圹立぀こずを本圓に願っおいたす。 コメントを曞いお、りェブサむトの高速化の経隓をコメントで共有しおください。

画像







All Articles