ঠিক আছে, আমরা এখন React ইকোসিস্টেমের একটি গুরুত্বপূর্ণ ধারণা—ভার্চুয়াল DOM (Virtual DOM)—নিয়ে আলোচনা শুরু করব। এই সিরিজটি তাদের জন্য যারা ইতোমধ্যে ব্রাউজার DOM সম্পর্কে বিস্তারিত জেনেছেন এবং এখন React-এর খুঁটিনাটি বিষয়গুলো বুঝতে চান। আমরা মোট দুটি পর্বে ভার্চুয়াল DOM নিয়ে আলোচনা করব।


ভার্চুয়াল DOM: React-এর গতির রহস্য (পর্ব ১) 🚀

আমরা আগের সিরিজগুলোতে দেখেছি ব্রাউজার DOM কীভাবে কাজ করে এবং জাভাস্ক্রিপ্ট ব্যবহার করে আমরা কীভাবে DOM ম্যানিপুলেট করতে পারি। আমরা এও জেনেছি যে, ঘন ঘন এবং সরাসরি DOM ম্যানিপুলেশন ওয়েব পেজের পারফরম্যান্সের জন্য ব্যয়বহুল হতে পারে, কারণ প্রতিটি পরিবর্তনের জন্য ব্রাউজারকে Reflow এবং Repaint করতে হতে পারে।

এই সমস্যার একটি চমৎকার সমাধান নিয়ে এসেছে React, আর তা হলো ভার্চুয়াল DOM। আজকের পর্বে আমরা জানব ভার্চুয়াল DOM কী, কেন এটি এত গুরুত্বপূর্ণ এবং এটি কীভাবে কাজ করে।

ভার্চুয়াল DOM আসলে কী? 🤔

সহজ ভাষায় বলতে গেলে, ভার্চুয়াল DOM হলো ব্রাউজার DOM-এর একটি হালকা ওজনের অনুলিপি বা অ্যাবস্ট্রাকশন, যা জাভাস্ক্রিপ্ট অবজেক্ট হিসেবে মেমোরিতে থাকে। যখন আমরা React ব্যবহার করে কোনো UI (ইউজার ইন্টারফেস) তৈরি করি, React প্রথমে এই ভার্চুয়াল DOM-এ সেই UI-এর একটি প্রতিচ্ছবি তৈরি করে। এটি আসল DOM-এর মতো শক্তিশালী নয়, কিন্তু এটি খুব দ্রুত তৈরি এবং পরিবর্তন করা যায় কারণ এটি সরাসরি ব্রাউজারের রেন্ডারিং ইঞ্জিনের সাথে যুক্ত নয়।

একটি বাস্তব জীবনের উদাহরণ: ধরুন, আপনি একটি বাড়ির নকশা করছেন। সরাসরি ইট-সিমেন্ট দিয়ে বাড়ি তৈরি করে আবার ভেঙে নতুন করে তৈরি করাটা যেমন সময়সাপেক্ষ ও ব্যয়বহুল, তেমনি সরাসরি ব্রাউজার DOM-এ ঘন ঘন পরিবর্তন করাটাও পারফরম্যান্সের জন্য ক্ষতিকর।

ভার্চুয়াল DOM এখানে একটি ব্লুপ্রিন্ট বা মডেলের মতো কাজ করে। আপনি প্রথমে কাগজে (মেমোরিতে) আপনার বাড়ির (UI) নকশা (ভার্চুয়াল DOM) তৈরি করেন। যদি কোনো পরিবর্তন দরকার হয়, আপনি সহজেই সেই কাগজে পরিবর্তন করতে পারেন। যখন আপনি চূড়ান্ত নকশা নিয়ে সন্তুষ্ট, তখন সেই নকশা দেখে আসল বাড়িটি (ব্রাউজার DOM) তৈরি করা হয়। React ঠিক এই কাজটিই করে।

কেন ভার্চুয়াল DOM? এর সুবিধা কী? ✨

ভার্চুয়াল DOM ব্যবহারের প্রধান কারণ হলো পারফরম্যান্স অপটিমাইজেশন

  1. কম DOM ম্যানিপুলেশন: React সরাসরি ব্রাউজার DOM পরিবর্তন না করে প্রথমে ভার্চুয়াল DOM-এ পরিবর্তন আনে। এরপর React দেখে যে আগের ভার্চুয়াল DOM এবং বর্তমান ভার্চুয়াল DOM-এর মধ্যে ঠিক কী কী পরিবর্তন হয়েছে (এই প্রক্রিয়াকে Diffing Algorithm বলে)। শুধুমাত্র যেটুকু পরিবর্তন দরকার, ঠিক সেটুকুই আসল ব্রাউজার DOM-এ প্রয়োগ করা হয়। এর ফলে অপ্রয়োজনীয় DOM ম্যানিপুলেশন কমে যায়।
  2. ব্যাচ আপডেট (Batch Update): React একাধিক পরিবর্তনকে একসাথে ব্যাচ করে নেয় এবং একবারে আসল DOM-এ প্রয়োগ করে। এতে বারবার Reflow/Repaint হওয়ার সম্ভাবনা কমে।
  3. দ্রুত রেন্ডারিং: যেহেতু ভার্চুয়াল DOM জাভাস্ক্রিপ্ট অবজেক্ট, এটি নিয়ে কাজ করা আসল DOM-এর চেয়ে অনেক দ্রুত।

সরাসরি DOM ম্যানিপুলেশন বনাম ভার্চুয়াল DOM (Vanilla JS উদাহরণ)

চলুন, একটি সাধারণ উদাহরণ দিয়ে দেখি কীভাবে সরাসরি DOM ম্যানিপুলেশন ব্যয়বহুল হতে পারে। "Learn with Sumit"-এর এই উদাহরণটি আমরা ব্যবহার করব।

ব্যয়বহুল (Costly) DOM ম্যানিপুলেশন (স্লো পদ্ধতি):

/*
 * Title: Main Script file
 * Description: A vanilla JS Implementation of costly DOM manipulation vs efficient DOM manipulation
 * Author: Sumit Saha ( Learn with Sumit )
 * Date: 07/01/2023
 *
 */
 
// এই কোডের "slow" অংশটি নিচে দেখানো হলো
let containerSlow = document.querySelector(".container-slow"); // একটি নতুন কন্টেইনার ধরে নিচ্ছি
let incrementSlow = 0;
 
// স্লো পদ্ধতি: প্রতিবার লুপ চলার সময় DOM আপডেট হচ্ছে
while (incrementSlow < 10000) {
  // ছোট সংখ্যা দিয়ে পরীক্ষা করা ভালো, ব্রাউজার হ্যাং হতে পারে
  incrementSlow++;
  containerSlow.innerHTML += " " + incrementSlow; // মারাত্মক পারফরম্যান্স কিলার!
}

উপরের "স্লো" পদ্ধতিতে, while লুপের ভেতরে প্রতিবার containerSlow.innerHTML পরিবর্তন করা হচ্ছে। এর মানে হলো, প্রতিবার লুপ চলার সময় ব্রাউজারকে containerSlow-এর সম্পূর্ণ কনটেন্ট নতুন করে পার্স করতে হচ্ছে, DOM ট্রি আপডেট করতে হচ্ছে এবং সম্ভবত Reflow/Repaint করতে হচ্ছে। যদি 10000 বার লুপ চলে, তাহলে 10000 বার DOM ম্যানিপুলেশন হচ্ছে, যা খুবই অদক্ষ।

তুলনামূলক দক্ষ (Efficient) DOM ম্যানিপুলেশন (ফাস্ট পদ্ধতি):

/*
 * Title: Main Script file
 * Description: A vanilla JS Implementation of costly DOM manipulation vs efficient DOM manipulation
 * Author: Sumit Saha ( Learn with Sumit )
 * Date: 07/01/2023
 *
 */
 
let arrayFast = [];
let incrementFast = 0;
let containerFast = document.querySelector(".container-fast"); // একটি নতুন কন্টেইনার ধরে নিচ্ছি
 
// ফাস্ট পদ্ধতি: প্রথমে ডেটা প্রস্তুত, তারপর একবার DOM আপডেট
while (incrementFast < 10000) {
  arrayFast.push(++incrementFast);
}
 
containerFast.innerHTML = arrayFast.join(" "); // একবার মাত্র DOM আপডেট

এই "ফাস্ট" পদ্ধতিতে, প্রথমে arrayFast নামের একটি জাভাস্ক্রিপ্ট অ্যারেতে সব সংখ্যা যোগ করা হচ্ছে। লুপ চলাকালীন কোনো DOM ম্যানিপুলেশন হচ্ছে না। সব ডেটা প্রস্তুত হওয়ার পর, arrayFast.join(" ") ব্যবহার করে পুরো স্ট্রিংটি তৈরি করে একবার মাত্র containerFast.innerHTML-এ সেট করা হচ্ছে। এর ফলে ব্রাউজারকে শুধুমাত্র একবার DOM পার্স এবং আপডেট করতে হচ্ছে। এটি আগের পদ্ধতির চেয়ে অনেক বেশি পারফরম্যান্ট।

React তার ভার্চুয়াল DOM এবং Diffing Algorithm ব্যবহার করে এই "ফাস্ট" পদ্ধতির মতো একটি অপটিমাইজড উপায়ে কাজ করে, তবে আরও স্মার্টলি।

** অপ্রয়োজনীয় (Redundant) DOM ম্যানিপুলেশন (Vanilla JS):**

আরেকটি উদাহরণ দেখি, যেখানে একটি লিস্ট আইটেম যোগ করার সময় পুরো লিস্টটি রি-রেন্ডার করা হচ্ছে, যা অপ্রয়োজনীয়।

/*
 * Title: Main Script file
 * Description: A vanilla JS Implementation of reduntant DOM manipulation
 * Author: Sumit Saha ( Learn with Sumit )
 * Date: 02/08/2021
 *
 */
 
Array.prototype.myPush = function (...a) {
  // কাস্টম পুশ মেথড
  this.push(a[0]);
  init(); // প্রতিবার পুশ করার পর পুরো লিস্ট রি-রেন্ডার
};
 
const display = document.getElementById("fruits");
const button = document.querySelector("#button"); // HTML-এ একটি বাটন ও ইনপুট ফিল্ড থাকতে হবে
// const inputField = document.getElementById("input"); // HTML-এ একটি ইনপুট ফিল্ড
 
let fruits = ["mango", "guava", "apple", "orange"]; // বানান ঠিক করা হলো
 
const init = function () {
  document.getElementById("fruits").innerHTML = ""; // পুরো লিস্ট খালি করা হচ্ছে
  fruits.sort().forEach((fruit) => {
    // লিস্ট সর্ট করে প্রতিবার নতুন করে আইটেম যোগ
    let item = document.createElement("li");
    item.textContent = fruit;
    document.getElementById("fruits").appendChild(item);
  });
};
 
const addItem = function () {
  const inputValue = document.getElementById("input").value; // ইনপুট থেকে ভ্যালু নেওয়া
  if (inputValue) {
    // যদি ইনপুট খালি না থাকে
    fruits.myPush(inputValue); // myPush কল করার ফলে init() আবার কল হবে
    document.getElementById("input").value = ""; // ইনপুট ফিল্ড খালি করা
  }
};
 
// button.addEventListener('click', addItem); // বাটনে ইভেন্ট লিসেনার যোগ করা
 
init(); // প্রাথমিকভাবে লিস্ট দেখানো

এই কোডে, addItem ফাংশন কল হলে fruits.myPush() কল হয়, যা আবার init() ফাংশনটিকে কল করে। init() ফাংশনটি প্রথমে innerHTML = '' ব্যবহার করে পুরো <ul> লিস্টটি খালি করে, তারপর fruits অ্যারের প্রতিটি আইটেমের জন্য নতুন করে <li> তৈরি করে DOM-এ যোগ করে। এর মানে হলো, একটি মাত্র নতুন ফল যোগ করার জন্য পুরো ফলের তালিকাটিকে DOM থেকে মুছে আবার নতুন করে তৈরি করা হচ্ছে। এটি খুবই অদক্ষ, বিশেষ করে যখন লিস্টে অনেক আইটেম থাকবে।

React এই ধরনের অপ্রয়োজনীয় রি-রেন্ডারিং এড়িয়ে চলে। এটি ভার্চুয়াল DOM-এ পরিবর্তনগুলো ট্র্যাক করে এবং শুধুমাত্র যেটুকু পরিবর্তন দরকার (যেমন, নতুন <li> আইটেমটি যোগ করা) সেটুকুই আসল DOM-এ প্রয়োগ করে।


আজকের পর্বে আমরা ভার্চুয়াল DOM-এর মৌলিক ধারণা, এর সুবিধা এবং কীভাবে এটি সরাসরি DOM ম্যানিপুলেশনের চেয়ে বেশি পারফরম্যান্ট, তা Vanilla JS-এর উদাহরণের মাধ্যমে বোঝার চেষ্টা করলাম।

পরবর্তী পর্বে আমরা দেখব React কীভাবে ভার্চুয়াল DOM ব্যবহার করে একটি অ্যাপ্লিকেশন তৈরি করে, React-এর Diffing Algorithm কীভাবে কাজ করে এবং একটি React কোডের উদাহরণ দিয়ে ভার্চুয়াল DOM-এর কার্যকারিতা আরও গভীরভাবে বোঝার চেষ্টা করব। সাথেই থাকুন!



ভার্চুয়াল DOM: React-এর গতির রহস্য (পর্ব ২ - চূড়ান্ত পর্ব) ⚡

আগের পর্বে আমরা ভার্চুয়াল DOM-এর মৌলিক ধারণা, এর সুবিধা এবং এটি কীভাবে সরাসরি DOM ম্যানিপুলেশনের চেয়ে বেশি পারফরম্যান্ট, তা জেনেছি। এই চূড়ান্ত পর্বে আমরা আরও গভীরে যাব। আমরা দেখব React কীভাবে ভার্চুয়াল DOM ব্যবহার করে অ্যাপ্লিকেশন তৈরি করে, React-এর Diffing Algorithm কীভাবে কাজ করে, এবং একটি React কোডের উদাহরণের মাধ্যমে এর কার্যকারিতা বিশ্লেষণ করব। এছাড়াও, ব্রাউজারের রেন্ডারিং প্রসেস এবং পারফরম্যান্স অপটিমাইজেশনের জন্য কিছু টুলস নিয়েও আলোচনা করব।


ব্রাউজারের রেন্ডারিং প্রসেস: একটি সংক্ষিপ্ত পরিচিতি 🖌️

ভার্চুয়াল DOM কেন এত কার্যকরী, তা বোঝার জন্য ব্রাউজার কীভাবে একটি ওয়েব পেজ রেন্ডার করে, তার মৌলিক ধাপগুলো জানা জরুরি:

  1. DOM Tree তৈরি: ব্রাউজার HTML কোডকে পার্স করে একটি ট্রি-লাইক স্ট্রাকচার তৈরি করে, যাকে আমরা Document Object Model (DOM) বলি।
  2. CSSOM Tree তৈরি: একইভাবে, CSS কোড পার্স করে CSS Object Model (CSSOM) তৈরি হয়। এটি DOM-এর প্রতিটি নোডের স্টাইল তথ্য ধারণ করে।
  3. Render Tree তৈরি: DOM এবং CSSOM ট্রি একত্রিত করে Render Tree তৈরি করা হয়। এই ট্রি-তে শুধুমাত্র দৃশ্যমান (visible) কন্টেন্টগুলোই থাকে (যেমন, display: none করা এলিমেন্টগুলো বাদ পড়ে)।
  4. Layout (বা Reflow): Render Tree-এর প্রতিটি নোডের জ্যামিতিক অবস্থান (যেমন, আকার, পজিশন) গণনা করা হয়।
  5. Painting (বা Rasterizing): Layout ধাপে প্রাপ্ত তথ্যের উপর ভিত্তি করে পিক্সেলগুলোকে স্ক্রিনে আঁকা হয়।

যখন DOM-এ কোনো পরিবর্তন আসে (যেমন, একটি এলিমেন্ট যোগ বা বিয়োগ করা, স্টাইল পরিবর্তন), ব্রাউজারকে উপরের ধাপগুলোর (বিশেষ করে Layout এবং Painting) কিছু অংশ বা পুরোটাই পুনরায় করতে হতে পারে। এই প্রক্রিয়াটিকেই আমরা Reflow (যখন লেআউট পরিবর্তন হয়) এবং Repaint (যখন শুধু স্টাইল পরিবর্তন হয়, লেআউট নয়) বলি। ঘন ঘন Reflow/Repaint পারফরম্যান্সের জন্য খুবই ব্যয়বহুল।


React এবং ভার্চুয়াল DOM: কীভাবে কাজ করে? ⚙️

React সরাসরি ব্রাউজার DOM-কে ম্যানিপুলেট না করে একটি মধ্যবর্তী স্তর হিসেবে ভার্চুয়াল DOM ব্যবহার করে।

  1. প্রাথমিক রেন্ডার: যখন একটি React অ্যাপ্লিকেশন প্রথমবার লোড হয়, React তার কম্পোনেন্টগুলোর উপর ভিত্তি করে একটি সম্পূর্ণ ভার্চুয়াল DOM ট্রি তৈরি করে এবং সেই অনুযায়ী আসল ব্রাউজার DOM তৈরি করে।
  2. স্টেট (State) পরিবর্তন: যখন অ্যাপ্লিকেশনের কোনো ডেটা বা স্টেট পরিবর্তন হয় (যেমন, ব্যবহারকারী একটি বাটনে ক্লিক করল, যার ফলে কিছু ডেটা আপডেট হলো), React একটি নতুন ভার্চুয়াল DOM ট্রি তৈরি করে।
  3. Diffing (পার্থক্য নিরূপণ): এখন React-এর কাছে দুটি ভার্চুয়াল DOM ট্রি আছে: আগের স্টেট পরিবর্তনের আগের ভার্চুয়াল DOM এবং বর্তমান স্টেট পরিবর্তনের পরের নতুন ভার্চুয়াল DOM। React এই দুটি ভার্চুয়াল DOM-এর মধ্যে পার্থক্য খুঁজে বের করে। এই প্রক্রিয়াটিকেই Diffing Algorithm বা Reconciliation বলা হয়।
  4. ব্যাচড আপডেট (Batched Update): React শুধুমাত্র যেটুকু পরিবর্তন হয়েছে (diff), সেটুকুই আসল ব্রাউজার DOM-এ প্রয়োগ করে, তাও আবার যতটা সম্ভব অপটিমাইজড এবং ব্যাচড (একসাথে একাধিক পরিবর্তন) উপায়ে। এর ফলে অপ্রয়োজনীয় DOM ম্যানিপুলেশন এড়ানো যায় এবং পারফরম্যান্স বাড়ে।

একটি রিয়েল লাইফ এনালজি: ধরুন, আপনি একটি বিশাল বইয়ের একটি পৃষ্ঠায় শুধুমাত্র একটি শব্দ পরিবর্তন করতে চান।

  • সরাসরি DOM ম্যানিপুলেশন (পুরোনো পদ্ধতি): আপনি পুরো পৃষ্ঠাটি ছিঁড়ে ফেলে, শব্দটি পরিবর্তন করে, আবার নতুন করে পুরো পৃষ্ঠাটি বইয়ে লাগাচ্ছেন।
  • ভার্চুয়াল DOM (React-এর পদ্ধতি): আপনি প্রথমে একটি ফটোকপিতে (ভার্চুয়াল DOM) শব্দটি পরিবর্তন করছেন। তারপর মূল বইয়ের পৃষ্ঠার সাথে ফটোকপির তুলনা করে দেখছেন শুধুমাত্র কোন শব্দটি পরিবর্তন হয়েছে। এরপর, আপনি খুব সাবধানে শুধুমাত্র সেই একটি শব্দই মূল বইয়ের পৃষ্ঠায় (আসল DOM) পরিবর্তন করছেন।

React-এর Diffing Algorithm: কিছু মূলনীতি 🧐

React-এর Diffing Algorithm দুটি অনুমানের উপর ভিত্তি করে একটি $O(n)$ কমপ্লেক্সিটির অ্যালগরিদম ব্যবহার করে, যা এটিকে খুব দ্রুত করে তোলে:

  1. ভিন্ন টাইপের দুটি এলিমেন্ট ভিন্ন ট্রি তৈরি করবে: যদি দুটি এলিমেন্টের টাইপ ভিন্ন হয় (যেমন, একটি <div> একটি <p> দিয়ে প্রতিস্থাপিত হয়), React আগের ট্রিটি সম্পূর্ণ ধ্বংস করে নতুন ট্রি তৈরি করবে।
  2. ডেভেলপার key প্রপের মাধ্যমে একই রকম এলিমেন্টের লিস্টে স্থিতিশীলতা নির্দেশ করতে পারে: যখন একই রকম চাইল্ড এলিমেন্টের একটি লিস্ট রেন্ডার করা হয় (যেমন, একটি <ul> এর ভেতরে অনেকগুলো <li>), React ডিফল্টভাবে লিস্টের আইটেমগুলোকে তাদের ক্রম (index) অনুযায়ী মিলানোর চেষ্টা করে। কিন্তু যদি লিস্টের আইটেমগুলো পুনর্বিন্যাসিত হয়, যোগ হয় বা মুছে যায়, এটি অদক্ষ হতে পারে। key প্রপ ব্যবহার করে প্রতিটি লিস্ট আইটেমকে একটি ইউনিক আইডেন্টিফায়ার দেওয়া হলে, React বুঝতে পারে কোন আইটেমটি কোনটি, এমনকি যদি তাদের ক্রম পরিবর্তন হয়। এটি Diffing প্রক্রিয়াকে অনেক বেশি দক্ষ করে তোলে।

React কোডের উদাহরণ এবং ভার্চুয়াল DOM-এর কার্যকারিতা 💻

আসুন "Learn with Sumit"-এর দেওয়া React কোডটি বিশ্লেষণ করি:

/*
 * Title: React JS File
 * Description: A React JS Implementation of efficient DOM manipulation
 * Author: Sumit Saha ( Learn with Sumit )
 * Date: 07/01/2023
 *
 */
 
const domContainer = document.querySelector("#root"); // HTML-এ <div id="root"></div> থাকতে হবে
 
const Fruits = () => {
  // স্টেট হুক: fruit বর্তমান ইনপুট ভ্যালু, setFruit এটি আপডেটের ফাংশন
  const [fruit, setFruit] = React.useState("");
  // স্টেট হুক: fruits হলো ফলের অ্যারে, setFruits এটি আপডেটের ফাংশন
  const [fruits, setFruits] = React.useState([
    "mango",
    "guava",
    "apple",
    "orange", // বানান ঠিক করা হলো
  ]);
 
  // এই ফাংশনটি UI ডেসক্রিপশন (ভার্চুয়াল DOM নোড) রিটার্ন করে
  return (
    <div className="container">
      <ul id="fruits">
        {/* fruits অ্যারে সর্ট করে প্রতিটি ফলের জন্য একটি li তৈরি */}
        {/* key প্রপ এখানে খুবই গুরুত্বপূর্ণ */}
        {fruits.sort().map((fruitItem, index) => (
          <li key={fruitItem}>{fruitItem}</li> // index এর পরিবর্তে fruitItem কে key হিসেবে ব্যবহার করা ভালো যদি আইটেম ইউনিক হয়
        ))}
      </ul>
      <br />
      <p>
        <input
          type="text"
          value={fruit} // ইনপুট ফিল্ডের ভ্যালু fruit স্টেটের সাথে বাইন্ড করা
          onChange={(e) => setFruit(e.target.value)} // ইনপুট পরিবর্তন হলে fruit স্টেট আপডেট হবে
        />
      </p>
      <button
        onClick={() => {
          // setFruits কল হলে fruits স্টেট আপডেট হবে
          // React একটি নতুন ভার্চুয়াল DOM তৈরি করবে
          // এবং শুধুমাত্র প্রয়োজনীয় পরিবর্তন আসল DOM-এ করবে
          if (fruit.trim() !== "") {
            // যদি ইনপুট খালি না থাকে
            setFruits([...fruits, fruit]);
            setFruit(""); // ইনপুট ফিল্ড খালি করা
          }
        }}
      >
        Add Item
      </button>
    </div>
  );
};
 
// Fruits কম্পোনেন্টকে #root DOM এলিমেন্টে রেন্ডার করা
ReactDOM.render(<Fruits />, domContainer);

কোড ব্যাখ্যা ও ভার্চুয়াল DOM-এর ভূমিকা:

  1. React.useState (স্টেট হুক): fruit এবং fruits নামে দুটি স্টেট ভ্যারিয়েবল তৈরি করা হয়েছে। fruit ইনপুট ফিল্ডের বর্তমান মান ধারণ করে, আর fruits ফলের তালিকা ধারণ করে। React-এ, যখন কোনো কম্পোনেন্টের স্টেট পরিবর্তন হয়, React সেই কম্পোনেন্টটিকে রি-রেন্ডার করে।
  2. JSX রিটার্ন: Fruits ফাংশনটি JSX (JavaScript XML) রিটার্ন করছে, যা দেখতে HTML-এর মতো হলেও এটি আসলে জাভাস্ক্রিপ্ট অবজেক্টে (ভার্চুয়াল DOM নোড) রূপান্তরিত হয়।
  3. ইনপুট ফিল্ড: ইনপুট ফিল্ডের value fruit স্টেটের সাথে যুক্ত। onChange ইভেন্টে setFruit কল করে fruit স্টেট আপডেট করা হয়। যখন fruit স্টেট পরিবর্তন হয়, React শুধুমাত্র ইনপুট ফিল্ডের value আপডেট করার জন্য ভার্চুয়াল DOM-এ পরিবর্তন আনে এবং ফলস্বরূপ আসল DOM-ও অপটিমাইজডভাবে আপডেট হয়।
  4. "Add Item" বাটন: এই বাটনে ক্লিক করলে setFruits কল হয়। setFruits([...fruits, fruit]) একটি নতুন অ্যারে তৈরি করে (পুরোনো fruits অ্যারের সব আইটেম এবং নতুন fruit সহ) এবং fruits স্টেট আপডেট করে।
  5. রি-রেন্ডারিং এবং Diffing: যখন setFruits কল হওয়ার কারণে fruits স্টেট পরিবর্তন হয়, React Fruits কম্পোনেন্টটিকে রি-রেন্ডার করার সিদ্ধান্ত নেয়।
    • React একটি নতুন ভার্চুয়াল DOM তৈরি করে, যেখানে ফলের তালিকায় নতুন ফলটি যুক্ত হয়েছে।
    • এরপর React এই নতুন ভার্চুয়াল DOM-টিকে আগের ভার্চুয়াল DOM-এর সাথে তুলনা করে (Diffing)।
    • Diffing Algorithm দেখতে পায় যে <ul> লিস্টের মধ্যে শুধুমাত্র একটি নতুন <li> আইটেম যোগ হয়েছে।
    • React তখন আসল DOM-এ গিয়ে শুধুমাত্র সেই নতুন <li> আইটেমটি <ul> এর শেষে যোগ করে। এটি পুরো <ul> লিস্টটি মুছে আবার নতুন করে তৈরি করার চেয়ে অনেক বেশি কার্যকর।
  6. key প্রপ: fruits.map(...) এর ভেতরে <li> এলিমেন্টে key={fruitItem} ব্যবহার করা হয়েছে। এটি React-কে লিস্টের প্রতিটি আইটেমকে স্বতন্ত্রভাবে সনাক্ত করতে সাহায্য করে, বিশেষ করে যখন আইটেমগুলো যোগ, বিয়োগ বা পুনর্বিন্যাসিত হয়। যদি key সঠিকভাবে ব্যবহার না করা হয়, React অদক্ষভাবে DOM আপডেট করতে পারে অথবা স্টেটের সমস্যা হতে পারে। (সাধারণত, যদি লিস্ট আইটেমের কোনো ইউনিক আইডি থাকে, সেটি key হিসেবে ব্যবহার করা সবচেয়ে ভালো। এখানে ফলের নাম ইউনিক ধরে fruitItem ব্যবহার করা হয়েছে, তবে একই ফল একাধিকবার যোগ করলে এটি সমস্যা তৈরি করতে পারে। সেক্ষেত্রে index-এর সাথে অন্য কিছু যোগ করে ইউনিক key তৈরি করা যেতে পারে, অথবা প্রতিটি ফলের জন্য একটি ইউনিক আইডি জেনারেট করা যেতে পারে।)

এভাবেই React ভার্চুয়াল DOM এবং এর Diffing Algorithm ব্যবহার করে DOM ম্যানিপুলেশনকে অত্যন্ত দক্ষ করে তোলে, যার ফলে ব্যবহারকারীরা দ্রুত এবং রেসপন্সিভ ইউজার ইন্টারফেস পায়।


পারফরম্যান্স অপটিমাইজেশন তুলনা: কিছু টুলস 🛠️

কীভাবে বুঝবেন আপনার DOM ম্যানিপুলেশন (Vanilla JS, jQuery, বা React-এর মাধ্যমে) কতটা পারফরম্যান্ট? ব্রাউজার ডেভেলপার টুলস এখানে খুবই সহায়ক।

  • Chrome DevTools Performance Tab:

    • এই ট্যাবটি আপনার ওয়েব পেজের লোডিং এবং ইন্টারঅ্যাকশনের সময়কার পারফরম্যান্স রেকর্ড এবং বিশ্লেষণ করতে দেয়।
    • আপনি দেখতে পারবেন JavaScript এক্সিকিউশনে কত সময় লাগছে, কতবার এবং কতক্ষণ ধরে Layout (Reflow) ও Painting হচ্ছে।
    • JS Profile: কোন জাভাস্ক্রিপ্ট ফাংশনগুলো বেশি সময় নিচ্ছে তা সনাক্ত করতে পারবেন।
    • Timeline: একটি ভিজ্যুয়াল টাইমলাইন দেখায় যেখানে বিভিন্ন টাস্ক (যেমন Parsing HTML, Recalculate Style, Layout, Paint, Composite Layers) কখন এবং কতক্ষণ ধরে চলছে তা দেখা যায়।
    • Vanilla JS-এর "স্লো" এবং "ফাস্ট" পদ্ধতির কোড দুটিকে এই টুলে চালিয়ে দেখলে, "স্লো" পদ্ধতির ক্ষেত্রে অনেক বেশি এবং দীর্ঘ সময় ধরে Layout ও Paint অপারেশন দেখতে পাবেন, যেখানে "ফাস্ট" পদ্ধতিতে এটি অনেক কম হবে। React অ্যাপ্লিকেশনের ক্ষেত্রেও একই রকম বিশ্লেষণ করে দেখতে পারেন যে অপ্রয়োজনীয় DOM আপডেট হচ্ছে কিনা।
  • Lighthouse (Chrome DevTools Audits Tab):

    • Lighthouse একটি স্বয়ংক্রিয় টুল যা আপনার ওয়েব পেজের পারফরম্যান্স, অ্যাক্সেসিবিলিটি, SEO এবং অন্যান্য বেস্ট প্র্যাকটিসগুলো অডিট করে। এটি পারফরম্যান্স উন্নতির জন্য সুনির্দিষ্ট পরামর্শও দেয়।
  • React Developer Tools (ব্রাউজার এক্সটেনশন):

    • React অ্যাপ্লিকেশনের জন্য বিশেষভাবে তৈরি এই এক্সটেনশনটি কম্পোনেন্ট হায়ারার্কি, স্টেট, প্রপস ইত্যাদি ইন্সপেক্ট করতে এবং পারফরম্যান্স প্রোফাইলিং করতে সাহায্য করে।
    • এতে "Profiler" নামে একটি ট্যাব আছে, যা React কম্পোনেন্টগুলো কতবার রি-রেন্ডার হচ্ছে এবং রেন্ডারিং-এ কত সময় লাগছে তা দেখায়। এর মাধ্যমে আপনি অপ্রয়োজনীয় রি-রেন্ডার সনাক্ত করে অপটিমাইজ করতে পারবেন।

jQuery এবং DOM ম্যানিপুলেশন: jQuery একসময় DOM ম্যানিপুলেশন সহজ করার জন্য খুবই জনপ্রিয় ছিল। এটি অনেক ব্রাউজার-নির্দিষ্ট জটিলতা লুকিয়ে রাখত। তবে, jQuery মূলত সরাসরি DOM-কেই ম্যানিপুলেট করে। যদিও এর কিছু মেথড DOM ম্যানিপুলেশনকে কিছুটা অপটিমাইজ করতে পারে (যেমন, .append() একাধিক আর্গুমেন্ট নিতে পারে), এটি ভার্চুয়াল DOM-এর মতো sofisticated কোনো কৌশল ব্যবহার করে না। তাই, জটিল এবং ডাইনামিক UI-এর ক্ষেত্রে, যেখানে ঘন ঘন DOM পরিবর্তন হয়, jQuery-এর পারফরম্যান্স React-এর তুলনায় কম হতে পারে। আধুনিক জাভাস্ক্রিপ্টে (document.querySelector, element.classList ইত্যাদি) অনেক কিছুই এখন jQuery ছাড়া সহজে করা যায়, তাই নতুন প্রজেক্টে jQuery-এর প্রয়োজনীয়তা কমে গেছে।


উপসংহার 🌟

ভার্চুয়াল DOM React-এর একটি মূল স্তম্ভ, যা ডেভেলপারদের জন্য DOM ম্যানিপুলেশনের জটিলতা কমিয়ে এনেছে এবং ইউজার ইন্টারফেসের পারফরম্যান্স উল্লেখযোগ্যভাবে বৃদ্ধি করেছে। এটি সরাসরি DOM পরিবর্তনের ব্যয়বহুল প্রক্রিয়া এড়িয়ে, পরিবর্তনগুলোকে বুদ্ধিমত্তার সাথে ব্যাচ করে এবং শুধুমাত্র প্রয়োজনীয় আপডেটগুলো আসল DOM-এ প্রয়োগ করে। React-এর Diffing Algorithm এবং key প্রপের সঠিক ব্যবহার এই প্রক্রিয়াকে আরও কার্যকর করে তোলে।

আশা করি, এই দুটি পর্বের মাধ্যমে ভার্চুয়াল DOM সম্পর্কে আপনার একটি পরিষ্কার এবং গভীর ধারণা তৈরি হয়েছে। React ইকোসিস্টেমের আরও অনেক আকর্ষণীয় দিক রয়েছে, যা জানার এবং শেখার জন্য আপনাকে উৎসাহিত করছি!