Turbolinks—রেইলস ইউনিভার্সের সবচেয়ে ঘৃণিত শব্দগুলোর একটি।
হয়তো আপনি এটা ট্রাই করেছেন। একটি নতুন প্রজেক্টে বা বিদ্যমান অ্যাপে turbolinks ইনক্লুড করেছেন। এবং যথেষ্ট শীঘ্রই অ্যাপটি غريبة ভাবে ব্যর্থ হতে শুরু করল। ভালো খবর হচ্ছে ফিক্সও ততোই সহজ—turbolinks অফ কর দিন।
...কিন্তু কিছু কোম্পানি এটাকে কাজ করিয়ে ফেলেছে। Honeybadger-এ আমরা এটাকে কাজ করিয়েছি—এবং আমরা ক génie নই।
উত্তরটা এত সহজ যে আমি বলতে অসম্ভব প্রায়। কিন্তু Ruby Nation এবং Madison+Ruby-এ এই বিষয়ে টক দিয়ে দেখলাম মানুষ এই টপিক হেল্পফুল পাচ্ছে। তাই চলুন গভীরে যাই।
Turbolinks এবং PJAX কীভাবে কাজ করে

Turbolinks এবং PJAX মূলত একইভাবে কাজ করে। সেগুলো এত সিমিলার যে, আমি এখন থেকে শুধু PJAX বলব। :)
আপনি PJAX কে দুটি পেজ রিকোয়েস্টের টার্মে বুঝতে পারেন। প্রথমবার একজন ইউজার একটি পেজ রিকোয়েস্ট করলে, সেটি যেকোনো অন্য "ট্রেডিশনাল" রেইলস পেজের মতো সার্ভ হয়। কিন্তু যখন ইউজার একটি PJAX-ইনাবলড লিংকে ক্লিক করে, কিছু স্পেশল হয়। পুরো পেজ রিলোড না হয়ে, শুধু পেজের একটি অংশ আপডেট হয়। এটি AJAX দিয়ে করা হয়।
এটি আমাদের সিঙ্গেল পেজ অ্যাপের অনেক সুবিধা দেয়, কিছু চ্যালেঞ্জ এড়িয়ে যেতে:
- PJAX অ্যাপগুলো প্রায় সিঙ্গেল পেজ অ্যাপের মতো স্ন্যাপি লাগে, কারণ পেজটি প্রতি রিকোয়েস্টে সম্পূর্ণ রিলোড হয় না।
- ফ্রন্ট-এন্ড এবং ব্যাক-এন্ড ডেভেলপমেন্টের জন্য একই স্ট্যাক ব্যবহার করতে পারেন।
- PJAX অ্যাপগুলো ডিফল্টভাবে গ্রেসফুলি ডিগ্রেড করে যখন ইউজার JS ডিজেবল করে।
- PJAX অ্যাপগুলো সহজে অ্যাকসেসিবল এবং SEO-ফ্রেন্ডলি বানানো যায়।
Turbolinks ইমপ্লিমেন্টেশন
PJAX-এর জন্য অনেক লাইব্রেরি আছে যা হеви লিফ্টিং করে। Turbolinks সম্ভবত সবচেয়ে পরিচিত। সেটআপ করা শুধু Gemfile-এ turbolinks জেম ইনক্লুড করার বিষয়:
gem 'turbolinks'
...এবং app/assets/javascripts/application.js-এ JS ইনক্লুড করা:
//= require turbolinks
এখন আপনার অ্যাপ রিলোড করলে, সব ইন্টারনাল লিংক turbolink হবে। আপনি যখন সেগুলোর ওপর ক্লিক করবেন, নতুন পেজ AJAX দিয়ে রিকোয়েস্ট হবে এবং কারেন্ট ডকুমেন্টে ইনসার্ট হবে।
jquery-pjax ইমপ্লিমেন্টেশন
Honeybadger-এ আমরা একটি PJAX লাইব্রেরি ব্যবহার করি যা অরিজিনালি GitHub-এ ডেভেলপ করা হয়েছিল। এটি Turbolinks-এ চেয়ে কিছুটা বেশি কনফিগারেশন চায়, কিন্তু এটি আরও ফ্লেক্সিবল।
Turbolinks-এর মত সব লিংক PJAX मानা না করে, এটি আপনাকে কন্ট্রোল দেয়। এটি আপনাকে কন্ট্রোল দেয় পেজের কোথায় PJAX কন্টেন্ট ইনসার্ট হবে সেটিও।
প্রথমে আমার HTML-এ একটি কন্টেইনার যোগ করতে হবে:
<div class="container" id="pjax-container">
Go to <a href="/page/2">next page</a>.
</div>
এখন PJAX লিংক সেটআপ করি:
$(document).pjax('a', '#pjax-container')
শেষে, আমি রেইলসকে বলব PJAX রিকোয়েস্টে লেয়াউট রেন্ডার না করতে। আপনি এমন কিছু করতে হবে, নাহলে আপনার হেডার এবং ফুটার ডুপ্লিকেট হবে। অর্থাৎ আপনার ওয়েবসাইট Inception-এর মতো দেখাবে।
def index
if request.headers['X-PJAX']
render layout: false
end
end
এটা সহজ নয়!
ঠিক আছে, এটি আমি যা বললাম তার চেয়ে কিছুটা বেশি জটিল। মূলত একটি যুগান্তকারী পিটফলের কারণে যা খুব কম লোক আলোচনা করে।
যখন আপনার DOM প্রতি পেজ লোডে ক্লিয়ার না হয়, এর মানে আপনার ট্রেডিশনাল রেইলস অ্যাপে যে JS কাজ করতো, এখন সেটি খুব غريبة ভাবে ভাঙতে শুরু করে।
এর কারণ হচ্ছে আমাদের অনেকের JS লিখার উপায় আকস্মিক নেমিং কনফ্লিক্টকে উত্সাহিত করে। সবচেয়ে ভয়াবহ Schuld একটি সিম্পল jQuery সিলেক্টর।
// আমি নিরাপদ দেখি, কিন্তু আমি নই!
$(".something")
পেজ রিলোড না হলে JS লিখা
কনফ্লিক্ট হলো পেজ রিলোড না হলে JS লিখার নম্বর ১ সমস্যা। সবচেয়ে 이상한, ডিবাগ করা কঠিন সমস্যাগুলো ঘটে যখন JS এমন HTML ম্যানিপুলেট করে যেটি সেটি স্পর্শ করার জন্য বানানো হয়নি। উদাহরণস্বরূপ, একটি সাধারণ jQuery ইভেন্ট হ্যান্ডলার দেখুন:
$(document).on("click", ".hide-form", function() {
$(".the-form").hide();
});
এটি পুরোপুরি যুক্তিসঙ্গত, যদি এটি শুধু একটি পেজে চলে। কিন্তু যদি DOM কখনো রিলোড না হয়, তবে কেবল সময়ের প্রশ্ন কেউ আসবে এবং আরেকটি এলিমেন্ট যোগ করবে .hide-form ক্লাসের সাথে। এখন আপনার একটি কনফ্লিক্ট আছে।
এমন কনফ্লিক্ট হয় যখন আপনার অনেক গ্লোবাল রেফারেন্স থাকে। আপনি গ্লোবাল রেফারেন্স কীভাবে কমাতে পারেন? নেমস্পেস ব্যবহার করুন।
সিলেক্টর নেমস্পেসিং
নিচের রুবি ক্লাসে, আমরা একটি গ্লোবাল নাম—ক্লাসের নাম—ব্যবহার করি অনেক মেথড নাম লুকাতে।
# একটি গ্লোবাল নাম দুটি মেথড নাম লুকাচ্ছে
class MyClass
def method1
end
def method2
end
end
যদিও DOM এলিমেন্টের জন্য বিল্ট-ইন নেমস্পেসিং সাপোর্ট নেই (কমপক্ষে ES6 WebComponents আসার আগে পর্যন্ত) কিন্তু কোডিং কনভেনশন দিয়ে নেমস্পেস সিমুলেট করা সম্ভব।
উদাহরণস্বরূপ, কল্পনা করুন আপনি একটি ট্যাগ এডিটিং উইজেট ইমপ্লিমেন্ট করতে চান। নেমস্পেসিং ছাড়া, এটা কিছু এইরকম দেখাবে। লক্ষ্য করুন তিনটি গ্লোবাল রেফারেন্স আছে।
// <input class="tags" type="text" />
// <button class="tag-submit">Save</button>
// <span class="tag-status"></span>
$(".tag-submit").click(function(){
save($(".tags").val());
$(".tag-status").html("Tags were saved");
});
তবে, একটি "নেমস্পেস" তৈরি করে এবং সকল এলিমেন্ট লুকআপ সেটির সাপেক্ষে রিলেটিভ করে, আমরা গ্লোবাল রেফারেন্সের সংখ্যা একে নামাতে পারি। আমরা কয়েকটি ক্লাসও বাদ দিয়ে দিয়েছি।
// <div class="tags-component">
// <input type="text" />
// <button>Save</button>
// <span></span>
// </div>
$container = $("#tags-component")
$container.on("click", "button", function(){
save($container.find("input").val());
$container.find("span").html("Tags were saved");
});
আমি বলেছিলাম এটি সহজ।
উপরের উদাহরণে, আমি আমার DOM এলিমেন্টগুলো নেমস্পেস করলাম সেগুলকে একটি কন্টেইনারের ভেতরে রাখে যেটার ক্লাস "tags-component"। সেই নির্দিষ্ট নামে কিছু স্পেশাল নেই। কিন্তু যদি আমি একটি নেমিং কনভেনশন অ্যাডাপ্ট করি যেখানে প্রতিটি নেমস্পেস কন্টেইনারের ক্লাস "-component" দিয়ে শেষ হয়, তবে কিছু খুব আকর্ষণীয় জিনিস ঘটে।
ভুল গ্লোবাল সিলেক্টর এক নজরে পাঠানো
যদি আপনি শুধু কম্পোনেন্টগুলোর গ্লোবাল সিলেক্টর আলো করুন, এবং সব কম্পোনেন্টের ক্লাস "-component" দিয়ে শেষ হয়, তবে আপনি এক নজরে দেখতে পারেন আপনার কোথাও খারাপ গ্লোবাল সিলেক্টর আছে কিনা।
// খারাপ
$(".foo").blah()
// ঠিক আছে
$(".foo-component").blah()
এইচটিএমএল কন্ট্রোল করা JS খুঁজে বের করা সহজ
আমাদের ইন্টারঅ্যাক্টিভ ট্যাগ ফর্মটি পুনরাবৃত্তি করি। আপনি ফর্মের HTML লিখেছেন। এখন আপনাকে কিছু JS এবং CSS যোগ করতে হবে। কিন্তু আপনি সেই ফাইলগুলো কোথায় রাখবেন? خوشকস্মত, আপনার নেমস্পেসের জন্য একটি নেমিং স্কিম থাকলে, এটি সহজে JS এবং CSS ফাইলের জন্য একটি নেমিং স্কিমে ট্রান্সলেট হয়। ডিরেক্টরি ট্রি কিছু এইরকম দেখতে পারে।
.
├── javascripts
│ ├── application.coffee
│ └── components
│ └── tags.coffee
└── stylesheets
├── application.scss
└── components
└── tags.scss
অটোমেটিক ইনিশিয়ালাইজেশন
একটি ট্রেডিশনাল ওয়েব অ্যাপে, সব JS পেজ লোডে ইনিশিয়ালাইজ করা স্বাভাবিক। কিন্তু PJAX এবং Turbolinks-এ, এলিমেন্টগুলো DOM-এ বারবার যোগ ও বাদ হয়। এর কারণে, যদি আপনার কোড অটোমেটিক্যালি ডিটেক্ট করতে পারে নতুন কম্পোনেন্ট DOM-এ এলে, এবং ফ্লাইতে সেগুলোর জন্য প্রয়োজনীয় JS ইনিশিয়ালাইজ করতে পারে, তবে এটিটি খুব বেনিফিশিয়াল।
একটি কনসিস্টেন্ট নেমিং স্কিম, এটিকে খুব সহজ করে দেয়। অটো-ইনিশিয়ালাইজেশনের জন্য আপনি অনেক পদ্ধতি अपना পারেন। এখানে একটি:
Initializers = {
tags: function(el){
$(el).on("click", "button", function(){
// setup component
});
}
// অন্যান্য ইনিশিয়ালাইজার এখানে যোগ করুন
}
// হ্যান্ডলার যা প্রতি নরমাল এবং pjax পেজ লোডে কল হয়
$(document).on("load, pjax:load", function(){
for(var key in Initializers){
$("." + key + "-component").each(Initializers[key]);
}
})
এটি আপনার CSS-ও ভালো করে!
JavaScript একমাত্র কনফ্লিক্টের উৎস নয় একটি ওয়েব পেজে। CSS হতে পারে আরও খারাপ! خوشকস্মত, আমাদের নেমস্পেসিং সিস্টেম CSS কনফ্লিক্ট ছাড়া লিখতেও অনেক সহজ করে দেয়। SCSS-এ এটি আরও সুন্দর:
.tags-component {
input { ... }
button { ... }
span { ... }
}