新しいホームポータルMail.Ru

皆さんの多くが少なくとも一度は古いMail.Ruメインページを見たことがあると思いますが、これはかなり長い間変更されていません。 したがって、一般的な理解のために、小さな前文:この投稿で説明する技術的な更新は、社内の変更後に可能になりました。







新しいメインの開発は、かなり大きなチームによって行われました。デザイナー、ユーザビリティスペシャリスト、そしてもちろん開発者が含まれていました。







まず、古いページがテーブルにレイアウトされていたため、必要な速度でロードできませんでした。 そのため、最初にダウンロードを高速化する必要がありました。



第二に、視覚的にも技術的にもページを近代化する必要がありました。 このページは、現在の期待と傾向を満たしている必要があります。

たとえば、再設計の要件の1つはスクロールの欠如だったと言えます。



まず最初に。



高速ローディング





Mail.ruは、良いインターネットチャネルと非常に遅いインターネットチャネルの両方を持つユーザーによってダウンロードされます。 ポータルナビゲーション、ロゴ、郵便、ソーシャルブロックをできるだけ早く表示して、チャンネルが遅いユーザーができるだけ早くページを操作できるようにしたかったのです。







<style> /* mailbox styles */ </style> <div class="mailbox"> <!-- mailbox HTML --> </div> <script> // mailbox JS </script>
      
      







これを行うために、各ブロックのHTMLコードの前にあるスタイルタグにCSSルールを配置しました。 ブラウザは、ブロックのレンダリングに必要なものをすべて手元に持っており、すぐにこれを行います。



ブロックが既に描画され、その機能を実行しないようにするために、たとえば、「カスタム」インターフェース要素を含むメールブロックの承認フォームでは、インラインスクリプトの基本的なJS機能がHTMLの後に配置されます。



したがって、ページが読み込まれると、高速化されたブロックが順番に表示され、レンダリング後に完全に動作します。







その後、残りのブロックを実行できます。

左の列の後に、残りのルール、中央の列のHTML、高度な機能スクリプト、および右の列のHTMLを含む外部スタイルシートを読み込みます。



拡張機能JSファイルの最適なロード場所を実験的に発見しました。

私たちの測定によると、検索への移行は、検索候補の可用性に大きく依存しています。 そのため、可能な限り検索ブロックの近くにナメクジgestのロジックを含むスクリプトをダウンロードしたいと思います。 ただし、中央の列の下には、ユーザーがページにアクセスするコンテンツを含む重要なブロックがあります。 そのため、スクリプトを中央の列を超えて移動します。



コードに沿ってスクリプトをもう少し再配置する場合、バナーがロードされるのを待つ必要があります。これは平均で180msをロードし、中央の列は2倍以上小さくなります。

したがって、最適な場所は中央スピーカーと左スピーカーの間にあります。







次のステップは、すぐにロードすることが不可能または不要なブロックをロードすることです。

これは、左側の列に写真があり、最初は非表示のニュースタブがあるブロックです。







写真のあるブロックには、外部の非静的コンテンツが含まれています。 これらは、いずれの場合でもユーザーが左列の表示よりも後に表示する画像です。 それらがロードされるまで待つ必要があります。つまり、ダウンロードを遅らせることができます。



最初は、ブロックの代わりに、プレースホルダーが単に必要なスペースを占有しています。 ページの最後のポストロードでは、プレースホルダーが実際のブロックコードに置き換えられ、イメージのロードが開始されます。







ページが読み込まれると、ユーザーには1つのニュースタブのみが表示され、残りはタブをクリックした後にのみ表示されます。 非表示のタブをダウンロード後に配置し、ユーザーの要求がある場合にのみ適切な場所に置きます。これにより、ブロックの読み込み時間が大幅に短縮されます。







ブロック自体が宣言される前に呼び出し要素が配置される、ほとんど使用されないブロック(さまざまなプロモーションポップアップなど)があります。

たとえば、新しいデザインに関するプロモーションプレートの「詳細」リンクをクリックすると、追加情報のポップアップが開きます。



なぜなら このブロックはめったに使用されないため、ページ上部のリンクの隣にブロックをロードしても意味がなく、より重要なコンテンツのロードが遅くなります。 したがって、非表示のポップアップはページの最後にあります。 しかし、リンクはほとんどすぐに表示され、ユーザーはポップアップをロードする前でもクリックできます。



このような状況を処理するために、遅延関数呼び出しの単純なシステムを使用します。

ハンドラーがリンクにハングアップし、渡された関数をキューに保存します。



 <a onclick="callbackQuery.run(function(){openPopup()});">...</a>
      
      







ページの最後で、キューハンドラーにページがロードされるコマンドが与えられ、ハンドラーはキューに保存されているすべての関数を起動します。その後、キューハンドラーに転送されたすべての新しい関数がすぐに起動されます。



  <div class="popup">...</div> <script> function openPopup(){ … } </script> ... <script> callbackQuery.loaded(); </script> </body>
      
      











今後、ロゴのダウンロードについて議論する価値があります。

実際には2つあります。 大きな解像度の場合は大きく、残りの場合は小さい。

ページがロードされると、そのうちの1つが表示されますが、ユーザーには現時点では2番目のロゴは表示されません。 非表示のロゴの読み込みを遅らせるには、次のソリューションが使用されます。

最初はコードでは、画像の代わりに、画像に似たクラスを持つ2つのスパンがあります



 <style> @media all and (min-height: 765px) { .logo__link__img_medium { display:none; } } @media all and (min-height: 765px) { .logo__link__img_wide { display:block; } } </style> <span class="logo__link__img logo__link__img_medium"></span> <span class="logo__link__img logo__link__img_wide"></span>
      
      







その後、スクリプトは、メディアクエリで非表示になっているものを判別して、このユーザーを許可し、対応する画像がダウンロード後に追加されます。 2番目はすぐに表示されます。



 <script> logos.forEach(function(logo){ if (logo.currentStyle.display === "block"){ document.write("<img … />"); } else { var image = document.createElement("img"); ... } }); </script>
      
      







もちろん、noscriptはJSが無効になっているユーザーの両方の画像を表示します。



 <noscript> <img class="logo__link__img logo__link__img_medium" … /> <img class="logo__link__img logo__link__img_wide" … /> </noscript>
      
      







CSS




各ブロックのスタイルは互いに分離されており、それぞれが独自のファイルにあります。







ロードを加速する必要があるブロックの場合、スタイルは、forced-blocksフォルダーの対応するサブディレクトリでブロックごとに収集され、ページ上の必要な場所のスタイルタグに自動的に置き換えられます。



 /* css/forced-blocks/mailbox.scss */ @import "../../blocks/mailbox/mailbox";
      
      







 <style> <fest:insert src="css/forced-blocks/mailbox.scss" /> </style>
      
      







残りのスタイルは単一のファイル(/css/styles.scss)に収集され、左の列の後にあるリンクを介してページにアップロードされます。



 @import "blocks/news/_news.scss"; @import "blocks/text-banner/_text-banner.scss"; @import "blocks/banner/_banner.scss"; @import "blocks/informers/_informers.scss";
      
      







 <link href="styles.css" />
      
      







このアプローチにより、ブロックのロードを簡単に操作できます。 ブロックを高速化する必要がある場合は、スタイルを異なる方法で接続するだけです。



Js




JSブートプロセスにもいくつかの微妙な違いがあります。

HEADページでは、「コア」の基本部分がインラインでロードされます。たとえば、加速ブロックの基本機能に必要なイベントを処理するための関数です。



 <head> <script> var mr = { bind: function(){} }; </script> </head> <div class="mailbox">...</div> <script> mr.bind </script>
      
      







その後、拡張機能の外部スクリプトがロードされます。

基本的な機能セットは拡大しています。



 <script> extend(mr, { position: function(){} }); </script>
      
      







したがって、スクリプトのロードも管理が簡単です。



それは何を与えましたか




行われた作業の結果、次の平均値が得られました。





これは、低速接続のエミュレーションを使用したページ読み込みプロセスのビデオをよく示しています。











グラフに示されているように、ユーザーはページのほとんど、つまりポータルナビゲーションと、わずか600ミリ秒後に左および中央の列を確認し、ほとんどの機能はさらに500ミリ秒後に動作します。 そして、1.5秒後、ページは完全に動作可能になります。



スクロール









2番目の大きな課題は巻物の不足でした。

誰もがすぐに必要なサイズにページを合わせることができるMedia Queriesテクノロジーを思い出しました。

主なことは、何をカスタマイズするかを理解することです。



最も単純な一見したところの解決策は、画面解像度に関する既存の統計を調べることです。







これらのデータは特定の数値を示していますが、実際の状況を示しているわけではありません。OSおよびブラウザーのインターフェース要素の設定は考慮されていません。 ページ上で使用可能なスペースが不明です。

したがって、実際のビューポートの独自の測定を行うことが決定されました。





x軸はビューポートの高さ、y軸はビューポートがこの高さより低いユーザーの数です。



まず、ページの読み込み時にビューポートを測定しました。 しかし、これらのデータに頼るべきではないように思われました。 ユーザーがページを開いてから、ブラウザウィンドウを開いてページの操作を続けるのをよく見ました。

ウィンドウのサイズ変更の数を計算しました。 これを行うユーザーは非常に多く、約12%でした。

推測を確認するために、セッションごとの最大ビューポートを測定することにしました。 同様に、メディアクエリをサポートしていないブラウザでのセッションごとの最大ビューポートもあります。これは、ユーザーが非常に多く、約15%持っています。







得られたデータは推測を裏付けました。

グラフは、同じユーザーがページを開いたときに1つのウィンドウサイズを持ち、後で別のウィンドウサイズを持つことを示しています。



グラフでは、ジャンプが明確に表示されます。つまり、この高さに多くのユーザーがいます。 これらのジャンプに集中することにしました。







次の値を選択しました。









633pxの意味?

ビューポートの少ないユーザーの21%にはスクロールが表示されます。 これは私たちに合わなかったので、この領域を別の値である576pxで分割しました。実際には、チャートに変更はありません。







また、領域を633から765ピクセルに分割し、別の値を670ピクセルに分割することにしました。 審美的な理由だけのために、私たちはユーザーの大きなグループで空きスペースを埋めることに決めました。



したがって、576pxからはユーザーにはスクロールがありません。



633pxでは、インデントを変更し、左側の列に署名を表示し、右側の列にメインニュースの追加情報といくつかのゲームを表示します。



670pxでは、空き領域を埋めるためにインデントするだけです。



765pxでは、左の列のロゴと写真のブロックを拡大し、検索の下に「今見ている」ブロック、追加の情報ブロック、右の列に大きなゲームを表示します。



830pxでインデントが変更され、追加のニュースが表示されます。



幅にも変更があります。

幅の広いページでは、列間のインデントが増加し、収まらない狭いページでは、ニュースのタブが「その他」の編集に転送されます。



メディアクエリをサポートしないブラウザの場合、高さ633ピクセルのオプションを選択しました。







実装




メディアクエリのコードとしきい値はいつでも変更できます。

したがって、プロジェクトの半分をシャベルで処理する必要がないようにするには、メディア式を生成するための中央の場所が必要です。

SASSミックスインを使用することにしました。



ブロックでは、デフォルトのスタイルが最初に指定され、以下では、動的スタイルが適用される高さおよび軸とともにminxinに渡されます。



 .someblock { margin-top:10px; @include respond-to-media(xsmall, vertical, margin-top, 5px); @include respond-to-media(small, vertical, margin-top, 10px); } @mixin respond-to-media($screen, $direction, $property, $value: "") { @if $direction == vertical { @if $screen == xsmall { @media only screen and (max-height: 632px) { ... } } @else if $screen == small { @media only screen and (min-height: 633px) and (max-height: 668px) {...} } ... } @else if $direction == horizontal { ... } }
      
      







渡された定数に基づいて、Mixinは表示するメディア式を認識し、結果は次のコードになります。



デフォルト状態



 .someblock { margin-top:5px; }
      
      







メディアクエリをサポートするブラウザーの状態の変更



 @media only screen and (max-height: 632px) { .someblock { margin-top:5px; } } @media only screen and (min-height: 633px) and (max-height: 668px) { .someblock { margin-top:10px; } }
      
      







ポータルナビゲーション同期









ページ上部のMail.Ruポータル全体には、アクティブなポータルナビゲーションの単一ユニットが含まれています。

その中で、ユーザーデータはタイムアウトによって更新されます。メール内の未読の手紙の数、My WorldとOdnoklassnikiの新しいイベントの数です。



左側の列の新しいメインページには、メールブロックに加えて、ソーシャルネットワークに関する拡張情報があり、これも動的に更新する必要があります。



最初の問題は、さまざまなソースからデータを取得することです。ポータルナビゲーションでは、1つのサーバーからMy WorldおよびMailデータ、別のサーバーからOdnoklassniki、メインページからMy Worldから拡張データを受け取ります。



2番目の問題は、クロスポータルであるポータルナビゲーションが個別のプロジェクトに分離されているため、ページに影響を与えることができず、データを同期的に更新する必要があることです。ポータルナビゲーションでOdnoklassnikiからデータを受信する場合、 My WorldおよびMailでデータを受信する場合、My Worldの拡張データを要求し、ページのその部分を更新してから、ポータルナビゲーションのみを更新します。



これを行うために、ポータルナビゲーションのJSONPコールバックで、ページ上のスクリプトを制御し、reverseコマンドで数値を更新する機能が導入されました。



レンダリングは、ページでコールバックが宣言されていない場合にのみ開始され、falseを返します。



 //   function JSONPCallback(data){ var _cb = JSONPCallback._pageCallback; if (typeof _cb == 'function' && _cb(data, draw) !== false){ draw(); }; function draw(){ //    } }
      
      







このようなコールバックを宣言する場合、非同期リクエストを作成し、ページ上の必要なブロックを更新してから、転送されたポータルナビゲーションレンダリング関数を実行して、ポータルナビゲーションに制御を戻すことができます。



 //  JSONPCallback._pageCallback = function(data, draw){ new AJAX('/', …, function(data){ //    … //     draw(); }); //  false,      return false; }
      
      







T.O. このページは、必要な情報の可用性に応じて更新の表示を制御できます。



まとめ









安定性のトピックはこの記事の範囲外ですが、完全に触れざるを得ません。

実際、メインページは、さまざまなプロジェクトからのデータのアグリゲーターです。 以前は、プロジェクトから直接HTMLブロックを取得していました。 多くのプロジェクトがあり、問題を制御するのは非現実的です。 属性が閉じられていないか、その他のエラーが原因でページが崩れる可能性があります。

新しいメインページを開発する過程で、データの受信と変換を既に自分の側で行い、爆発の可能性を大幅に減らしました。



エゴール・ダイキンキン、

Mail.Ruホームページ開発チームおよびクロスポータルプロジェクトのリーダー



All Articles