नई JavaScript डिपेंडेंसी और आउटपुट को चालू करके, परफ़ॉर्मेंस को बेहतर बनाएं.
90% से ज़्यादा ब्राउज़र, आधुनिक JavaScript को चला सकते हैं. हालांकि, वेब पर परफ़ॉर्मेंस से जुड़ी समस्याओं का मुख्य सोर्स, लेगसी JavaScript का मौजूद होना है.
नया JavaScript
आधुनिक JavaScript को किसी खास ECMAScript के स्पेसिफ़िकेशन वर्शन में लिखे गए कोड के तौर पर नहीं जाना जाता. इसे सिंटैक्स के तौर पर जाना जाता है, जो सभी आधुनिक ब्राउज़र के साथ काम करता है. Chrome, Edge, Firefox, और Safari जैसे आधुनिक वेब ब्राउज़र, ब्राउज़र मार्केट के 90% से ज़्यादा हिस्से पर काबिज हैं. साथ ही, एक ही रेंडरिंग इंजन पर काम करने वाले अलग-अलग ब्राउज़र, 5% हिस्से पर काबिज हैं. इसका मतलब है कि दुनिया भर में 95% वेब ट्रैफ़िक, ऐसे ब्राउज़र से आता है जो पिछले 10 सालों में, JavaScript भाषा की सबसे ज़्यादा इस्तेमाल की जाने वाली सुविधाओं के साथ काम करते हैं. इनमें ये शामिल हैं:
- क्लास (ES2015)
- ऐरो फ़ंक्शन (ES2015)
- जनरेटर (ES2015)
- ब्लॉक स्कोपिंग (ES2015)
- डेटा स्ट्रक्चर में बदलाव करना (ES2015)
- बाकी और स्प्रेड पैरामीटर (ES2015)
- ऑब्जेक्ट का शॉर्टहैंड (ES2015)
- Async/await (ES2017)
आम तौर पर, भाषा के स्पेसिफ़िकेशन के नए वर्शन की सुविधाएं, आधुनिक ब्राउज़र पर एक जैसी काम नहीं करती हैं. उदाहरण के लिए, ES2020 और ES2021 की कई सुविधाएं, ब्राउज़र मार्केट के सिर्फ़ 70% हिस्से में काम करती हैं. हालांकि, यह ब्राउज़र के ज़्यादातर हिस्से में काम करती हैं, लेकिन इन सुविधाओं पर सीधे तौर पर भरोसा करना सुरक्षित नहीं है. इसका मतलब है कि "नया" JavaScript, समय के साथ बदलता रहता है. हालांकि, ES2017 में ब्राउज़र के साथ काम करने की सबसे ज़्यादा सुविधाएं उपलब्ध हैं. इसमें, सिंटैक्स की ज़्यादातर सामान्य सुविधाएं शामिल हैं. दूसरे शब्दों में, ES2017, आज के सिंटैक्स के सबसे करीब है.
लेगसी JavaScript
लेगसी JavaScript ऐसा कोड है जो खास तौर पर, ऊपर बताई गई भाषा की सभी सुविधाओं का इस्तेमाल नहीं करता. ज़्यादातर डेवलपर, मॉडर्न सिंटैक्स का इस्तेमाल करके अपना सोर्स कोड लिखते हैं. हालांकि, वे ब्राउज़र के साथ बेहतर तरीके से काम करने के लिए, सभी कोड को लेगसी सिंटैक्स में कंपाइल करते हैं. लेगसी सिंटैक्स में कॉम्पाइल करने से, ब्राउज़र के साथ काम करने की सुविधा बढ़ती है. हालांकि, अक्सर इसका असर उतना नहीं होता जितना हमें लगता है. कई मामलों में, सहायता की दर 95% से बढ़कर 98% हो जाती है. हालांकि, इसके लिए ज़्यादा खर्च करना पड़ता है:
आम तौर पर, लेगसी JavaScript, मिलते-जुलते आधुनिक कोड से करीब 20% बड़ा और धीमा होता है. टूल की कमी और गलत कॉन्फ़िगरेशन की वजह से, अक्सर इस अंतर को और भी बढ़ा दिया जाता है.
इंस्टॉल की गई लाइब्रेरी, आम तौर पर प्रोडक्शन के लिए इस्तेमाल किए जाने वाले 90% JavaScript कोड के लिए ज़िम्मेदार होती हैं. लाइब्रेरी कोड में, polyfill और हेल्पर के डुप्लीकेट होने की वजह से, लेगसी JavaScript का ओवरहेड और भी ज़्यादा होता है. हालांकि, नए कोड को पब्लिश करके, इस ओवरहेड से बचा जा सकता है.
npm पर आधुनिक JavaScript
हाल ही में, Node.js ने "exports"
फ़ील्ड को स्टैंडर्ड किया है, ताकि किसी पैकेज के एंट्री पॉइंट तय किए जा सकें:
{
"exports": "./index.js"
}
"exports"
फ़ील्ड से रेफ़र किए गए मॉड्यूल का मतलब है कि Node का कम से कम 12.8 वर्शन होना चाहिए, जो ES2019 के साथ काम करता है. इसका मतलब है कि "exports"
फ़ील्ड का इस्तेमाल करके रेफ़र किए गए किसी भी मॉड्यूल को नए वर्शन के JavaScript में लिखा जा सकता है. पैकेज का इस्तेमाल करने वाले लोगों को यह मानना होगा कि "exports"
फ़ील्ड वाले मॉड्यूल में आधुनिक कोड है और ज़रूरत पड़ने पर, उन्हें ट्रांसपाइल किया जा सकता है.
सिर्फ़ मॉडर्न
अगर आपको किसी पैकेज को आधुनिक कोड के साथ पब्लिश करना है और उसे डिपेंडेंसी के तौर पर इस्तेमाल करने पर, ट्रांसपाइल करने की ज़िम्मेदारी उपयोगकर्ता पर छोड़नी है, तो सिर्फ़ "exports"
फ़ील्ड का इस्तेमाल करें.
{
"name": "foo",
"exports": "./modern.js"
}
लेगसी फ़ॉलबैक वाला आधुनिक वर्शन
मॉडर्न कोड का इस्तेमाल करके अपना पैकेज पब्लिश करने के लिए, "main"
के साथ "exports"
फ़ील्ड का इस्तेमाल करें. साथ ही, लेगसी ब्राउज़र के लिए ES5 + CommonJS फ़ॉलबैक भी शामिल करें.
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs"
}
लेगसी फ़ॉलबैक और ईएसएम बंडलर ऑप्टिमाइज़ेशन के साथ आधुनिक
फ़ॉलबैक CommonJS एंट्रीपॉइंट तय करने के अलावा, "module"
फ़ील्ड का इस्तेमाल, किसी मिलते-जुलते लेगसी फ़ॉलबैक बंडल पर ले जाने के लिए किया जा सकता है. हालांकि, यह ऐसा बंडल होना चाहिए जो JavaScript मॉड्यूल सिंटैक्स (import
और export
) का इस्तेमाल करता हो.
{
"name": "foo",
"exports": "./modern.js",
"main": "./legacy.cjs",
"module": "./module.js"
}
webpack और Rollup जैसे कई बंडलर, मॉड्यूल की सुविधाओं का फ़ायदा पाने और ट्री शेकिंग को चालू करने के लिए, इस फ़ील्ड का इस्तेमाल करते हैं.
यह अब भी एक लेगसी बंडल है, जिसमें import
/export
सिंटैक्स के अलावा कोई आधुनिक कोड नहीं है. इसलिए, इस तरीके का इस्तेमाल करके, आधुनिक कोड को लेगसी फ़ॉलबैक के साथ शिप करें, जो अब भी बंडलिंग के लिए ऑप्टिमाइज़ किया गया है.
ऐप्लिकेशन में आधुनिक JavaScript
वेब ऐप्लिकेशन में, तीसरे पक्ष की डिपेंडेंसी, आम तौर पर ज़्यादातर प्रोडक्शन JavaScript कोड का हिस्सा होती हैं. हालांकि, npm डिपेंडेंसी को पहले से ही लेगसी ES5 सिंटैक्स के तौर पर पब्लिश किया जाता रहा है, लेकिन अब यह एक सुरक्षित अनुमान नहीं है. साथ ही, डिपेंडेंसी के अपडेट से आपके ऐप्लिकेशन में ब्राउज़र की सहायता बंद होने का खतरा भी रहता है.
npm पैकेज की बढ़ती संख्या, आधुनिक JavaScript पर माइग्रेट हो रही है. इसलिए, यह पक्का करना ज़रूरी है कि उन्हें मैनेज करने के लिए, बिल्ड टूल सेट अप किया गया हो. ऐसा हो सकता है कि आपके काम के कुछ npm पैकेज, पहले से ही आधुनिक भाषा की सुविधाओं का इस्तेमाल कर रहे हों. पुराने ब्राउज़र में अपने ऐप्लिकेशन को काम करते हुए बनाए रखने के लिए, npm से आधुनिक कोड इस्तेमाल करने के कई विकल्प उपलब्ध हैं. हालांकि, आम तौर पर, बिल्ड सिस्टम को डिपेंडेंसी को उसी सिंटैक्स टारगेट में ट्रांसपाइल करना होता है जो आपके सोर्स कोड के लिए इस्तेमाल किया जाता है.
webpack
webpack 5 में, अब यह कॉन्फ़िगर किया जा सकता है कि बंडल और मॉड्यूल के लिए कोड जनरेट करते समय, webpack किस सिंटैक्स का इस्तेमाल करेगा. इससे आपके कोड या डिपेंडेंसी को ट्रांसपाइल नहीं किया जाता. इसका असर सिर्फ़ webpack से जनरेट किए गए "ग्लू" कोड पर पड़ता है. ब्राउज़र के साथ काम करने वाले टारेट की जानकारी देने के लिए, अपने प्रोजेक्ट में browserslist कॉन्फ़िगरेशन जोड़ें या सीधे अपने वेबपैक कॉन्फ़िगरेशन में ऐसा करें:
module.exports = {
target: ['web', 'es2017'],
};
webpack को कॉन्फ़िगर करके, ऑप्टिमाइज़ किए गए बंडल जनरेट किए जा सकते हैं. ये बंडल, आधुनिक ES मॉड्यूल वाले एनवायरमेंट को टारगेट करते समय, ग़ैर-ज़रूरी रैपर फ़ंक्शन को हटा देते हैं. इससे webpack को भी कॉन्फ़िगर किया जाता है, ताकि वह <script type="module">
का इस्तेमाल करके, कोड के अलग-अलग हिस्सों वाले बंडल लोड कर सके.
module.exports = {
target: ['web', 'es2017'],
output: {
module: true,
},
experiments: {
outputModule: true,
},
};
Optimize प्लग इन और BabelEsmPlugin जैसे कई वेबपैक प्लग इन उपलब्ध हैं. इनकी मदद से, आधुनिक JavaScript को कॉम्पाइल और शिप किया जा सकता है. साथ ही, ये प्लग इन लेगसी ब्राउज़र के साथ भी काम करते हैं.
Optimize प्लग इन
Optimize प्लग इन एक वेबपैक प्लग इन है. यह हर सोर्स फ़ाइल के बजाय, बंडल किए गए फ़ाइनल कोड को मॉडर्न से लेगसी JavaScript में बदलता है. यह एक ऐसा सेटअप है जिसमें सभी चीज़ें अपने-आप काम करती हैं. इससे आपके वेबपैक कॉन्फ़िगरेशन को यह मानने में मदद मिलती है कि सभी चीज़ें आधुनिक JavaScript हैं. साथ ही, इसमें एक से ज़्यादा आउटपुट या सिंटैक्स के लिए कोई खास शाखा नहीं होती.
Optimize प्लग इन, अलग-अलग मॉड्यूल के बजाय बंडल पर काम करता है. इसलिए, यह आपके ऐप्लिकेशन के कोड और आपकी डिपेंडेंसी को बराबर प्रोसेस करता है. इससे npm से आधुनिक JavaScript डिपेंडेंसी का इस्तेमाल करना सुरक्षित हो जाता है, क्योंकि उनके कोड को बंडल किया जाएगा और सही सिंटैक्स में ट्रांसपाइल किया जाएगा. यह उन पारंपरिक तरीकों से भी तेज़ हो सकता है जिनमें कंपाइल करने के दो चरण होते हैं. साथ ही, यह मॉडर्न और लेगसी ब्राउज़र के लिए अलग-अलग बंडल जनरेट करता है. बंडल के दो सेट, module/nomodule पैटर्न का इस्तेमाल करके लोड किए जाने के लिए डिज़ाइन किए गए हैं.
// webpack.config.js
const OptimizePlugin = require('optimize-plugin');
module.exports = {
// ...
plugins: [new OptimizePlugin()],
};
Optimize Plugin
, कस्टम वेबपैक कॉन्फ़िगरेशन की तुलना में तेज़ और ज़्यादा असरदार हो सकता है. आम तौर पर, कस्टम वेबपैक कॉन्फ़िगरेशन, मॉडर्न और लेगसी कोड को अलग-अलग बंडल करते हैं. यह आपके लिए Babel को भी चलाता है. साथ ही, Terser का इस्तेमाल करके बंडल को छोटा करता है. इसके लिए, आधुनिक और लेगसी आउटपुट के लिए अलग-अलग ऑप्टिमाइज़ की गई सेटिंग का इस्तेमाल किया जाता है. आखिर में, जनरेट किए गए लेगसी बंडल के लिए ज़रूरी पॉलीफ़िल को एक खास स्क्रिप्ट में निकाला जाता है, ताकि उन्हें कभी डुप्लीकेट न किया जाए या नए ब्राउज़र में ज़रूरत से ज़्यादा लोड न किया जाए.
BabelEsmPlugin
BabelEsmPlugin एक वेबपैक प्लग इन है. यह @babel/preset-env के साथ काम करता है, ताकि मौजूदा बंडल के आधुनिक वर्शन जनरेट किए जा सकें. इससे, आधुनिक ब्राउज़र में कम ट्रांसपाइल किए गए कोड को शिप किया जा सकता है. यह module/nomodule के लिए, पहले से मौजूद सबसे लोकप्रिय समाधान है. इसका इस्तेमाल Next.js और Preact CLI करते हैं.
// webpack.config.js
const BabelEsmPlugin = require('babel-esm-plugin');
module.exports = {
//...
module: {
rules: [
// your existing babel-loader configuration:
{
test: /\.js$/,
exclude: /node_modules/,
use: {
loader: 'babel-loader',
options: {
presets: ['@babel/preset-env'],
},
},
},
],
},
plugins: [new BabelEsmPlugin()],
};
BabelEsmPlugin
, वेबपैक कॉन्फ़िगरेशन की एक बड़ी रेंज के साथ काम करता है, क्योंकि यह आपके ऐप्लिकेशन के दो अलग-अलग बिल्ड चलाता है. बड़े ऐप्लिकेशन को दो बार कंपाइल करने में थोड़ा ज़्यादा समय लग सकता है. हालांकि, इस तकनीक की मदद से BabelEsmPlugin
को मौजूदा वेबपैक कॉन्फ़िगरेशन में आसानी से इंटिग्रेट किया जा सकता है. साथ ही, यह सबसे सुविधाजनक विकल्पों में से एक है.
node_modules को ट्रांसपाइल करने के लिए, babel-loader को कॉन्फ़िगर करना
अगर पिछले दो प्लग इन में से किसी एक के बिना babel-loader
का इस्तेमाल किया जा रहा है, तो आधुनिक JavaScript npm मॉड्यूल का इस्तेमाल करने के लिए, एक ज़रूरी चरण पूरा करना होगा. दो अलग-अलग babel-loader
कॉन्फ़िगरेशन तय करने से, node_modules
में मौजूद आधुनिक भाषा की सुविधाओं को अपने-आप इकट्ठा करके, ES2017 में बदला जा सकता है. साथ ही, अपने प्रोजेक्ट के कॉन्फ़िगरेशन में तय किए गए Babel प्लग इन और प्रीसेट की मदद से, पहले पक्ष के कोड को ट्रांसपाइल किया जा सकता है. इससे, मॉड्यूल/नोमॉड्यूल सेटअप के लिए, मॉडर्न और लेगसी बंडल जनरेट नहीं होते. हालांकि, इससे पुराने ब्राउज़र को काम करने से रोके बिना, मॉडर्न JavaScript वाले npm पैकेज इंस्टॉल और इस्तेमाल किए जा सकते हैं.
webpack-plugin-modern-npm, इस तकनीक का इस्तेमाल उन npm डिपेंडेंसी को कंपाइल करने के लिए करता है जिनके package.json
में "exports"
फ़ील्ड होता है. ऐसा इसलिए, क्योंकि इनमें आधुनिक सिंटैक्स हो सकता है:
// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');
module.exports = {
plugins: [
// auto-transpile modern stuff found in node_modules
new ModernNpmPlugin(),
],
};
इसके अलावा, इस तकनीक को अपने वेबपैक कॉन्फ़िगरेशन में मैन्युअल तरीके से भी लागू किया जा सकता है. इसके लिए, मॉड्यूल के package.json
में "exports"
फ़ील्ड देखें, क्योंकि उन्हें हल किया जाता है. कम शब्दों में बताने के लिए, कैश मेमोरी में सेव करने की सुविधा को छोड़कर, कस्टम लागू करने का तरीका कुछ ऐसा दिख सकता है:
// webpack.config.js
module.exports = {
module: {
rules: [
// Transpile for your own first-party code:
{
test: /\.js$/i,
loader: 'babel-loader',
exclude: /node_modules/,
},
// Transpile modern dependencies:
{
test: /\.js$/i,
include(file) {
let dir = file.match(/^.*[/\\]node_modules[/\\](@.*?[/\\])?.*?[/\\]/);
try {
return dir && !!require(dir[0] + 'package.json').exports;
} catch (e) {}
},
use: {
loader: 'babel-loader',
options: {
babelrc: false,
configFile: false,
presets: ['@babel/preset-env'],
},
},
},
],
},
};
इस तरीके का इस्तेमाल करते समय, आपको यह पक्का करना होगा कि आपके मिनिफ़ायर में मॉडर्न सिंटैक्स काम करता हो. Terser और uglify-es, दोनों में {ecma: 2017}
को तय करने का विकल्प होता है. इससे, संपीड़न और फ़ॉर्मैटिंग के दौरान ES2017 सिंटैक्स को बनाए रखने और कुछ मामलों में जनरेट करने में मदद मिलती है.
रोलअप
रोलअप में, एक ही बिल्ड के हिस्से के तौर पर बंडल के कई सेट जनरेट करने की सुविधा पहले से मौजूद है. साथ ही, यह डिफ़ॉल्ट रूप से आधुनिक कोड जनरेट करता है. इस वजह से, रोलअप को आधिकारिक प्लग इन के साथ आधुनिक और लेगसी बंडल जनरेट करने के लिए कॉन्फ़िगर किया जा सकता है. हो सकता है कि आपने इन प्लग इन का इस्तेमाल पहले से ही किया हो.
@rollup/plugin-babel
Rollup का इस्तेमाल करने पर, getBabelOutputPlugin()
तरीका (Rollup के आधिकारिक Babel प्लग इन से मिलता है) कोड को अलग-अलग सोर्स मॉड्यूल के बजाय, जनरेट किए गए बंडल में बदल देता है.
Rollup में, एक ही बिल्ड के हिस्से के तौर पर बंडल के कई सेट जनरेट करने की सुविधा पहले से मौजूद है. हर सेट में अपने प्लग इन होते हैं. इसका इस्तेमाल करके, आधुनिक और लेगसी के लिए अलग-अलग बंडल बनाए जा सकते हैं. इसके लिए, हर बंडल को Babel आउटपुट प्लग इन के अलग-अलग कॉन्फ़िगरेशन से पास करें:
// rollup.config.js
import {getBabelOutputPlugin} from '@rollup/plugin-babel';
export default {
input: 'src/index.js',
output: [
// modern bundles:
{
format: 'es',
plugins: [
getBabelOutputPlugin({
presets: [
[
'@babel/preset-env',
{
targets: {esmodules: true},
bugfixes: true,
loose: true,
},
],
],
}),
],
},
// legacy (ES5) bundles:
{
format: 'amd',
entryFileNames: '[name].legacy.js',
chunkFileNames: '[name]-[hash].legacy.js',
plugins: [
getBabelOutputPlugin({
presets: ['@babel/preset-env'],
}),
],
},
],
};
बिल्ड करने के अन्य टूल
Rollup और webpack को ज़्यादा से ज़्यादा कॉन्फ़िगर किया जा सकता है. इसका मतलब है कि हर प्रोजेक्ट को अपने कॉन्फ़िगरेशन को अपडेट करना होगा, ताकि डिपेंडेंसी में आधुनिक JavaScript सिंटैक्स चालू किया जा सके. इसके अलावा, Parcel, Snowpack, Vite, और WMR जैसे बेहतर बिल्ड टूल भी हैं. ये कॉन्फ़िगरेशन के बजाय, कॉन्वेंशन और डिफ़ॉल्ट सेटअप को प्राथमिकता देते हैं. इनमें से ज़्यादातर टूल, यह मानते हैं कि npm डिपेंडेंसी में आधुनिक सिंटैक्स हो सकता है. साथ ही, प्रोडक्शन के लिए बिल्ड करते समय, उन्हें सही सिंटैक्स लेवल पर ट्रांसपाइल कर देंगे.
webpack और Rollup के लिए खास प्लग इन के अलावा, डिवोल्यूशन का इस्तेमाल करके, किसी भी प्रोजेक्ट में लेगसी फ़ॉलबैक वाले आधुनिक JavaScript बंडल जोड़े जा सकते हैं. Devolution एक स्टैंडअलोन टूल है. यह किसी बिल्ड सिस्टम के आउटपुट को बदलकर, लेगसी JavaScript वैरिएंट बनाता है. इससे, बंडलिंग और ट्रांसफ़ॉर्मेशन की मदद से, आधुनिक आउटपुट टारगेट का अनुमान लगाया जा सकता है.