तेज़ी से काम करने वाले ऐप्लिकेशन के लिए आधुनिक JavaScript प्रकाशित करें, शिप करें, और इंस्टॉल करें

मॉडर्न JavaScript डिपेंडेंसी और आउटपुट चालू करके परफ़ॉर्मेंस को बेहतर बनाएं.

जेसन मिलर
जेसन मिलर

90% से ज़्यादा ब्राउज़र मॉडर्न JavaScript चला सकते हैं. हालांकि, लेगसी JavaScript का इस्तेमाल आज भी वेब पर परफ़ॉर्मेंस की समस्याओं का एक बड़ा सोर्स है.

मॉडर्न JavaScript

आधुनिक JavaScript को किसी खास ECMAScript वर्शन में लिखे गए कोड के तौर पर नहीं दिखाया जाता है. इसके बजाय, इसे ऐसे सिंटैक्स में इस्तेमाल किया जाता है जो सभी मॉडर्न ब्राउज़र पर काम करता है. 90% से ज़्यादा ब्राउज़र में Chrome, Edge, Firefox, और Safari जैसे मॉडर्न वेब ब्राउज़र हैं. वहीं, अलग-अलग ब्राउज़र में भी 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% बड़ा और धीमा होता है. टूल की कमियां और कॉन्फ़िगरेशन को ठीक से लागू न करने की वजह से, यह अंतर अक्सर और भी बढ़ जाता है.

  • सामान्य प्रोडक्शन JavaScript कोड का 90% हिस्सा इंस्टॉल की गई लाइब्रेरी से आता है. पॉलीफ़िल और हेल्पर डुप्लिकेशन की वजह से, लाइब्रेरी कोड पर लेगसी JavaScript ओवरहेड ज़्यादा होता है, जिसे मॉडर्न कोड पब्लिश करके रोका जा सकता है.

एनपीएम पर आधुनिक JavaScript

हाल ही में, Node.js ने पैकेज के लिए एंट्री पॉइंट के बारे में बताने के लिए "exports" फ़ील्ड का स्टैंडर्ड तय किया है:

{
  "exports": "./index.js"
}

"exports" फ़ील्ड में बताए गए मॉड्यूल कम से कम 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"
}

वेबपैक और रोलअप जैसे कई बंडलर, मॉड्यूल की सुविधाओं का फ़ायदा पाने और पेड़ को हिलाने की सुविधा चालू करने के लिए इस फ़ील्ड का इस्तेमाल करते हैं. यह अब भी एक लेगसी बंडल है, जिसमें import/export सिंटैक्स के अलावा कोई मॉडर्न कोड नहीं है. इसलिए, इस तरीके का इस्तेमाल करके, ऐसे लेगसी फ़ॉलबैक के साथ मॉडर्न कोड को शिप करें जिसे अब भी बंडलिंग के लिए ऑप्टिमाइज़ किया गया हो.

ऐप्लिकेशन में आधुनिक JavaScript

वेब ऐप्लिकेशन में जनरेट होने वाले JavaScript कोड का ज़्यादातर हिस्सा तीसरे-पक्ष की डिपेंडेंसी से जनरेट होता है. हालांकि, एनपीएम डिपेंडेंसी को पहले से लेगसी ES5 सिंटैक्स के तौर पर पब्लिश किया गया है, लेकिन अब इसे सुरक्षित माना नहीं जाता है. साथ ही, रिस्क डिपेंडेंसी आपके ऐप्लिकेशन में ब्राउज़र सपोर्ट को तोड़ती है.

npm पैकेज की संख्या बढ़ने के साथ-साथ आधुनिक JavaScript का इस्तेमाल करते हुए, यह पक्का करना ज़रूरी है कि उन्हें मैनेज करने के लिए बिल्ड टूल सेट अप किए गए हों. इस बात की अच्छी संभावना है कि आप जिन एनपीएम पैकेज पर निर्भर हैं उनमें से कुछ में भाषा की आधुनिक सुविधाओं का इस्तेमाल पहले से ही हो. पुराने ब्राउज़र में अपने ऐप्लिकेशन को नुकसान पहुंचाए बिना npm से मॉडर्न कोड का इस्तेमाल करने के कई विकल्प मौजूद हैं. हालांकि, इसका सामान्य आइडिया यह है कि बिल्ड सिस्टम ट्रांसपाइल डिपेंडेंसी आपके सोर्स कोड की तरह ही सिंटैक्स टारगेट के लिए होनी चाहिए.

वेबपैक

वेबपैक 5 के बाद से, यह कॉन्फ़िगर किया जा सकता है कि बंडल और मॉड्यूल के लिए कोड जनरेट करते समय वेबपैक किस सिंटैक्स का इस्तेमाल करेगा. यह आपके कोड या डिपेंडेंसी को ट्रांसपाइल नहीं करता है. इसका असर सिर्फ़ वेबपैक से जनरेट किए गए "ग्लू" कोड पर पड़ता है. ब्राउज़र सहायता टारगेट तय करने के लिए, अपने प्रोजेक्ट में ब्राउज़र सूची कॉन्फ़िगरेशन जोड़ें या सीधे अपने वेबपैक कॉन्फ़िगरेशन में ऐसा करें:

module.exports = {
  target: ['web', 'es2017'],
};

वेबपैक को ऐसे ऑप्टिमाइज़ किए गए बंडल जनरेट करने के लिए भी कॉन्फ़िगर किया जा सकता है जो मॉडर्न ईएस मॉड्यूल एनवायरमेंट को टारगेट करते समय, गै़र-ज़रूरी रैपर फ़ंक्शन को हटा दें. यह वेबपैक, <script type="module"> का इस्तेमाल करके कोड-स्प्लिट बंडल लोड करने के लिए भी कॉन्फ़िगर करता है.

module.exports = {
  target: ['web', 'es2017'],
  output: {
    module: true,
  },
  experiments: {
    outputModule: true,
  },
};

ऐसे कई वेबपैक प्लगिन उपलब्ध हैं जो लेगसी ब्राउज़र, जैसे कि Optimize प्लगिन और बेबलEsmPlugin) के साथ काम करते हुए भी आधुनिक JavaScript को कंपाइल और शिप करने में आपकी मदद करते हैं.

प्लग इन ऑप्टिमाइज़ करें

Optimize प्लगिन एक ऐसा वेबपैक प्लगिन है जो हर सोर्स फ़ाइल के बजाय, फ़ाइनल बंडल किए गए कोड को मॉडर्न से लेगसी JavaScript में बदलता है. यह अपने-आप में पूरा किया गया सेटअप है, जिससे आपके वेबपैक कॉन्फ़िगरेशन में यह माना जाता है कि हर चीज़ को मॉडर्न JavaScript माना जाता है. इसमें कई आउटपुट या सिंटैक्स के लिए कोई खास ब्रांच नहीं होती है.

Optimize प्लगिन अलग-अलग मॉड्यूल के बजाय बंडल पर काम करता है. इसलिए, यह आपके ऐप्लिकेशन के कोड और आपकी डिपेंडेंसी को समान रूप से प्रोसेस करता है. इससे npm की मॉडर्न JavaScript डिपेंडेंसी का इस्तेमाल सुरक्षित तरीके से हो जाता है, क्योंकि उनका कोड बंडल किया जाएगा और सही सिंटैक्स में ट्रांसफ़र किया जाएगा. यह दो कंपाइलेशन चरणों वाले पारंपरिक समाधानों से भी तेज़ हो सकता है और इसके साथ-साथ मॉडर्न और लेगसी ब्राउज़र के लिए अलग-अलग बंडल जनरेट करता है. बंडल के दो सेट इस तरह डिज़ाइन किए गए हैं कि उन्हें मॉड्यूल/नोमॉड्यूल पैटर्न का इस्तेमाल करके लोड किया जा सके.

// webpack.config.js
const OptimizePlugin = require('optimize-plugin');

module.exports = {
  // ...
  plugins: [new OptimizePlugin()],
};

Optimize Plugin, कस्टम वेबपैक कॉन्फ़िगरेशन की तुलना में तेज़ और ज़्यादा कारगर हो सकता है. यह आम तौर पर, मॉडर्न और लेगसी कोड को अलग-अलग बंडल करता है. यह आपके लिए Babel चलाने का तरीका भी मैनेज करता है. साथ ही, Terser का इस्तेमाल करके, मॉडर्न और लेगसी आउटपुट के लिए, अलग-अलग सेटिंग का इस्तेमाल करके बंडल को छोटा करता है. आखिर में, जनरेट किए गए लेगसी बंडल के लिए ज़रूरी पॉलीफ़िल एक खास स्क्रिप्ट में एक्सट्रैक्ट किए जाते हैं, ताकि नए ब्राउज़र में उनकी डुप्लीकेट कॉपी न बनाई जाए या ज़रूरत पड़ने पर वे नए ब्राउज़र में लोड न हों.

तुलना: सोर्स मॉड्यूल को दो बार और जनरेट किए गए बंडल को ट्रांसपाइलिंग करना.

BabelEsmPlugin

BabelEsmPlugin एक वेबपैक प्लगिन है. यह @babel/preset-env के साथ मिलकर काम करता है, ताकि मौजूदा बंडल के मॉडर्न वर्शन जनरेट किए जा सकें. इससे, कम ट्रांसपाइल किए गए कोड को मॉडर्न ब्राउज़र पर भेजा जा सकता है. यह मॉड्यूल/नोमॉड्यूल के लिए सबसे लोकप्रिय ऑफ़र है. इसका इस्तेमाल Next.js और Preact सीएलआई करते हैं.

// 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, Webpack कॉन्फ़िगरेशन की एक बड़ी रेंज के साथ काम करता है. ऐसा इसलिए, क्योंकि यह आपके ऐप्लिकेशन के दो अलग-अलग बिल्ड चलाता है. बड़े ऐप्लिकेशन के लिए, दो बार कंपाइल करने में थोड़ा ज़्यादा समय लग सकता है. हालांकि, इस तकनीक से BabelEsmPlugin, मौजूदा वेबपैक कॉन्फ़िगरेशन में आसानी से इंटिग्रेट हो पाता है. यह सबसे आसान विकल्पों में से एक है.

babel-loader को ट्रांसपीाइल node_modules पर कॉन्फ़िगर करें

अगर पिछले दो प्लग इन में से किसी के बिना babel-loader का इस्तेमाल किया जा रहा है, तो नए JavaScript npm मॉड्यूल का इस्तेमाल करने के लिए यह ज़रूरी चरण है. babel-loader के दो अलग-अलग कॉन्फ़िगरेशन तय करने पर, node_modules में मिलने वाली आधुनिक भाषा की सुविधाओं को ES2017 में अपने-आप कंपाइल किया जा सकता है. साथ ही, आपके प्रोजेक्ट के कॉन्फ़िगरेशन में तय किए गए बेबल प्लगिन और प्रीसेट के साथ, अपने पहले पक्ष के कोड को ट्रांसपाइल किया जा सकता है. यह किसी मॉड्यूल/नोमॉड्यूल सेट अप के लिए मॉडर्न और लेगसी बंडल जनरेट नहीं करता. हालांकि, इससे एनपीएम पैकेज को इंस्टॉल और इस्तेमाल किया जा सकता है, लेकिन पुराने ब्राउज़र में बदलाव किए बिना, मॉडर्न JavaScript होता है.

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

अगर रोलअप का इस्तेमाल किया जाता है, तो getBabelOutputPlugin() का तरीका (रोलअप के आधिकारिक बेबल प्लगिन से मिला) अलग-अलग सोर्स मॉड्यूल के बजाय, जनरेट किए गए बंडल में कोड को बदलता है. रोलअप टूल में, एक बिल्ड के हिस्से के तौर पर बंडल के कई सेट जनरेट करने की सुविधा पहले से मौजूद होती है. हर सेट के अपने प्लग इन होते हैं. इसका इस्तेमाल करके मॉडर्न और लेगसी के लिए, अलग-अलग बंडल बनाए जा सकते हैं. हर बंडल को एक अलग 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'],
        }),
      ],
    },
  ],
};

बिल्ड करने के अतिरिक्त टूल

रोलअप और वेबपैक को बहुत ज़्यादा कॉन्फ़िगर किया जा सकता है. इसका मतलब है कि हर प्रोजेक्ट को अपना कॉन्फ़िगरेशन अपडेट करना होगा, ताकि डिपेंडेंसी के दौरान मॉडर्न JavaScript सिंटैक्स चालू हो. ऐसे उच्च-लेवल बिल्ड टूल भी हैं जो कॉन्फ़िगरेशन के बजाय कन्वेंशन और डिफ़ॉल्ट का समर्थन करते हैं, जैसे कि Parcel, Snowpack, Vite, और WMR. इनमें से ज़्यादातर टूल यह मान लेते हैं कि एनपीएम डिपेंडेंसी में मॉडर्न सिंटैक्स हो सकता है. साथ ही, ये उन्हें प्रोडक्शन के लिए बनाते समय सही सिंटैक्स लेवल पर ले जाएंगे.

वेबपैक और रोलअप के लिए खास तौर पर बनाए गए प्लग इन के अलावा, लेगसी फ़ॉलबैक वाले मॉडर्न JavaScript बंडल को डिवोल्यूशन का इस्तेमाल करके किसी भी प्रोजेक्ट में जोड़ा जा सकता है. डिवोल्यूशन एक स्टैंडअलोन टूल है. यह बिल्ड सिस्टम से आउटपुट को बदलकर, JavaScript के लेगसी वैरिएंट बनाता है. इससे बंडलिंग और ट्रांसफ़ॉर्मेशन को एक मॉडर्न आउट टारगेट माना जाता है.