আজকের ওয়েব অ্যাপ্লিকেশনগুলি বেশ বড় হতে পারে, বিশেষ করে জাভাস্ক্রিপ্টের অংশ। 2018 সালের মাঝামাঝি পর্যন্ত, HTTP আর্কাইভ মোবাইল ডিভাইসে জাভাস্ক্রিপ্টের মাঝামাঝি স্থানান্তর আকারকে প্রায় 350 KB রাখে। এবং এই শুধু স্থানান্তর আকার! নেটওয়ার্কের মাধ্যমে পাঠানো হলে জাভাস্ক্রিপ্ট প্রায়ই সংকুচিত হয়, যার মানে ব্রাউজার এটি ডিকম্প্রেস করার পরে জাভাস্ক্রিপ্টের প্রকৃত পরিমাণ কিছুটা বেশি হয়। এটি উল্লেখ করা গুরুত্বপূর্ণ, কারণ যতদূর সম্পদ প্রক্রিয়াকরণ উদ্বিগ্ন, কম্প্রেশন অপ্রাসঙ্গিক। 900 KB ডিকম্প্রেসড জাভাস্ক্রিপ্ট এখনও পার্সার এবং কম্পাইলারের কাছে 900 KB, যদিও কম্প্রেস করার সময় এটি প্রায় 300 KB হতে পারে।
জাভাস্ক্রিপ্ট প্রক্রিয়া করার জন্য একটি ব্যয়বহুল সম্পদ। ছবিগুলির বিপরীতে যেগুলি একবার ডাউনলোড করার পরে তুলনামূলকভাবে তুচ্ছ ডিকোড সময় লাগে, জাভাস্ক্রিপ্ট অবশ্যই পার্স করা, কম্পাইল করা এবং তারপরে শেষ পর্যন্ত কার্যকর করা উচিত। বাইটের জন্য বাইট, এটি জাভাস্ক্রিপ্টকে অন্যান্য ধরণের সংস্থানগুলির তুলনায় আরও ব্যয়বহুল করে তোলে।
![জাভাস্ক্রিপ্টের 170 KB প্রক্রিয়াকরণের সময় বনাম একটি সমতুল্য আকারের JPEG চিত্রের তুলনা করে একটি চিত্র৷ জাভাস্ক্রিপ্ট রিসোর্সটি JPEG-এর তুলনায় বাইটের জন্য অনেক বেশি রিসোর্স-ইনটেনসিভ বাইট।](https://web.dev/static/articles/reduce-javascript-payloads-with-tree-shaking/image/a-diagram-comparing-proc-5b49bd91e7285.png?authuser=2&hl=bn)
যদিও JavaScript ইঞ্জিনগুলির কার্যকারিতা উন্নত করার জন্য ক্রমাগত উন্নতি করা হচ্ছে , জাভাস্ক্রিপ্টের কার্যকারিতা উন্নত করা — সবসময়ের মতো — বিকাশকারীদের জন্য একটি কাজ৷
সেই লক্ষ্যে, জাভাস্ক্রিপ্ট কর্মক্ষমতা উন্নত করার কৌশল রয়েছে। কোড স্প্লিটিং হল এমনই একটি কৌশল যা অ্যাপ্লিকেশন জাভাস্ক্রিপ্টকে খণ্ডে বিভক্ত করে কার্যক্ষমতা উন্নত করে এবং সেই অংশগুলিকে শুধুমাত্র একটি অ্যাপ্লিকেশনের রুটে পরিবেশন করে যার প্রয়োজন হয়।
যদিও এই কৌশলটি কাজ করে, এটি জাভাস্ক্রিপ্ট-ভারী অ্যাপ্লিকেশনগুলির একটি সাধারণ সমস্যার সমাধান করে না, যা কখনও ব্যবহৃত হয় না এমন কোডের অন্তর্ভুক্তি। গাছ কাঁপানো এই সমস্যা সমাধানের চেষ্টা।
গাছ কাঁপানো কি?
গাছ কাঁপানো মৃত কোড নির্মূল একটি ফর্ম. শব্দটি রোলআপ দ্বারা জনপ্রিয় হয়েছিল , কিন্তু মৃত কোড নির্মূলের ধারণাটি কিছু সময়ের জন্য বিদ্যমান ছিল। ধারণাটি ওয়েবপ্যাকে কেনাকাটাও খুঁজে পেয়েছে, যা একটি নমুনা অ্যাপের মাধ্যমে এই নিবন্ধে প্রদর্শিত হয়েছে।
"বৃক্ষ কাঁপানো" শব্দটি আপনার প্রয়োগের মানসিক মডেল এবং গাছের মতো কাঠামো হিসাবে এর নির্ভরতা থেকে এসেছে। গাছের প্রতিটি নোড একটি নির্ভরতার প্রতিনিধিত্ব করে যা আপনার অ্যাপের জন্য স্বতন্ত্র কার্যকারিতা প্রদান করে। আধুনিক অ্যাপ্লিকেশানগুলিতে, এই নির্ভরতাগুলি স্ট্যাটিক import
স্টেটমেন্টের মাধ্যমে আনা হয় যেমন:
// Import all the array utilities!
import arrayUtils from "array-utils";
যখন একটি অ্যাপ তরুণ হয়—একটি চারা, যদি আপনি চান—তার কিছু নির্ভরতা থাকতে পারে। এটি সর্বাধিক ব্যবহার করছে—যদি সব না হয়—আপনি যে নির্ভরতাগুলি যোগ করেন। আপনার অ্যাপটি পরিপক্ক হওয়ার সাথে সাথে, আরও নির্ভরতা যুক্ত হতে পারে। যৌগিক বিষয়গুলির জন্য, পুরানো নির্ভরতাগুলি ব্যবহারের বাইরে পড়ে যায়, তবে আপনার কোডবেস থেকে ছাঁটাই নাও হতে পারে। শেষ ফলাফল হল যে একটি অ্যাপ প্রচুর অব্যবহৃত জাভাস্ক্রিপ্ট সহ শিপিং শেষ করে৷ ES6 মডিউলগুলির নির্দিষ্ট অংশগুলিতে স্ট্যাটিক import
স্টেটমেন্টগুলি কীভাবে টানছে তার সুবিধা গ্রহণ করে গাছ কাঁপানো এটিকে সম্বোধন করে:
// Import only some of the utilities!
import { unique, implode, explode } from "array-utils";
এই import
উদাহরণ এবং আগেরটির মধ্যে পার্থক্য হল যে "array-utils"
মডিউল থেকে সবকিছু ইম্পোর্ট করার পরিবর্তে—যা অনেক কোড হতে পারে)-এই উদাহরণটি শুধুমাত্র নির্দিষ্ট অংশ আমদানি করে। dev বিল্ডগুলিতে, এটি কিছুই পরিবর্তন করে না, কারণ সম্পূর্ণ মডিউলটি নির্বিশেষে আমদানি করা হয়। প্রোডাকশন বিল্ডে, ওয়েবপ্যাককে ES6 মডিউলগুলি থেকে রপ্তানি বন্ধ করার জন্য কনফিগার করা যেতে পারে যেগুলি স্পষ্টভাবে আমদানি করা হয়নি, এই উৎপাদনগুলিকে ছোট করে তোলে। এই নির্দেশিকাতে, আপনি শিখবেন কিভাবে এটি করতে হয়!
একটি গাছ ঝাঁকান সুযোগ খোঁজা
দৃষ্টান্তমূলক উদ্দেশ্যে, একটি নমুনা এক-পৃষ্ঠার অ্যাপ উপলব্ধ যা দেখায় যে কীভাবে গাছ কাঁপানো কাজ করে। আপনি এটি ক্লোন করতে পারেন এবং আপনি যদি চান তবে অনুসরণ করতে পারেন, তবে আমরা এই গাইডে একসাথে পথের প্রতিটি ধাপ কভার করব, তাই ক্লোনিং প্রয়োজনীয় নয় (যদি না হাতে-কলমে শেখা আপনার জিনিস হয়)।
নমুনা অ্যাপটি গিটার ইফেক্ট প্যাডেলের একটি অনুসন্ধানযোগ্য ডাটাবেস। আপনি একটি প্রশ্ন লিখুন এবং প্রভাব প্যাডেলগুলির একটি তালিকা প্রদর্শিত হবে।
![গিটার ইফেক্ট প্যাডেলগুলির একটি ডাটাবেস অনুসন্ধানের জন্য একটি নমুনা এক পৃষ্ঠার অ্যাপ্লিকেশনের একটি স্ক্রিনশট৷](https://web.dev/static/articles/reduce-javascript-payloads-with-tree-shaking/image/a-screenshot-a-sample-p-c7096b568d49b.png?authuser=2&hl=bn)
যে আচরণটি এই অ্যাপটিকে চালিত করে তা বিক্রেতা (যেমন, প্রেক্ট এবং ইমোশন ) এবং অ্যাপ-নির্দিষ্ট কোড বান্ডেলগুলিতে (বা "খণ্ডগুলি", যেমন ওয়েবপ্যাক সেগুলিকে কল করে):
![Chrome-এর DevTools-এর নেটওয়ার্ক প্যানেলে দেখানো দুটি অ্যাপ্লিকেশান কোড বান্ডেলের (বা খণ্ডগুলির) একটি স্ক্রিনশট৷](https://web.dev/static/articles/reduce-javascript-payloads-with-tree-shaking/image/a-screenshot-two-applica-13160017c55f2.png?authuser=2&hl=bn)
উপরের চিত্রে দেখানো জাভাস্ক্রিপ্ট বান্ডেলগুলি হল প্রোডাকশন বিল্ডস, যার অর্থ এগুলি কুশ্রীকরণের মাধ্যমে অপ্টিমাইজ করা হয়েছে৷ একটি অ্যাপ-নির্দিষ্ট বান্ডিলের জন্য 21.1 KB খারাপ নয়, তবে এটি লক্ষ করা উচিত যে কোনও গাছ কাঁপছে না। আসুন অ্যাপ কোডটি দেখুন এবং এটি ঠিক করতে কী করা যেতে পারে তা দেখুন।
যেকোন প্রয়োগে, গাছ কাঁপানোর সুযোগ খোঁজার সাথে স্ট্যাটিক import
স্টেটমেন্টের সন্ধান করতে হবে। প্রধান উপাদান ফাইলের শীর্ষের কাছে , আপনি এইরকম একটি লাইন দেখতে পাবেন:
import * as utils from "../../utils/utils";
আপনি বিভিন্ন উপায়ে ES6 মডিউল আমদানি করতে পারেন, কিন্তু এর মতো আপনার দৃষ্টি আকর্ষণ করা উচিত। এই নির্দিষ্ট লাইনটি বলে " utils
মডিউল থেকে সবকিছু import
এবং এটিকে utils
নামক একটি নামস্থানে রাখুন।" এখানে জিজ্ঞাসা করা বড় প্রশ্ন হল, "সেই মডিউলে কতটা জিনিস আছে?"
আপনি যদি utils
মডিউল সোর্স কোড দেখেন, আপনি দেখতে পাবেন সেখানে প্রায় 1,300 লাইন কোড রয়েছে।
আপনি যে সব জিনিস প্রয়োজন ? সেই নেমস্পেসের কতগুলি দৃষ্টান্ত আসে তা দেখতে utils
মডিউল আমদানি করে এমন মূল উপাদান ফাইলটি অনুসন্ধান করে দুবার পরীক্ষা করা যাক।
!['utils'-এর জন্য একটি পাঠ্য সম্পাদকে অনুসন্ধানের একটি স্ক্রিনশট, শুধুমাত্র 3টি ফলাফল প্রদান করে।](https://web.dev/static/articles/reduce-javascript-payloads-with-tree-shaking/image/a-screenshot-a-search-a-b9c933e924ee5.png?authuser=2&hl=bn)
utils
নেমস্পেস থেকে টন মডিউল আমদানি করেছি তা শুধুমাত্র প্রধান উপাদান ফাইলের মধ্যে তিনবার আহ্বান করা হয়েছে। দেখা যাচ্ছে, utils
নেমস্পেস আমাদের অ্যাপ্লিকেশনে মাত্র তিনটি জায়গায় প্রদর্শিত হবে-কিন্তু কোন কাজের জন্য? আপনি যদি মূল কম্পোনেন্ট ফাইলটি আবার দেখেন তবে এটি শুধুমাত্র একটি ফাংশন বলে মনে হচ্ছে, যা হল utils.simpleSort
, যা সার্চ ফলাফলের তালিকাকে বেশ কয়েকটি মানদণ্ড দ্বারা সাজানোর জন্য ব্যবহৃত হয় যখন সাজানোর ড্রপডাউনগুলি পরিবর্তন করা হয়:
if (this.state.sortBy === "model") {
// `simpleSort` gets used here...
json = utils.simpleSort(json, "model", this.state.sortOrder);
} else if (this.state.sortBy === "type") {
// ..and here...
json = utils.simpleSort(json, "type", this.state.sortOrder);
} else {
// ..and here.
json = utils.simpleSort(json, "manufacturer", this.state.sortOrder);
}
একগুচ্ছ রপ্তানি সহ একটি 1,300 লাইন ফাইলের মধ্যে শুধুমাত্র একটি ব্যবহার করা হয়। এর ফলে প্রচুর অব্যবহৃত জাভাস্ক্রিপ্ট পাঠানো হয়।
যদিও এই উদাহরণ অ্যাপটি স্বীকৃতভাবে কিছুটা কাল্পনিক, এটি এই সত্যটিকে পরিবর্তন করে না যে এই সিন্থেটিক ধরণের দৃশ্যের সাথে আপনি একটি প্রোডাকশন ওয়েব অ্যাপে সম্মুখীন হতে পারেন এমন প্রকৃত অপ্টিমাইজেশন সুযোগগুলির সাথে সাদৃশ্যপূর্ণ। এখন আপনি গাছ কাঁপানোর জন্য একটি সুযোগ চিহ্নিত করেছেন দরকারী হতে, এটি আসলে কীভাবে করা হয়?
Babel কে ES6 মডিউল CommonJS মডিউলে স্থানান্তর করা থেকে বিরত রাখা
বাবেল একটি অপরিহার্য হাতিয়ার, কিন্তু এটি গাছ কাঁপানোর প্রভাবগুলি পর্যবেক্ষণ করা আরও কঠিন করে তুলতে পারে। আপনি যদি @babel/preset-env
ব্যবহার করেন, তাহলে Babel ES6 মডিউলকে আরও ব্যাপকভাবে সামঞ্জস্যপূর্ণ CommonJS মডিউলে রূপান্তরিত করতে পারে —অর্থাৎ, import
পরিবর্তে আপনার require
মডিউল।
যেহেতু কমনজেএস মডিউলগুলির জন্য গাছ কাঁপানো আরও কঠিন, আপনি যদি সেগুলি ব্যবহার করার সিদ্ধান্ত নেন তবে ওয়েবপ্যাক বান্ডিলগুলি থেকে কী ছাঁটাই করতে হবে তা জানবে না। সমাধান হল স্পষ্টভাবে ES6 মডিউলগুলিকে একা ছেড়ে দেওয়ার জন্য @babel/preset-env
কনফিগার করা। আপনি যেখানেই Babel কনফিগার করেন—সেটি babel.config.js
বা package.json
তেই হোক—এতে কিছু অতিরিক্ত কিছু যোগ করা জড়িত:
// babel.config.js
export default {
presets: [
[
"@babel/preset-env", {
modules: false
}
]
]
}
modules: false
আপনার @babel/preset-env
কনফিগারেশনে মিথ্যা ব্যাবেলকে পছন্দসই আচরণ করতে দেয়, যা ওয়েবপ্যাককে আপনার নির্ভরতা ট্রি বিশ্লেষণ করতে এবং অব্যবহৃত নির্ভরতাগুলি ঝেড়ে ফেলতে দেয়।
পার্শ্ব প্রতিক্রিয়া মাথায় রাখা
আপনার অ্যাপ থেকে নির্ভরতা কাঁপানোর সময় বিবেচনা করার আরেকটি দিক হল আপনার প্রকল্পের মডিউলগুলির পার্শ্বপ্রতিক্রিয়া আছে কিনা। একটি পার্শ্ব প্রতিক্রিয়ার একটি উদাহরণ হল যখন একটি ফাংশন তার নিজস্ব সুযোগের বাইরে কিছু পরিবর্তন করে, যা তার সম্পাদনের একটি পার্শ্ব প্রতিক্রিয়া :
let fruits = ["apple", "orange", "pear"];
console.log(fruits); // (3) ["apple", "orange", "pear"]
const addFruit = function(fruit) {
fruits.push(fruit);
};
addFruit("kiwi");
console.log(fruits); // (4) ["apple", "orange", "pear", "kiwi"]
এই উদাহরণে, addFruit
একটি পার্শ্ব প্রতিক্রিয়া তৈরি করে যখন এটি fruits
অ্যারে পরিবর্তন করে, যা তার সুযোগের বাইরে।
পার্শ্ব প্রতিক্রিয়াগুলি ES6 মডিউলগুলিতেও প্রযোজ্য, এবং এটি গাছ কাঁপানোর প্রসঙ্গে গুরুত্বপূর্ণ। যে মডিউলগুলি অনুমানযোগ্য ইনপুটগুলি নেয় এবং তাদের নিজস্ব সুযোগের বাইরে কিছু পরিবর্তন না করে সমানভাবে অনুমানযোগ্য আউটপুট তৈরি করে সেগুলি নির্ভরতা যা নিরাপদে বাদ দেওয়া যেতে পারে যদি আমরা সেগুলি ব্যবহার না করি। তারা স্বয়ংসম্পূর্ণ, কোডের মডুলার টুকরা। অতএব, "মডিউল"।
যেখানে ওয়েবপ্যাক সম্পর্কিত, একটি ইঙ্গিত ব্যবহার করা যেতে পারে যে একটি প্যাকেজ এবং তার নির্ভরতা পার্শ্বপ্রতিক্রিয়া মুক্ত "সাইড ইফেক্টস" উল্লেখ করে: একটি প্রকল্পের package.json
ফাইলে "sideEffects": false
:
{
"name": "webpack-tree-shaking-example",
"version": "1.0.0",
"sideEffects": false
}
বিকল্পভাবে, আপনি ওয়েবপ্যাককে বলতে পারেন কোন নির্দিষ্ট ফাইলগুলি পার্শ্ব প্রতিক্রিয়া-মুক্ত নয়:
{
"name": "webpack-tree-shaking-example",
"version": "1.0.0",
"sideEffects": [
"./src/utils/utils.js"
]
}
পরবর্তী উদাহরণে, নির্দিষ্ট করা হয়নি এমন যেকোন ফাইলকে পার্শ্বপ্রতিক্রিয়ামুক্ত বলে ধরে নেওয়া হবে। আপনি যদি আপনার package.json
ফাইলে এটি যোগ করতে না চান, তাহলে আপনি module.rules
এর মাধ্যমে আপনার ওয়েবপ্যাক কনফিগারেশনে এই পতাকাটি নির্দিষ্ট করতে পারেন ।
শুধুমাত্র যা প্রয়োজন তা আমদানি করা হচ্ছে
ব্যাবেলকে ES6 মডিউলগুলিকে একা ছেড়ে দেওয়ার নির্দেশ দেওয়ার পরে, শুধুমাত্র utils
মডিউল থেকে প্রয়োজনীয় ফাংশনগুলি আনতে আমাদের import
সিনট্যাক্সে একটি সামান্য সমন্বয় প্রয়োজন৷ এই গাইডের উদাহরণে, যা প্রয়োজন তা হল simpleSort
ফাংশন:
import { simpleSort } from "../../utils/utils";
কারণ সমগ্র utils
মডিউলের পরিবর্তে শুধুমাত্র simpleSort
আমদানি করা হচ্ছে, utils.simpleSort
এর প্রতিটি উদাহরণকে simpleSort
এ পরিবর্তন করতে হবে:
if (this.state.sortBy === "model") {
json = simpleSort(json, "model", this.state.sortOrder);
} else if (this.state.sortBy === "type") {
json = simpleSort(json, "type", this.state.sortOrder);
} else {
json = simpleSort(json, "manufacturer", this.state.sortOrder);
}
এই উদাহরণে কাজ করার জন্য গাছ কাঁপানোর জন্য যা প্রয়োজন তা হওয়া উচিত। নির্ভরতা ট্রি কাঁপানোর আগে এটি ওয়েবপ্যাক আউটপুট:
Asset Size Chunks Chunk Names
js/vendors.16262743.js 37.1 KiB 0 [emitted] vendors
js/main.797ebb8b.js 20.8 KiB 1 [emitted] main
গাছ কাঁপানো সফল হওয়ার পরে এটি হল আউটপুট:
Asset Size Chunks Chunk Names
js/vendors.45ce9b64.js 36.9 KiB 0 [emitted] vendors
js/main.559652be.js 8.46 KiB 1 [emitted] main
যদিও উভয় বান্ডিল সঙ্কুচিত হয়, এটি সত্যিই main
বান্ডিল যা সবচেয়ে বেশি উপকৃত হয়। utils
মডিউলের অব্যবহৃত অংশগুলিকে ঝাঁকিয়ে, main
বান্ডিল প্রায় 60% সঙ্কুচিত হয়। এটি শুধুমাত্র স্ক্রিপ্টটি ডাউনলোড করতে যে সময় নেয় তা কম করে না, তবে প্রক্রিয়াকরণের সময়ও কমিয়ে দেয়।
কিছু গাছ ঝাঁকান যান!
গাছ কাঁপানো থেকে আপনি যে মাইলেজ পাবেন তা আপনার অ্যাপ এবং এর নির্ভরতা এবং আর্কিটেকচারের উপর নির্ভর করে। এটা চেষ্টা করুন! যদি আপনি জানেন যে আপনি এই অপ্টিমাইজেশানটি সম্পাদন করার জন্য আপনার মডিউল বান্ডলার সেট আপ করেন নি, তাহলে এটি আপনার অ্যাপ্লিকেশনটিকে কীভাবে উপকৃত করে তা চেষ্টা করে দেখার কোন ক্ষতি নেই।
আপনি গাছ কাঁপানো থেকে একটি উল্লেখযোগ্য কর্মক্ষমতা লাভ উপলব্ধি করতে পারেন, বা খুব বেশি নয়। কিন্তু প্রোডাকশন বিল্ডে এই অপ্টিমাইজেশনের সুবিধা নিতে আপনার বিল্ড সিস্টেম কনফিগার করে এবং আপনার অ্যাপ্লিকেশনের জন্য যা প্রয়োজন তা বেছে বেছে আমদানি করে, আপনি সক্রিয়ভাবে আপনার অ্যাপ্লিকেশন বান্ডিলগুলি যতটা সম্ভব ছোট রাখবেন।
ক্রিস্টোফার ব্যাক্সটার, জেসন মিলার , অ্যাডি ওসমানী , জেফ পসনিক , স্যাম স্যাকোন এবং ফিলিপ ওয়ালটনকে তাদের মূল্যবান প্রতিক্রিয়ার জন্য বিশেষ ধন্যবাদ, যা এই নিবন্ধটির গুণমানকে উল্লেখযোগ্যভাবে উন্নত করেছে।