ایجاد: ۱۹:۴۷ ۱۳۹۲/۱۰/۲۰
ویرایش: ۱۹:۱۵ ۱۳۹۵/۶/۲۵

آموزش‌ها

»

بکارگیری pushState و replaceState در برنامه نویسی سایت های ای جکسی

زمانی که کاربر بر روی یک لینک از سایت شما کلیک می‌کند تا به یک صفحه دیگر برود، آدرس صفحه تغییر می‌کند و کل محتویات html آن دوباره از سرور فراخوانی می‌شود و حتی اگر asset های استاتیک (js, css, font و ...) کش شده باشند باز هم نیاز به اجرا و قرار گرفتن روی حافظه (ram) کلاینت دارند در حالی که در بسیاری از اوقات، صفحه فعلی با صفحه‌ی بعدی ممکن است به مقدار مثلا 10~20 درصد از html صفحه تفاوت داشته باشد.

با توجه به توضیحات فوق، کاملا منطقی است که ما با استفاده از ajax، تنها بخشی از صفحه که نیاز به تغییر دارد را لود و جایگزین کنیم تا درخواست محتوای آن لینک جدید، سریعتر نمایش داده شود. (به سه دلیل سریع‌تر می‌شود: 1. پردازش کمتر سرور، 2. پهنای باند مصرفی کمتر سرور و کلاینت، 3. قرار داشتن js و css و سایر موارد استاتیک روی Ram کلاینت)

اما لود کردن به صورت Ajax این مشکل را دارد که آدرس صفحه عوض نمی‌شود (یا حداکثر هش‌تگ آخر آدرس عوض می‌شود) که این مساله برای سئو مشکل‌ساز می‌شود. حتی اگر بخواهید از راه‌حل گوگل برای حل مشکل صفحات ای‌جکسی هم بهره ببرید، باز هم علاوه بر نیاز به برنامه‌نویسی و کد بیشتر، شکل ظاهرا آدرس صفحات شما جالب و زیبا نخواهد بود.
پس راه حل چیست؟ آیا روش مناسبی وجود دارد؟!

تاریخچه آبجکت history

آبجکت history از سال‌ها پیش موجود بود (حتی زمان IE6) و متدهایی مانند back و forward داشت که معادل فشردن آیکون back و forward در بالای مرورگر بود:

window.history.back();
window.history.forward();
همچنین ما از قدیم‌الایام! متد go را داشتیم که به وسیله آن می‌توانستیم یک یا چند مرحله به عقب یا جلو برویم:
window.history.go(-2);
window.history.go(3);
همچنین خصیصه (property) ای با نام length داشتیم که تعداد URL های موجود در آبجکت history صفحه فعلی را به ما برمی‌گرداند. یکی از کاربردهای این پراپرتی برای استفاده با متد go بود.
var moves = window.history.length - 1;
window.history.go(-moves);
هر 4 مورد فوق در html4 و از سال‌ها قبل موجود بود. (سایت w3school را ببینید) اما در html5 متدهای جدیدی به آن اضافه شده است که در ادامه به بررسی آنها می‌پردازیم.

استفاده از HTML5 history API

امروزه اکثر کاربران از براوزرهایی استفاده می‌کنند که HTML5 و ویژگی‌هایش را پشتیبانی می‌کند. یکی از قابلیت‌های html5 ، اضافه شدن برخی متدهای مفید (مثل pushState) به آبجکت history است امکان بازی کردن با آدرس نوار آدرس را فراهم می‌کند. با کمک این متدها، شما می‌توانید آدرس موجود در نوار آدرس را (که کاربر تایپ کرده) عوض کنید بدون اینکه صفحه از نو بارگذاری شود. این تغییر آدرس در history مرورگر کاربر می‌ماند و کاربر می‌تواند به راحتی با دکمه next و forward براوزر خود کار کند.

سوالی که اینجا مطرح می‌شود این است که اگر براوزر کاربر از آبجکت history و متد pushState و replaceState پشتیبانی نکند یا javascript غیرفعال باشد، چه اتفاقی خواهد افتاد؟!
این مساله توسط برنامه‌نویسی به سادگی قابل کنترل است. هم در سمت سرور و هم در سمت کلاینت.
مثلا برای کنترل در سمت سرور کافی است برنامه چک کند که درخواست یک صفحه مثل /about توسط Ajax ارسال شده یا عادی؟

<?php
if((isset($_SERVER['HTTP_X_REQUESTED_WITH']) AND strtolower($_SERVER['HTTP_X_REQUESTED_WITH'])=='xmlhttprequest'))
{
    صفحه از طریق ای جکسی درخواست شده
    لذا فقط متن اصلی درخواست شده را ارسال میکنیم
}
else
{
    صفحه به صورت عادی درخواست شده
    تولید اچ تی ام ال کامل صفحه و ارسال آن به براوزر
}

مقالات مختلفی در خصوص کار با آبجکت history و متد pushState/replaceState نوشته شده که در انتهای همین مطلب برخی از آنها لینک داده شده‌اند.

نگاهی به متدهای pushState, replaceState

این متدها که در html5 اضافه شده، امکان اضافه کردن و ویرایش مقادیر موجود در history را برای ما فراهم کرده‌اند. هر دو متد دارای 3 ورودی هستند که عینا مشابه هم هستند:
window.history.pushState(stateObj, title, URL);
window.history.replaceState(stateObj, title, URL);

ورودی اول یک آبجکت حاوی اطلاعات دلخواه شما است که به صورت سریالایز شده در hard کلاینت (نه ram) ذخیره می‌شود. هر زمان که بخواهیم می‌توانیم با خواندن پراپرتی state از آبجکت history به مقدار این آبجکت (stateObj) دست پیدا کنیم.
زمانی که یک صفحه لود می‌شود، آبجکت مذکور خالی است مگر آنکه قبلا توسط pushState یا replaceState آبجکتی در آن history آن ذخیره شده باشد که در اینصورت پس از ریستارت مرورگر، آن آبجکت دوباره از هارد فراخوانی شده و در state آن صفحه قرار می‌گیرد.

ورودی دوم pushState/replaceState فعلا بلااستفاده است اما بهتر است آن را در کدهای خود خالی نگذارید تا اگر در ورژن‌های بعدی نیاز شد، برنامه شما دچار مشکل نشود.

ورودی سوم URL است که مهم‌ترین بخش کار است.
زمانی که یک URL را pushState می‌شود، چه اتفاقی می‌افتد؟ آیا آن آدرس بارگذاری می‌شود؟
خیر! زمانی که یک آدرس را pushState می‌کنید، در نوار آدرس آن آدرس بجای آدرس صفحه فعلی قرار می‌گیرد اما لود نمی‌شود.
فرض کنید ما در صفحه http://9px.ir/page1.html هستیم و در این صفحه، کد جاوا اسکریپت زیر را اجرا می‌کنیم:

var stateObj = { name: "علی‌رضا معظمی" };
history.pushState(stateObj, "صفحه دوم", "page2.html");

خب در اینجا آدرس بالای صفحه از page1 به page2 تغییر می‌کند اما چیزی لود نمی‌شود و حتی وجود داشتن صفحه مذکور چک نمی‌شود. (یعنی هیچ درخواستی از کلاینت به سرور ارسال نمی‌شود)
فرض کنید کاربر پس از pushState شدن آدرس page2 ، به سایت دیگری برود (مثلا آدرس google.com را در نوار آدرس تایپ کند و وارد سایت گوگل شود) حال اگر دکمه back بالای مرورگر خود را بزند، به صفحه قبل برمی‌گردد. یعنی به صفحه‌ای که آدرس بالای آن page2 است اما محتویات آن مربوط به page1 است.

نکته 1: URL ذکر شده برای pushState یا replaceState باید مربوط به همان دامنه باشد و نمی‌توان url مربوط به سایت دیگری را درج کرد و الا منجر به خطا می‌شود.

نکته 2: در روش استفاده از هش‌تگ، نمی‌توان اطلاعات چندانی ذخیره کرد (فقط در جلوی هش‌تگ می‌توان اطلاعات محدودی درج کرد) اما در روش history state می‌توان یک آبجکت دلخواه تو در تو شامل اطلاعات لازم درج کرد. منتها توجه کنید که این آبجکت دلخواه پوش شده نیز اگر بسیار بزرگ باشد (مثلا بزرگتر از 640KB برای مرورگر فایرفاکس) باز هم منجر به بروز خطا می‌شود.

نکته 3: همانطور که در بالا گفته شد، به صرف pushState یا replaceState هیچ چیزی بارگذاری نمی‌شود. اما رویداد (event) ی به نام popstate فراخوانی می‌شود و در این رویداد، شما می‌توانید محتوای موردنظر را به صورت ای‌جکسی فراخوانی کنید.

چند پلاگین کاربردی برای راحتی استفاده از pushState

  • pjax
    pjax پلاگینی خوب برای کار با pushState و ajax است که کار با آن نیز ساده است. در سمت کلاینت کافی است به لینک‌هایی که می‌خواهید با pjax لود شود، کلاس خاصی بیفزایید و سپس یک خط کد بنویسد که لینک‌هایی با فلان کلاس، به وسیله pjax لود شود. در سمت سرور نیز تنظیم کوچکی لازم است که اگر هدر x-pjax ارسال شده بود، صفحه را بدون فریم ارسال کند. (مشابه if ذکر شده در کد اول صفحه)
  • ajaxify
    این کد نیز مشابه کد فوق است و سازنده خود در سایت وردپرسی‌اش از این پلاگین به صورت آنلاین استفاده کرده که قدرت و کاربرد عملی آن را به دیگران نشان دهد.
    این پلاگین به صورت مشهودی از pjax قدرتمندتر است و چندین قابلیت خوب نیز اخیرا به آن اضافه شده است. یکی از این قابلیت‌ها prefetch کردن لینک در صورت hover است که سرعت لود را بالاتر می‌برد. همچنین در این پلاگین لزومی به تغییر در سمت سرور نیست و این پلاگین خود css, js های مشترک و غیرمشترک صفحات را مدیریت می‌کند...
  • history.js
    همانطور که در ابتدای مطلب گفته شد، برای براوزرهایی که html5 را ساپورت نمی‌کنند، برنامه می‌تواند بدون pushState به صورت عادی (غیر ای‌جکسی) خود ادامه دهد. اما پلاگین history.js اینگونه عمل می‌کند که برای براوزرهای قدیمی، بجای آدرس حقیقی، آدرس با کمک هش‌تگ لود می‌کند و لذا برنامه در براوزرهای قدیمی نیز ای‌جکسی عمل خواهد کرد. بدین منظور پس از فراخوانی اسکریپت مربوطه، کافی‌ست از آبجکت History بجای آبجکت پیش‌فرض history استفاده کنید و نام متدهای این آبجکت جدید (History) نیز دقیقا مثل متدهای آبجکت اصلی (history) تعریف شده است.
  • Easyfood pageAccelerator
    این پلاگین شبیه pJax است که در بالا معرفی شد. اما هم استفاده از آن ساده‌تر است و هم سبک‌تر. فقط 2.2 کیلوبایت!

چند لینک مرتبط و مفید در این زمینه:

در مطلب بسیار عالی history.pushState and jQuery نیز می‌توانید یک آموزش مرحله به مرحله کامل و عملی و خوب برای استفاده از pushState بیابید.
یادداشت‌های مرتبط
  1. ساخت اپلیکیشن های کلاینتی با AngularJS گوگل
  2. کنترل رویدادهای صفحه کلید، موس و صفحات لمسی
  3. امنیت در انگولر جی اس
  4. حذف درخواست اضافه تصویر انیمیشن لودینگ
  5. همه آن چیزهایی که لازم است درباره ی فید بدانید
  6. افزودن جستجوی سایت شما به براوزر کاربر
  7. نکاتی پیرامون SEO (بهینه سازی سایت جهت موتورهای جستجو)
  8. www در ابتدای آدرس سایت، خوب است یا بد؟
  9. جلوگیری از ضربه محتوای تکراری به رتبه سایت در موتورهای جستجو

مدیریت

نام وسیله

اعتبار ورود

نام کاربری

رمز عبور

رمز یکبارمصرف

... لیست تمام آموزش‌ها

تبلیغات

ربات‌های تلگرامی:
مجموعه ربات‌های تلگرامی ما برای ساده‌تر کردن کار با تلگرام و مدیریت کانال
دامنه‌های فروشی:
دامنه‌های زیر مربوط به ما یا مشتریان ماست که قابل واگذاری هستند. در صورت تمایل به داشتن یکی از آنها، با این اکانت تلگرامی مکاتبه نمایید.

دامنه‌هایی که می‌تواند مصرف تخصصی داشته باشد:
AnyDesk.ir, Firebase.ir, Angularjs.ir, 9px.ir alAdmin.ir, iQore.ir notion.ir, 3dn.ir,

دامنه‌هایی که می‌تواند مصرف فرهنگی/مذهبی داشته باشد:
mavaez.ir, 2aha.ir, babolelm.ir, mahjoor.ir

دامنه‌هایی که می‌تواند مصرف شخصی/عمومی داشته باشد:
azizami.ir, 90blog.ir