রিঅ্যাক্টের পারফর্ম্যান্সের পেছনের জাদু উন্মোচন করুন। এই বিশদ গাইডটি রিকনসিলিয়েশন অ্যালগরিদম, ভার্চুয়াল DOM ডিফারেন্সিং এবং মূল অপটিমাইজেশন কৌশলগুলো ব্যাখ্যা করে।
রিঅ্যাক্টের গোপন রহস্য: রিকনসিলিয়েশন অ্যালগরিদম এবং ভার্চুয়াল DOM ডিফারেন্সিং-এর গভীর বিশ্লেষণ
আধুনিক ওয়েব ডেভেলপমেন্টের জগতে, ডাইনামিক এবং ইন্টারেক্টিভ ইউজার ইন্টারফেস তৈরির জন্য রিঅ্যাক্ট নিজেকে একটি প্রভাবশালী শক্তি হিসাবে প্রতিষ্ঠা করেছে। এর জনপ্রিয়তা কেবল তার কম্পোনেন্ট-ভিত্তিক আর্কিটেকচারের জন্য নয়, বরং এর অসাধারণ পারফর্ম্যান্সের জন্যও। কিন্তু রিঅ্যাক্ট এত দ্রুত কেন? উত্তরটি কোনো জাদু নয়; এটি ইঞ্জিনিয়ারিং-এর একটি চমৎকার নিদর্শন যা রিকনসিলিয়েশন অ্যালগরিদম নামে পরিচিত।
অনেক ডেভেলপারের কাছে রিঅ্যাক্টের অভ্যন্তরীণ কার্যপ্রণালী একটি ব্ল্যাক বক্সের মতো। আমরা কম্পোনেন্ট লিখি, স্টেট পরিচালনা করি এবং দেখি UI নিখুঁতভাবে আপডেট হচ্ছে। তবে, এই নির্বিঘ্ন প্রক্রিয়ার পেছনের কৌশলগুলো, বিশেষ করে ভার্চুয়াল DOM এবং এর ডিফারেন্সিং অ্যালগরিদম বোঝা একজন ভালো রিঅ্যাক্ট ডেভেলপারকে একজন সেরা ডেভেলপার থেকে আলাদা করে। এই গভীর জ্ঞান আপনাকে অত্যন্ত অপটিমাইজড অ্যাপ্লিকেশন লিখতে, পারফর্ম্যান্সের সমস্যা ডিবাগ করতে এবং লাইব্রেরিটিকে সত্যিকার অর্থে আয়ত্ত করতে সাহায্য করে।
এই বিশদ গাইডটি রিঅ্যাক্টের মূল রেন্ডারিং প্রক্রিয়াটিকে সহজবোধ্য করে তুলবে। আমরা অন্বেষণ করব কেন সরাসরি DOM ম্যানিপুলেশন ব্যয়বহুল, কীভাবে ভার্চুয়াল DOM একটি চমৎকার সমাধান প্রদান করে, এবং কীভাবে রিকনসিলিয়েশন অ্যালগরিদম দক্ষতার সাথে আপনার UI আপডেট করে। আমরা আসল স্ট্যাক রিকনসিলার থেকে আধুনিক ফাইবার আর্কিটেকচারে এর বিবর্তন নিয়েও আলোচনা করব এবং শেষে এমন কার্যকরী কৌশল নিয়ে আলোচনা করব যা আপনি আজই আপনার নিজের অ্যাপ্লিকেশন অপটিমাইজ করতে প্রয়োগ করতে পারেন।
মূল সমস্যা: কেন সরাসরি DOM ম্যানিপুলেশন অদক্ষ
রিঅ্যাক্টের সমাধানকে উপলব্ধি করতে হলে, প্রথমে আমাদের সেই সমস্যাটি বুঝতে হবে যা এটি সমাধান করে। ডকুমেন্ট অবজেক্ট মডেল (DOM) হল HTML ডকুমেন্টের প্রতিনিধিত্ব এবং তার সাথে ইন্টারঅ্যাক্ট করার জন্য একটি ব্রাউজার API। এটি অবজেক্টের একটি ট্রি (tree) হিসাবে গঠিত, যেখানে প্রতিটি নোড ডকুমেন্টের একটি অংশকে প্রতিনিধিত্ব করে (যেমন একটি এলিমেন্ট, টেক্সট বা অ্যাট্রিবিউট)।
যখন আপনি স্ক্রিনে কিছু পরিবর্তন করতে চান, তখন আপনি এই DOM ট্রি-কে ম্যানিপুলেট করেন। উদাহরণস্বরূপ, একটি নতুন লিস্ট আইটেম যোগ করতে, আপনি একটি নতুন `
- ` নোডে যুক্ত করেন। যদিও এটি সহজবোধ্য মনে হয়, DOM অপারেশনগুলো কম্পিউটেশনালি ব্যয়বহুল। এর কারণ হলো:
- Layout এবং Reflow: যখনই আপনি কোনো এলিমেন্টের জ্যামিতি (যেমন এর প্রস্থ, উচ্চতা বা অবস্থান) পরিবর্তন করেন, ব্রাউজারকে প্রভাবিত সমস্ত এলিমেন্টের অবস্থান এবং মাত্রা পুনরায় গণনা করতে হয়। এই প্রক্রিয়াটিকে "reflow" বা "layout" বলা হয় এবং এটি পুরো ডকুমেন্টে ছড়িয়ে পড়তে পারে, যা উল্লেখযোগ্য প্রসেসিং পাওয়ার খরচ করে।
- Repainting: একটি reflow-এর পরে, ব্রাউজারকে আপডেট হওয়া এলিমেন্টগুলোর জন্য স্ক্রিনে পিক্সেলগুলো পুনরায় আঁকতে হয়। একে "repainting" বা "rasterizing" বলা হয়। ব্যাকগ্রাউন্ডের রঙের মতো একটি সাধারণ পরিবর্তন শুধুমাত্র একটি repaint ট্রিগার করতে পারে, কিন্তু একটি layout পরিবর্তন সবসময় একটি repaint ট্রিগার করবে।
- Synchronous এবং Blocking: DOM অপারেশনগুলো সিনক্রোনাস। যখন আপনার জাভাস্ক্রিপ্ট কোড DOM পরিবর্তন করে, তখন ব্রাউজারকে প্রায়শই অন্যান্য কাজ, যেমন ব্যবহারকারীর ইনপুটে সাড়া দেওয়া, থামিয়ে reflow এবং repaint করতে হয়, যা একটি ধীর বা জমে যাওয়া ইউজার ইন্টারফেসের কারণ হতে পারে।
- প্রাথমিক রেন্ডার: যখন আপনার অ্যাপ্লিকেশন প্রথম লোড হয়, রিঅ্যাক্ট আপনার UI-এর জন্য একটি সম্পূর্ণ ভার্চুয়াল DOM ট্রি তৈরি করে এবং এটি ব্যবহার করে প্রাথমিক আসল DOM তৈরি করে।
- স্টেট আপডেট: যখন অ্যাপ্লিকেশনের স্টেট পরিবর্তন হয় (যেমন, একজন ব্যবহারকারী একটি বোতামে ক্লিক করে), রিঅ্যাক্ট একটি নতুন ভার্চুয়াল DOM ট্রি তৈরি করে যা নতুন স্টেটকে প্রতিফলিত করে।
- ডিফারেন্সিং (Diffing): রিঅ্যাক্টের কাছে এখন মেমরিতে দুটি ভার্চুয়াল DOM ট্রি রয়েছে: পুরানোটি (স্টেট পরিবর্তনের আগে) এবং নতুনটি। এরপর এটি তার "ডিফারেন্সিং" অ্যালগরিদম চালায় এই দুটি ট্রি-এর তুলনা করতে এবং সঠিক পার্থক্যগুলো সনাক্ত করতে।
- ব্যাচিং এবং আপডেট: রিঅ্যাক্ট আসল DOM-কে নতুন ভার্চুয়াল DOM-এর সাথে মেলানোর জন্য প্রয়োজনীয় সবচেয়ে কার্যকর এবং ন্যূনতম অপারেশনগুলো গণনা করে। এই অপারেশনগুলো একসাথে ব্যাচ করা হয় এবং একটি একক, অপটিমাইজড ক্রমানুসারে আসল DOM-এ প্রয়োগ করা হয়।
- এটি পুরো পুরানো ট্রি-টি ভেঙে ফেলে, সমস্ত পুরানো কম্পোনেন্ট আনমাউন্ট করে এবং তাদের স্টেট ধ্বংস করে।
- এটি নতুন এলিমেন্টের টাইপের উপর ভিত্তি করে স্ক্র্যাচ থেকে একটি সম্পূর্ণ নতুন ট্রি তৈরি করে।
- আইটেম B
- আইটেম C
- আইটেম A
- আইটেম B
- আইটেম C
- এটি ইনডেক্স ০-এর পুরানো আইটেম ('আইটেম B') এবং ইনডেক্স ০-এর নতুন আইটেম ('আইটেম A')-এর তুলনা করে। তারা ভিন্ন, তাই এটি প্রথম আইটেমটিকে মিউটেট করে।
- এটি ইনডেক্স ১-এর পুরানো আইটেম ('আইটেম C') এবং ইনডেক্স ১-এর নতুন আইটেম ('আইটেম B')-এর তুলনা করে। তারা ভিন্ন, তাই এটি দ্বিতীয় আইটেমটিকে মিউটেট করে।
- এটি দেখে যে ইনডেক্স ২-এ একটি নতুন আইটেম ('আইটেম C') আছে এবং এটি সন্নিবেশ করে।
- আইটেম B
- আইটেম C
- আইটেম A
- আইটেম B
- আইটেম C
- রিঅ্যাক্ট নতুন তালিকার চাইল্ডদের দেখে এবং 'b' ও 'c' কী (key) সহ এলিমেন্টগুলো খুঁজে পায়।
- এটি জানে যে 'b' এবং 'c' কী সহ এলিমেন্টগুলো পুরানো তালিকাতে ইতিমধ্যে বিদ্যমান, তাই এটি কেবল তাদের সরিয়ে নেয়।
- এটি দেখে যে 'a' কী সহ একটি নতুন এলিমেন্ট আছে যা আগে ছিল না, তাই এটি তৈরি করে এবং সন্নিবেশ করে।
- ... )`) একটি অ্যান্টি-প্যাটার্ন যদি তালিকাটি কখনো পুনরায় সাজানো, ফিল্টার করা, বা মাঝখান থেকে আইটেম যোগ/মুছে ফেলা হয়, কারণ এটি কোনো কী না থাকার মতোই একই সমস্যার সৃষ্টি করে। সেরা কী হল আপনার ডেটা থেকে অনন্য শনাক্তকারী, যেমন একটি ডাটাবেস আইডি।
- ইনক্রিমেন্টাল রেন্ডারিং: এটি রেন্ডারিংয়ের কাজকে ছোট ছোট খণ্ডে বিভক্ত করতে এবং একাধিক ফ্রেমে ছড়িয়ে দিতে পারে।
- অগ্রাধিকার প্রদান: এটি বিভিন্ন ধরণের আপডেটের জন্য বিভিন্ন অগ্রাধিকার স্তর নির্ধারণ করতে পারে। উদাহরণস্বরূপ, একজন ব্যবহারকারী ইনপুট ফিল্ডে টাইপ করলে তার অগ্রাধিকার ব্যাকগ্রাউন্ডে ডেটা ফেচ করার চেয়ে বেশি।
- বিরতি এবং বাতিল করার ক্ষমতা: এটি একটি উচ্চ-অগ্রাধিকারের কাজ সামলানোর জন্য একটি নিম্ন-অগ্রাধিকারের আপডেটের কাজ থামাতে পারে, এবং এমনকি যে কাজ আর প্রয়োজন নেই তা বাতিল বা পুনরায় ব্যবহার করতে পারে।
- রেন্ডার/রিকনসিলিয়েশন পর্যায় (অ্যাসিঙ্ক্রোনাস): এই পর্যায়ে, রিঅ্যাক্ট ফাইবার নোডগুলো প্রসেস করে একটি "ওয়ার্ক-ইন-প্রোগ্রেস" ট্রি তৈরি করে। এটি কম্পোনেন্টের `render` মেথড কল করে এবং ডিফারেন্সিং অ্যালগরিদম চালায় DOM-এ কী পরিবর্তন করতে হবে তা নির্ধারণ করতে। গুরুত্বপূর্ণভাবে, এই পর্যায়টি বাধা দেওয়া যায়। রিঅ্যাক্ট এই কাজটি আরও গুরুত্বপূর্ণ কিছু সামলানোর জন্য থামাতে পারে এবং পরে আবার শুরু করতে পারে। যেহেতু এটি বাধা দেওয়া যেতে পারে, তাই একটি অসামঞ্জস্যপূর্ণ UI স্টেট এড়াতে রিঅ্যাক্ট এই পর্যায়ে কোনো প্রকৃত DOM পরিবর্তন প্রয়োগ করে না।
- কমিট পর্যায় (সিঙ্ক্রোনাস): ওয়ার্ক-ইন-প্রোগ্রেস ট্রি সম্পূর্ণ হয়ে গেলে, রিঅ্যাক্ট কমিট পর্যায়ে প্রবেশ করে। এটি গণনাকৃত পরিবর্তনগুলো নেয় এবং সেগুলোকে আসল DOM-এ প্রয়োগ করে। এই পর্যায়টি সিঙ্ক্রোনাস এবং বাধা দেওয়া যায় না। এটি নিশ্চিত করে যে ব্যবহারকারী সর্বদা একটি সামঞ্জস্যপূর্ণ UI দেখতে পায়। `componentDidMount` এবং `componentDidUpdate`-এর মতো লাইফসাইকেল মেথড, সেইসাথে `useLayoutEffect` এবং `useEffect` হুকগুলো এই পর্যায়ে কার্যকর করা হয়।
- `React.memo()`: ফাংশন কম্পোনেন্টগুলোর জন্য একটি হায়ার-অর্ডার কম্পোনেন্ট। এটি কম্পোনেন্টের প্রপসের একটি শ্যালো তুলনা করে। যদি প্রপস পরিবর্তন না হয়, রিঅ্যাক্ট কম্পোনেন্টটি রি-রেন্ডার করা এড়িয়ে যাবে এবং শেষ রেন্ডার করা ফলাফলটি পুনরায় ব্যবহার করবে।
- `useCallback()`: একটি কম্পোনেন্টের ভিতরে সংজ্ঞায়িত ফাংশনগুলো প্রতিটি রেন্ডারে পুনরায় তৈরি হয়। যদি আপনি এই ফাংশনগুলোকে `React.memo`-তে মোড়ানো একটি চাইল্ড কম্পোনেন্টে প্রপস হিসাবে পাস করেন, তবে চাইল্ডটি রি-রেন্ডার হবে কারণ ফাংশন প্রপটি প্রযুক্তিগতভাবে প্রতিবার একটি নতুন ফাংশন। `useCallback` ফাংশনটিকে মেমোইজ করে, এটি নিশ্চিত করে যে এটি কেবল তার ডিপেন্ডেন্সি পরিবর্তন হলেই পুনরায় তৈরি হবে।
- `useMemo()`: `useCallback`-এর মতো, কিন্তু ভ্যালুর জন্য। এটি একটি ব্যয়বহুল গণনার ফলাফলকে মেমোইজ করে। গণনাটি কেবল তখনই পুনরায় চালানো হয় যদি তার কোনো ডিপেন্ডেন্সি পরিবর্তিত হয়। এটি প্রতিটি রেন্ডারে ব্যয়বহুল গণনা রোধ করার জন্য এবং প্রপস হিসাবে পাস করা স্থিতিশীল অবজেক্ট/অ্যারে রেফারেন্স বজায় রাখার জন্য দরকারী।
হাজার হাজার নোড সহ একটি জটিল অ্যাপ্লিকেশনের কথা ভাবুন। যদি আপনি স্টেট আপডেট করেন এবং সরাসরি DOM ম্যানিপুলেট করে পুরো UI পুনরায় রেন্ডার করেন, তাহলে আপনি ব্রাউজারকে ব্যয়বহুল reflow এবং repaint-এর একটি ক্যাসকেডে বাধ্য করবেন, যার ফলে একটি ভয়াবহ ব্যবহারকারীর অভিজ্ঞতা হবে।
সমাধান: ভার্চুয়াল DOM (VDOM)
রিঅ্যাক্টের নির্মাতারা সরাসরি DOM ম্যানিপুলেশনের পারফর্ম্যান্সের বাধাটি বুঝতে পেরেছিলেন। তাদের সমাধান ছিল একটি অ্যাবস্ট্র্যাকশন লেয়ার চালু করা: ভার্চুয়াল DOM।
ভার্চুয়াল DOM কী?
ভার্চুয়াল DOM হল আসল DOM-এর একটি হালকা, ইন-মেমরি উপস্থাপনা। এটি মূলত একটি প্লেইন জাভাস্ক্রিপ্ট অবজেক্ট যা UI-কে বর্ণনা করে। একটি VDOM অবজেক্টের বৈশিষ্ট্য থাকে যা একটি আসল DOM এলিমেন্টের অ্যাট্রিবিউটগুলোর অনুরূপ। উদাহরণস্বরূপ, একটি সাধারণ `
{ type: 'div', props: { className: 'container', children: 'Hello World' } }
যেহেতু এগুলো কেবল জাভাস্ক্রিপ্ট অবজেক্ট, তাই এগুলো তৈরি এবং ম্যানিপুলেট করা অবিশ্বাস্যভাবে দ্রুত। এতে ব্রাউজার API-এর সাথে কোনো ইন্টারঅ্যাকশন জড়িত নয়, তাই কোনো reflow বা repaint হয় না।
ভার্চুয়াল DOM কীভাবে কাজ করে?
VDOM UI ডেভেলপমেন্টের জন্য একটি ডিক্লেয়ারেটিভ অ্যাপ্রোচ সক্ষম করে। ব্রাউজারকে ধাপে ধাপে কীভাবে DOM পরিবর্তন করতে হবে তা বলার পরিবর্তে (ইম্পারেটিভ), আপনি কেবল ঘোষণা করেন যে একটি নির্দিষ্ট স্টেটের জন্য UI-কে কেমন দেখতে হবে (ডিক্লেয়ারেটিভ)। বাকিটা রিঅ্যাক্ট সামলে নেয়।
প্রক্রিয়াটি এইরকম দেখায়:
আপডেটগুলো ব্যাচ করে, রিঅ্যাক্ট ধীরগতির DOM-এর সাথে সরাসরি ইন্টারঅ্যাকশন কমিয়ে দেয়, যা পারফর্ম্যান্সকে উল্লেখযোগ্যভাবে উন্নত করে। এই দক্ষতার মূল ভিত্তি হল "ডিফারেন্সিং" ধাপ, যা আনুষ্ঠানিকভাবে রিকনসিলিয়েশন অ্যালগরিদম নামে পরিচিত।
রিঅ্যাক্টের মূল ভিত্তি: রিকনসিলিয়েশন অ্যালগরিদম
রিকনসিলিয়েশন হল সেই প্রক্রিয়া যার মাধ্যমে রিঅ্যাক্ট DOM-কে সর্বশেষ কম্পোনেন্ট ট্রি-এর সাথে মেলাতে আপডেট করে। যে অ্যালগরিদম এই তুলনাটি সম্পাদন করে তাকে আমরা "ডিফারেন্সিং অ্যালগরিদম" বলি।
তাত্ত্বিকভাবে, একটি ট্রি-কে অন্যটিতে রূপান্তর করার জন্য ন্যূনতম সংখ্যক রূপান্তর খুঁজে বের করা একটি খুব জটিল সমস্যা, যার অ্যালগরিদম কমপ্লেক্সিটি O(n³) অর্ডারের, যেখানে n হল ট্রি-এর নোডের সংখ্যা। এটি বাস্তব-বিশ্বের অ্যাপ্লিকেশনের জন্য খুব ধীর হবে। এই সমস্যা সমাধানের জন্য, রিঅ্যাক্টের দল ওয়েব অ্যাপ্লিকেশনগুলো সাধারণত কীভাবে আচরণ করে সে সম্পর্কে কিছু চমৎকার পর্যবেক্ষণ করেছে এবং একটি হিউরিস্টিক অ্যালগরিদম প্রয়োগ করেছে যা অনেক দ্রুত—O(n) সময়ে কাজ করে।
হিউরিস্টিকস: ডিফারেন্সিং দ্রুত এবং অনুমানযোগ্য করা
রিঅ্যাক্টের ডিফারেন্সিং অ্যালগরিদম দুটি প্রাথমিক অনুমান বা হিউরিস্টিকসের উপর নির্মিত:
হিউরিস্টিক ১: ভিন্ন এলিমেন্টের ধরন ভিন্ন ট্রি তৈরি করে
এটি প্রথম এবং সবচেয়ে সহজবোধ্য নিয়ম। দুটি VDOM নোড তুলনা করার সময়, রিঅ্যাক্ট প্রথমে তাদের টাইপ দেখে। যদি রুট এলিমেন্টগুলোর টাইপ ভিন্ন হয়, রিঅ্যাক্ট ধরে নেয় যে ডেভেলপার একটিকে অন্যটিতে রূপান্তর করার চেষ্টা করতে চায় না। পরিবর্তে, এটি একটি আরও কঠোর কিন্তু অনুমানযোগ্য পদ্ধতি গ্রহণ করে:
উদাহরণস্বরূপ, এই পরিবর্তনটি বিবেচনা করুন:
আগে: <div><Counter /></div>
পরে: <span><Counter /></span>
যদিও চাইল্ড `Counter` কম্পোনেন্টটি একই, রিঅ্যাক্ট দেখে যে রুটটি `div` থেকে `span`-এ পরিবর্তিত হয়েছে। এটি পুরানো `div` এবং এর ভেতরের `Counter` ইনস্ট্যান্সটিকে সম্পূর্ণরূপে আনমাউন্ট করবে (তার স্টেট হারিয়ে) এবং তারপর একটি নতুন `span` এবং `Counter`-এর একটি ব্র্যান্ড নিউ ইনস্ট্যান্স মাউন্ট করবে।
মূল কথা: যদি আপনি একটি কম্পোনেন্ট সাবট্রি-এর স্টেট সংরক্ষণ করতে চান বা সেই সাবট্রি-এর সম্পূর্ণ রি-রেন্ডার এড়াতে চান, তাহলে তার রুট এলিমেন্টের টাইপ পরিবর্তন করা থেকে বিরত থাকুন।
হিউরিস্টিক ২: ডেভেলপাররা `key` প্রপ ব্যবহার করে স্থিতিশীল এলিমেন্টগুলোর ইঙ্গিত দিতে পারে
এটি ডেভেলপারদের জন্য বোঝা এবং সঠিকভাবে প্রয়োগ করার জন্য তর্কাতীতভাবে সবচেয়ে গুরুত্বপূর্ণ হিউরিস্টিক। যখন রিঅ্যাক্ট চাইল্ড এলিমেন্টগুলোর একটি তালিকা তুলনা করে, তখন এর ডিফল্ট আচরণ হল একই সময়ে উভয় চাইল্ড তালিকার উপর পুনরাবৃত্তি করা এবং যেখানেই পার্থক্য থাকে সেখানে একটি মিউটেশন তৈরি করা।
ইনডেক্স-ভিত্তিক ডিফারেন্সিং-এর সমস্যা
আসুন কল্পনা করি আমাদের আইটেমের একটি তালিকা আছে এবং আমরা কী (key) ব্যবহার না করে তালিকার শুরুতে একটি নতুন আইটেম যোগ করি।
প্রাথমিক তালিকা:
আপডেট করা তালিকা (শুরুতে 'আইটেম A' যোগ করুন):
কী (key) ছাড়া, রিঅ্যাক্ট একটি সাধারণ, ইনডেক্স-ভিত্তিক তুলনা করে:
এটি অত্যন্ত অদক্ষ। রিঅ্যাক্ট দুটি অপ্রয়োজনীয় মিউটেশন এবং একটি সন্নিবেশ করেছে, যেখানে কেবল শুরুতে একটি একক সন্নিবেশ প্রয়োজন ছিল। যদি এই তালিকা আইটেমগুলো তাদের নিজস্ব স্টেট সহ জটিল কম্পোনেন্ট হতো, তবে এটি গুরুতর পারফর্ম্যান্স সমস্যা এবং বাগের কারণ হতে পারত, কারণ কম্পোনেন্টগুলোর মধ্যে স্টেট মিশে যেতে পারত।
`key` প্রপের শক্তি
`key` প্রপ একটি সমাধান প্রদান করে। এটি একটি বিশেষ স্ট্রিং অ্যাট্রিবিউট যা আপনাকে এলিমেন্টের তালিকা তৈরি করার সময় অন্তর্ভুক্ত করতে হবে। কী (key) রিঅ্যাক্টকে প্রতিটি এলিমেন্টের জন্য একটি স্থিতিশীল পরিচয় দেয়।
আসুন একই উদাহরণটি আবার দেখি, কিন্তু এবার স্থিতিশীল, অনন্য কী (key) সহ:
প্রাথমিক তালিকা:
আপডেট করা তালিকা:
এখন, রিঅ্যাক্টের ডিফারেন্সিং প্রক্রিয়া অনেক বেশি স্মার্ট:
এটি অনেক বেশি কার্যকর। রিঅ্যাক্ট সঠিকভাবে সনাক্ত করে যে এটির কেবল একটি সন্নিবেশ করা প্রয়োজন। 'b' এবং 'c' কী-এর সাথে যুক্ত কম্পোনেন্টগুলো সংরক্ষিত থাকে, তাদের অভ্যন্তরীণ স্টেট বজায় রেখে।
কী-এর জন্য গুরুত্বপূর্ণ নিয়ম: কী অবশ্যই স্থিতিশীল, অনুমানযোগ্য এবং অনন্য হতে হবে তাদের সিবলিংদের মধ্যে। অ্যারে ইনডেক্সকে কী হিসাবে ব্যবহার করা (`items.map((item, index) =>
বিবর্তন: স্ট্যাক থেকে ফাইবার আর্কিটেকচার
উপরে বর্ণিত রিকনসিলিয়েশন অ্যালগরিদমটি বহু বছর ধরে রিঅ্যাক্টের ভিত্তি ছিল। তবে, এর একটি বড় সীমাবদ্ধতা ছিল: এটি ছিল সিনক্রোনাস এবং ব্লকিং। এই আসল বাস্তবায়নটিকে এখন স্ট্যাক রিকনসিলার হিসাবে উল্লেখ করা হয়।
পুরানো পদ্ধতি: স্ট্যাক রিকনসিলার
স্ট্যাক রিকনসিলারে, যখন একটি স্টেট আপডেট একটি রি-রেন্ডার ট্রিগার করত, রিঅ্যাক্ট পুনরাবৃত্তিমূলকভাবে পুরো কম্পোনেন্ট ট্রি ট্র্যাভার্স করত, পরিবর্তনগুলো গণনা করত এবং সেগুলোকে DOM-এ প্রয়োগ করত—সবই একটি একক, নিরবচ্ছিন্ন ক্রমানুসারে। ছোট আপডেটের জন্য, এটি ঠিক ছিল। কিন্তু বড় কম্পোনেন্ট ট্রি-এর জন্য, এই প্রক্রিয়াটি একটি উল্লেখযোগ্য পরিমাণ সময় নিতে পারত (যেমন, ১৬ মিলিসেকেন্ডের বেশি), যা ব্রাউজারের প্রধান থ্রেডকে ব্লক করে দিত। এটি UI-কে প্রতিক্রিয়াহীন করে তুলত, যার ফলে ফ্রেম ড্রপ, জ্যাঙ্কি অ্যানিমেশন এবং একটি খারাপ ব্যবহারকারীর অভিজ্ঞতা হতো।
রিঅ্যাক্ট ফাইবারের পরিচিতি (রিঅ্যাক্ট ১৬+)
এই সমস্যা সমাধানের জন্য, রিঅ্যাক্ট টিম মূল রিকনসিলিয়েশন অ্যালগরিদমটি সম্পূর্ণরূপে পুনর্লিখনের জন্য একটি বহু-বছরের প্রকল্প গ্রহণ করে। এর ফল, রিঅ্যাক্ট ১৬-এ প্রকাশিত, যার নাম রিঅ্যাক্ট ফাইবার।
ফাইবার আর্কিটেকচারটি কনকারেন্সি সক্ষম করার জন্য গ্রাউন্ড আপ থেকে ডিজাইন করা হয়েছিল—রিঅ্যাক্টের একাধিক কাজ একবারে করার এবং অগ্রাধিকারের ভিত্তিতে তাদের মধ্যে স্যুইচ করার ক্ষমতা।
একটি "ফাইবার" হল একটি প্লেইন জাভাস্ক্রিপ্ট অবজেক্ট যা কাজের একটি ইউনিটকে প্রতিনিধিত্ব করে। এটি একটি কম্পোনেন্ট, তার ইনপুট (প্রপস) এবং তার আউটপুট (চিলড্রেন) সম্পর্কে তথ্য ধারণ করে। একটি পুনরাবৃত্তিমূলক ট্র্যাভার্সালের পরিবর্তে যা বাধা দেওয়া যেত না, রিঅ্যাক্ট এখন ফাইবার নোডগুলোর একটি লিঙ্কড লিস্ট প্রসেস করে, একবারে একটি।
এই নতুন আর্কিটেকচারটি বেশ কয়েকটি মূল ক্ষমতা আনলক করেছে:
ফাইবারের দুটি পর্যায়
ফাইবারের অধীনে, রেন্ডারিং প্রক্রিয়াটি দুটি স্বতন্ত্র পর্যায়ে বিভক্ত:
ফাইবার আর্কিটেকচারটি রিঅ্যাক্টের অনেক আধুনিক বৈশিষ্ট্যের ভিত্তি, যার মধ্যে রয়েছে `Suspense`, কনকারেন্ট রেন্ডারিং, `useTransition`, এবং `useDeferredValue`, যা সবই ডেভেলপারদের আরও প্রতিক্রিয়াশীল এবং সাবলীল ইউজার ইন্টারফেস তৈরি করতে সাহায্য করে।
ডেভেলপারদের জন্য ব্যবহারিক অপটিমাইজেশন কৌশল
রিঅ্যাক্টের রিকনসিলিয়েশন প্রক্রিয়া বোঝা আপনাকে আরও পারফর্ম্যান্ট কোড লেখার ক্ষমতা দেয়। এখানে কিছু কার্যকরী কৌশল রয়েছে:
১. তালিকার জন্য সর্বদা স্থিতিশীল এবং অনন্য কী ব্যবহার করুন
এই বিষয়টি যথেষ্ট জোর দিয়ে বলা সম্ভব নয়। এটি তালিকার জন্য সবচেয়ে গুরুত্বপূর্ণ অপটিমাইজেশন। আপনার ডেটা থেকে একটি অনন্য আইডি ব্যবহার করুন (যেমন, `product.id`)। অ্যারে ইনডেক্স ব্যবহার করা এড়িয়ে চলুন যদি না তালিকাটি সম্পূর্ণ স্ট্যাটিক হয় এবং কখনও পরিবর্তন না হয়।
২. অপ্রয়োজনীয় রি-রেন্ডার এড়িয়ে চলুন
একটি কম্পোনেন্ট রি-রেন্ডার হয় যদি তার স্টেট পরিবর্তন হয় বা তার প্যারেন্ট রি-রেন্ডার হয়। কখনও কখনও, একটি কম্পোনেন্ট রি-রেন্ডার হয় যদিও তার আউটপুট একই থাকত। আপনি এটি প্রতিরোধ করতে পারেন:
৩. স্মার্ট কম্পোনেন্ট কম্পোজিশন
আপনি যেভাবে আপনার কম্পোনেন্টগুলো গঠন করেন তা পারফর্ম্যান্সের উপর একটি উল্লেখযোগ্য প্রভাব ফেলতে পারে। যদি আপনার কম্পোনেন্টের স্টেটের একটি অংশ ঘন ঘন আপডেট হয়, তবে সেটিকে সেই অংশগুলো থেকে আলাদা করার চেষ্টা করুন যা আপডেট হয় না।
উদাহরণস্বরূপ, একটি বড় কম্পোনেন্টে একটি ঘন ঘন পরিবর্তনশীল ইনপুট ফিল্ড থাকার পরিবর্তে যা পুরো কম্পোনেন্টটিকে রি-রেন্ডার করে, সেই স্টেটটিকে তার নিজস্ব ছোট কম্পোনেন্টে তুলে নিন। এইভাবে, ব্যবহারকারী টাইপ করার সময় কেবল ছোট কম্পোনেন্টটি রি-রেন্ডার হয়।
৪. দীর্ঘ তালিকা ভার্চুয়ালাইজ করুন
যদি আপনাকে শত শত বা হাজার হাজার আইটেম সহ তালিকা রেন্ডার করতে হয়, এমনকি সঠিক কী ব্যবহার করেও, সেগুলোকে একবারে রেন্ডার করা ধীর হতে পারে এবং অনেক মেমরি খরচ করতে পারে। সমাধান হল ভার্চুয়ালাইজেশন বা উইন্ডোয়িং। এই কৌশলটিতে কেবল সেই ছোট আইটেমের উপসেট রেন্ডার করা হয় যা বর্তমানে ভিউপোর্টে দৃশ্যমান। ব্যবহারকারী স্ক্রোল করার সাথে সাথে পুরানো আইটেমগুলো আনমাউন্ট করা হয় এবং নতুন আইটেমগুলো মাউন্ট করা হয়। `react-window` এবং `react-virtualized`-এর মতো লাইব্রেরিগুলো এই প্যাটার্নটি বাস্তবায়নের জন্য শক্তিশালী এবং সহজে ব্যবহারযোগ্য কম্পোনেন্ট সরবরাহ করে।
উপসংহার
রিঅ্যাক্টের পারফর্ম্যান্স কোনো দুর্ঘটনা নয়; এটি ভার্চুয়াল DOM এবং একটি দক্ষ রিকনসিলিয়েশন অ্যালগরিদমকে কেন্দ্র করে একটি সুচিন্তিত এবং অত্যাধুনিক আর্কিটেকচারের ফল। সরাসরি DOM ম্যানিপুলেশনকে অ্যাবস্ট্রাক্ট করে, রিঅ্যাক্ট এমনভাবে আপডেটগুলো ব্যাচ এবং অপটিমাইজ করতে পারে যা ম্যানুয়ালি পরিচালনা করা অবিশ্বাস্যভাবে জটিল হবে।
ডেভেলপার হিসাবে, আমরা এই প্রক্রিয়ার একটি গুরুত্বপূর্ণ অংশ। ডিফারেন্সিং অ্যালগরিদমের হিউরিস্টিকস বোঝার মাধ্যমে—সঠিকভাবে কী ব্যবহার করে, কম্পোনেন্ট এবং ভ্যালু মেমোইজ করে, এবং আমাদের অ্যাপ্লিকেশনগুলোকে চিন্তাভাবনা করে গঠন করে—আমরা রিঅ্যাক্টের রিকনসিলারের সাথে কাজ করতে পারি, তার বিরুদ্ধে নয়। ফাইবার আর্কিটেকচারে বিবর্তন যা সম্ভব তার সীমানা আরও বাড়িয়েছে, যা তরল এবং প্রতিক্রিয়াশীল UI-এর একটি নতুন প্রজন্মকে সক্ষম করেছে।
পরের বার যখন আপনি দেখবেন আপনার UI একটি স্টেট পরিবর্তনের পরে সঙ্গে সঙ্গে আপডেট হয়ে যাচ্ছে, তখন পর্দার আড়ালে ঘটে যাওয়া ভার্চুয়াল DOM, ডিফারেন্সিং অ্যালগরিদম এবং কমিট পর্যায়ের চমৎকার নাচের প্রশংসা করার জন্য এক মুহূর্ত সময় নিন। এই বোঝাপড়াই হল বিশ্বব্যাপী দর্শকদের জন্য দ্রুত, আরও দক্ষ এবং আরও শক্তিশালী রিঅ্যাক্ট অ্যাপ্লিকেশন তৈরির চাবিকাঠি।