Skip to content

Rendering Mechanism

Vue কিভাবে একটি টেমপ্লেট নেয় এবং এটিকে প্রকৃত DOM নোডে পরিণত করে? Vue কীভাবে সেই DOM নোডগুলিকে দক্ষতার সাথে আপডেট করে? আমরা এখানে Vue-এর অভ্যন্তরীণ রেন্ডারিং পদ্ধতিতে ডুব দিয়ে এই প্রশ্নগুলির উপর কিছু আলোকপাত করার চেষ্টা করব।

Virtual DOM

আপনি সম্ভবত "ভার্চুয়াল DOM" শব্দটি সম্পর্কে শুনেছেন, যার উপর ভিত্তি করে Vue এর রেন্ডারিং সিস্টেম।

ভার্চুয়াল DOM (VDOM) হল একটি প্রোগ্রামিং ধারণা যেখানে একটি UI এর আদর্শ বা "ভার্চুয়াল" উপস্থাপনা মেমরিতে রাখা হয় এবং "বাস্তব" DOM-এর সাথে সিঙ্ক করা হয়। ধারণাটি React দ্বারা প্রবর্তিত হয়েছিল এবং Vue সহ বিভিন্ন বাস্তবায়ন সহ অন্যান্য অনেক কাঠামোতে গৃহীত হয়েছে।

ভার্চুয়াল ডিওএম একটি নির্দিষ্ট প্রযুক্তির চেয়ে একটি প্যাটার্নের বেশি, তাই এখানে কোনও ক্যানোনিকাল বাস্তবায়ন নেই। আমরা একটি সাধারণ উদাহরণ ব্যবহার করে ধারণাটি ব্যাখ্যা করতে পারি:

js
const vnode = {
  type: 'div',
  props: {
    id: 'hello'
  },
  children: [
    /* more vnodes */
  ]
}

এখানে, vnode হল একটি প্লেইন জাভাস্ক্রিপ্ট অবজেক্ট (একটি "ভার্চুয়াল নোড") একটি <div> কম্পোনেন্টকে উপস্থাপন করে। এটিতে সমস্ত তথ্য রয়েছে যা আমাদের প্রকৃত কম্পোনেন্ট তৈরি করতে হবে। এটিতে আরও চাইল্ড ভনোড রয়েছে, যা এটিকে একটি ভার্চুয়াল DOM treeর মূল করে তোলে।

একটি রানটাইম রেন্ডারার একটি ভার্চুয়াল DOM ট্রি হাঁটতে পারে এবং এটি থেকে একটি বাস্তব DOM ট্রি তৈরি করতে পারে। এই প্রক্রিয়াটিকে মাউন্ট বলা হয়।

যদি আমাদের কাছে ভার্চুয়াল DOM treeর দুটি কপি থাকে, তাহলে রেন্ডারারও হাঁটতে পারে এবং দুটি treeর তুলনা করতে পারে, পার্থক্যগুলি খুঁজে বের করতে পারে এবং সেই পরিবর্তনগুলিকে প্রকৃত DOM-এ প্রয়োগ করতে পারে৷ এই প্রক্রিয়াটিকে patch বলা হয়, যা "diffing" বা "reconciliation" নামেও পরিচিত।

ভার্চুয়াল DOM-এর প্রধান সুবিধা হল এটি ডেভেলপারকে প্রোগ্রাম্যাটিকভাবে তৈরি, পরিদর্শন এবং একটি ডিক্লেয়ার উপায়ে পছন্দসই UI স্ট্রাকচার রচনা করার ক্ষমতা দেয়, যখন রেন্ডারারের কাছে সরাসরি DOM ম্যানিপুলেশন ছেড়ে যায়।

Render Pipeline

উচ্চ স্তরে, যখন একটি Vue কম্পোনেন্ট Mount করা হয় তখন এটি ঘটে:

  1. Compile: Vue টেমপ্লেটগুলিকে রেন্ডার ফাংশন-এ কম্পাইল করা হয়: ফাংশন যা ভার্চুয়াল DOM ট্রি ফেরত দেয়। এই ধাপটি বিল্ড স্টেপের মাধ্যমে সময়ের আগে করা যেতে পারে, অথবা রানটাইম কম্পাইলার ব্যবহার করে অন-দ্য-ফ্লাই করা যেতে পারে।

  2. Mount: রানটাইম রেন্ডারার রেন্ডার ফাংশনগুলিকে আহ্বান করে, ফিরে আসা ভার্চুয়াল DOM ট্রিতে চলে এবং এর উপর ভিত্তি করে প্রকৃত DOM নোড তৈরি করে। এই পদক্ষেপটি একটি প্রতিক্রিয়াশীল প্রভাব হিসাবে সঞ্চালিত হয়, তাই এটি ব্যবহৃত সমস্ত প্রতিক্রিয়াশীল নির্ভরতাগুলির উপর নজর রাখে।

  3. Patch: মাউন্টের সময় ব্যবহৃত একটি নির্ভরতা পরিবর্তন হলে, প্রভাব পুনরায় সঞ্চালিত হয়। এই সময়, একটি নতুন, আপডেট করা ভার্চুয়াল DOM tree তৈরি করা হয়েছে৷ রানটাইম রেন্ডারার নতুন tree চলে, এটিকে পুরানোটির সাথে তুলনা করে এবং প্রকৃত DOM-এ প্রয়োজনীয় আপডেটগুলি প্রয়োগ করে৷

render pipeline

Templates vs. Render Functions

Vue টেমপ্লেটগুলি ভার্চুয়াল DOM রেন্ডার ফাংশনে সংকলিত হয়। Vue এপিআইও প্রদান করে যা আমাদের টেমপ্লেট সংকলন ধাপ এড়িয়ে যেতে এবং সরাসরি লেখকের ফাংশন রেন্ডার করতে দেয়। অত্যন্ত গতিশীল লজিক নিয়ে কাজ করার সময় রেন্ডার ফাংশনগুলি টেমপ্লেটগুলির চেয়ে বেশি নমনীয়, কারণ আপনি জাভাস্ক্রিপ্টের সম্পূর্ণ শক্তি ব্যবহার করে vnodes নিয়ে কাজ করতে পারেন।

তাহলে কেন Vue ডিফল্টরূপে টেমপ্লেট সুপারিশ করে? কারণ একটি সংখ্যা আছে:

  1. টেমপ্লেট প্রকৃত HTML এর কাছাকাছি। এটি বিদ্যমান HTML স্নিপেটগুলি পুনরায় ব্যবহার করা, অ্যাক্সেসযোগ্যতার সর্বোত্তম অনুশীলনগুলি প্রয়োগ করা, CSS এর সাথে স্টাইল করা এবং ডিজাইনারদের বুঝতে এবং পরিবর্তন করা সহজ করে তোলে।

  2. টেমপ্লেটগুলি আরও নির্ধারক সিনট্যাক্সের কারণে স্ট্যাটিকভাবে বিশ্লেষণ করা সহজ। এটি Vue-এর টেমপ্লেট কম্পাইলারকে ভার্চুয়াল DOM-এর কর্মক্ষমতা উন্নত করতে অনেক কম্পাইল-টাইম অপ্টিমাইজেশান প্রয়োগ করতে দেয় (যা আমরা নীচে আলোচনা করব)।

অনুশীলনে, অ্যাপ্লিকেশনগুলিতে বেশিরভাগ ব্যবহারের ক্ষেত্রে টেমপ্লেটগুলি যথেষ্ট। রেন্ডার ফাংশনগুলি সাধারণত শুধুমাত্র পুনঃব্যবহারযোগ্য কম্পোনেন্টগুলিতে ব্যবহৃত হয় যা অত্যন্ত গতিশীল রেন্ডারিং যুক্তির সাথে মোকাবিলা করতে হয়। রেন্ডার ফাংশন ব্যবহার সম্পর্কে আরও বিস্তারিতভাবে Render Functions & JSX এ আলোচনা করা হয়েছে।

Compiler-Informed Virtual DOM

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

কিন্তু এটা যে ভাবে হতে হবে না. Vue-তে, ফ্রেমওয়ার্ক কম্পাইলার এবং রানটাইম উভয়ই নিয়ন্ত্রণ করে। এটি আমাদেরকে অনেক কম্পাইল-টাইম অপ্টিমাইজেশান বাস্তবায়ন করতে দেয় যেগুলি শুধুমাত্র একটি শক্তভাবে-সংযুক্ত রেন্ডারার সুবিধা নিতে পারে। কম্পাইলার টেমপ্লেটটি স্থিরভাবে বিশ্লেষণ করতে পারে এবং জেনারেট করা কোডে ইঙ্গিত দিতে পারে যাতে রানটাইম যখনই সম্ভব শর্টকাট নিতে পারে। একই সময়ে, আমরা এখনও প্রান্তের ক্ষেত্রে আরও সরাসরি নিয়ন্ত্রণের জন্য ব্যবহারকারীর রেন্ডার ফাংশন স্তরে নেমে যাওয়ার ক্ষমতা সংরক্ষণ করি। আমরা এই হাইব্রিড পদ্ধতিকে কম্পাইলার-ইনফর্মড ভার্চুয়াল DOM বলি।

নীচে, আমরা ভার্চুয়াল DOM-এর রানটাইম কর্মক্ষমতা উন্নত করতে Vue টেমপ্লেট কম্পাইলার দ্বারা করা কয়েকটি প্রধান অপ্টিমাইজেশন নিয়ে আলোচনা করব।

Static Hoisting

প্রায়শই একটি টেমপ্লেটে এমন কিছু অংশ থাকবে যা কোনো গতিশীল বাইন্ডিং ধারণ করে না:

template
<div>
  <div>foo</div> <!-- hoisted -->
  <div>bar</div> <!-- hoisted -->
  <div>{{ dynamic }}</div>
</div>

Inspect in Template Explorer

foo এবং bar ডিভগুলি স্থির - ভনোডগুলি পুনরায় তৈরি করা এবং প্রতিটি পুনরায় রেন্ডারে তাদের আলাদা করা অপ্রয়োজনীয়। Vue কম্পাইলার স্বয়ংক্রিয়ভাবে তাদের vnode তৈরির কলগুলিকে রেন্ডার ফাংশনের বাইরে তুলে ধরে এবং প্রতিটি রেন্ডারে একই vnode পুনরায় ব্যবহার করে। রেন্ডারার পুরানো vnode এবং নতুন vnode একই রকম হলে সেগুলিকে সম্পূর্ণরূপে আলাদা করা এড়িয়ে যেতে সক্ষম।

উপরন্তু, যখন পর্যাপ্ত পর্যাপ্ত স্ট্যাটিক কম্পোনেন্ট থাকে, তখন সেগুলিকে একটি একক "স্ট্যাটিক ভনোড"-এ ঘনীভূত করা হবে যাতে এই সমস্ত নোডের জন্য প্লেইন HTML স্ট্রিং থাকে (Example)। এই স্ট্যাটিক ভনোডগুলি সরাসরি innerHTML সেট করে মাউন্ট করা হয়। তারা প্রাথমিক মাউন্টে তাদের সংশ্লিষ্ট DOM নোডগুলিও ক্যাশে করে - যদি অ্যাপের একই অংশটি অন্য কোথাও পুনরায় ব্যবহার করা হয়, তাহলে নেটিভ cloneNode() ব্যবহার করে নতুন DOM নোড তৈরি করা হয়, যা অত্যন্ত কার্যকর।

Patch Flags

ডায়নামিক বাইন্ডিং সহ একটি একক কম্পোনেন্টের জন্য, আমরা কম্পাইলের সময় এটি থেকে অনেক তথ্য অনুমান করতে পারি:

template
<!-- class binding only -->
<div :class="{ active }"></div>

<!-- id and value bindings only -->
<input :id="id" :value="value">

<!-- text children only -->
<div>{{ dynamic }}</div>

Inspect in Template Explorer

এই কম্পোনেন্টগুলির জন্য রেন্ডার ফাংশন কোড তৈরি করার সময়, Vue vnode তৈরি কলে তাদের প্রত্যেকের প্রয়োজনীয় আপডেটের প্রকারকে এনকোড করে:

js
createElementVNode("div", {
  class: _normalizeClass({ active: _ctx.active })
}, null, 2 /* CLASS */)

শেষ আর্গুমেন্ট, 2 হল একটি patch flag। একটি কম্পোনেন্টের একাধিক patch ফ্ল্যাগ থাকতে পারে, যা একটি একক সংখ্যায় একত্রিত হবে। রানটাইম রেন্ডারার তারপরে bitwise অপারেশনস ব্যবহার করে flag গুলির বিরুদ্ধে পরীক্ষা করতে পারে যে এটি নির্দিষ্ট কাজ করতে হবে কিনা তা নির্ধারণ করতে:

js
if (vnode.patchFlag & PatchFlags.CLASS /* 2 */) {
  // update the element's class
}

Bitwise চেক অত্যন্ত দ্রুত হয়. patch flag গুলির সাথে, Vue গতিশীল বাইন্ডিং সহ কম্পোনেন্টগুলি আপডেট করার সময় প্রয়োজনীয় ন্যূনতম পরিমাণ কাজ করতে সক্ষম হয়।

Vue একটি vnode-এর বাচ্চাদের ধরনও এনকোড করে। উদাহরণস্বরূপ, একাধিক রুট নোড রয়েছে এমন একটি টেমপ্লেটকে একটি খণ্ড হিসাবে উপস্থাপন করা হয়। বেশিরভাগ ক্ষেত্রে, আমরা নিশ্চিতভাবে জানি যে এই রুট নোডগুলির ক্রম কখনই পরিবর্তন হবে না, তাই এই তথ্যটি রানটাইমে patch flagহিসাবেও সরবরাহ করা যেতে পারে:

js
export function render() {
  return (_openBlock(), _createElementBlock(_Fragment, null, [
    /* children */
  ], 64 /* STABLE_FRAGMENT */))
}

রানটাইম এইভাবে রুট ফ্র্যাগমেন্টের জন্য চাইল্ড-অর্ডার পুনর্মিলনকে সম্পূর্ণভাবে এড়িয়ে যেতে পারে।

Tree Flattening

পূর্ববর্তী উদাহরণ থেকে জেনারেট করা কোডটি আরেকবার দেখে নিলে, আপনি লক্ষ্য করবেন যে প্রত্যাবর্তিত ভার্চুয়াল DOM ট্রিটির মূল একটি বিশেষ createElementBlock() কল ব্যবহার করে তৈরি করা হয়েছে:

js
export function render() {
  return (_openBlock(), _createElementBlock(_Fragment, null, [
    /* children */
  ], 64 /* STABLE_FRAGMENT */))
}

ধারণাগতভাবে, একটি "ব্লক" হল টেমপ্লেটের একটি অংশ যা স্থিতিশীল অভ্যন্তরীণ কাঠামো রয়েছে। এই ক্ষেত্রে, সমগ্র টেমপ্লেটটিতে একটি একক ব্লক রয়েছে কারণ এতে v-if এবং v-for এর মতো কোনো কাঠামোগত নির্দেশ নেই।

প্রতিটি ব্লক patch flagআছে এমন কোনো বংশধর নোড (শুধু সরাসরি চাইল্ডদের নয়) ট্র্যাক করে। উদাহরণ স্বরূপ:

template
<div> <!-- root block -->
  <div>...</div>         <!-- not tracked -->
  <div :id="id"></div>   <!-- tracked -->
  <div>                  <!-- not tracked -->
    <div>{{ bar }}</div> <!-- tracked -->
  </div>
</div>

ফলাফল হল একটি সমতল অ্যারে যা শুধুমাত্র গতিশীল বংশধর নোডগুলি ধারণ করে:

div (block root)
- div with :id binding
- div with {{ bar }} binding

যখন এই কম্পোনেন্টটিকে পুনরায় রেন্ডার করার প্রয়োজন হয়, তখন এটি সম্পূর্ণ tree পরিবর্তে কেবল চ্যাপ্টা tree অতিক্রম করতে হবে। এটিকে Tree Flattening বলা হয়, এবং এটি ভার্চুয়াল DOM পুনর্মিলনের সময় যে নোডগুলিকে অতিক্রম করতে হবে তার সংখ্যাকে ব্যাপকভাবে হ্রাস করে। টেমপ্লেটের যেকোনো স্ট্যাটিক অংশ কার্যকরভাবে এড়িয়ে যাওয়া হয়।

v-if এবং v-for নির্দেশাবলী নতুন ব্লক নোড তৈরি করবে:

template
<div> <!-- root block -->
  <div>
    <div v-if> <!-- if block -->
      ...
    <div>
  </div>
</div>

একটি চাইল্ড ব্লক প্যারেন্ট ব্লকের গতিশীল বংশধরের অ্যারের ভিতরে ট্র্যাক করা হয়। এটি মূল ব্লকের জন্য একটি স্থিতিশীল কাঠামো বজায় রাখে।

Impact on SSR Hydration

patch flags এবং tree চ্যাপ্টাকরণ উভয়ই Vue এর SSR হাইড্রেশন কর্মক্ষমতাকে ব্যাপকভাবে উন্নত করে:

  • একক কম্পোনেন্ট হাইড্রেশন সংশ্লিষ্ট vnode এর patch flag উপর ভিত্তি করে দ্রুত পাথ নিতে পারে।

  • হাইড্রেশনের সময় শুধুমাত্র ব্লক নোড এবং তাদের গতিশীল বংশধরগুলিকে অতিক্রম করতে হবে, কার্যকরভাবে টেমপ্লেট স্তরে আংশিক হাইড্রেশন অর্জন করতে হবে।

Rendering Mechanism has loaded