برای برنامه‌های کاربردی سریع‌تر، جاوا اسکریپت مدرن را منتشر، ارسال و نصب کنید

با روشن کردن وابستگی‌ها و خروجی‌های جاوا اسکریپت مدرن، عملکرد را بهبود بخشید.

بیش از 90 درصد مرورگرها قادر به اجرای جاوا اسکریپت مدرن هستند، اما رواج جاوا اسکریپت قدیمی امروزه منبع بزرگی از مشکلات عملکرد در وب باقی مانده است.

جاوا اسکریپت مدرن

جاوا اسکریپت مدرن به عنوان کد نوشته شده در یک نسخه خاص ECMAScript مشخص نمی شود، بلکه به صورت دستوری است که توسط همه مرورگرهای مدرن پشتیبانی می شود. مرورگرهای وب مدرن مانند کروم، اج، فایرفاکس و سافاری بیش از 90 درصد بازار مرورگرها را تشکیل می‌دهند و مرورگرهای مختلفی که به موتورهای رندر زیرین یکسانی متکی هستند، 5 درصد دیگر را تشکیل می‌دهند. این بدان معناست که 95 درصد از ترافیک وب جهانی از مرورگرهایی است که از پرکاربردترین ویژگی های زبان جاوا اسکریپت در 10 سال گذشته پشتیبانی می کنند، از جمله:

  • کلاس ها (ES2015)
  • توابع پیکان (ES2015)
  • ژنراتورها (ES2015)
  • محدوده محدوده (ES2015)
  • تخریب (ES2015)
  • پارامترهای استراحت و پخش (ES2015)
  • مختصر شی (ES2015)
  • Async/wait (ES2017)

ویژگی‌های نسخه‌های جدیدتر مشخصات زبان معمولاً در مرورگرهای مدرن پشتیبانی کمتری دارند. به عنوان مثال، بسیاری از ویژگی‌های ES2020 و ES2021 تنها در 70 درصد از بازار مرورگرها پشتیبانی می‌شوند - هنوز هم اکثر مرورگرها، اما به اندازه‌ای نیست که بتوان مستقیماً به آن ویژگی‌ها اعتماد کرد. این بدان معنی است که اگرچه جاوا اسکریپت "مدرن" یک هدف متحرک است، ES2017 دارای وسیع ترین طیف سازگاری با مرورگر است در حالی که بیشتر ویژگی های نحوی مدرن رایج را در بر می گیرد . به عبارت دیگر، ES2017 نزدیکترین نحو به نحو مدرن امروزی است .

جاوا اسکریپت قدیمی

جاوا اسکریپت قدیمی کدی است که به طور خاص از استفاده از تمام ویژگی های زبان فوق اجتناب می کند. اکثر توسعه دهندگان کد منبع خود را با استفاده از نحو مدرن می نویسند، اما برای افزایش پشتیبانی مرورگر، همه چیز را به نحو قدیمی کامپایل می کنند. کامپایل کردن به نحو قدیمی پشتیبانی مرورگر را افزایش می دهد، با این حال تأثیر آن اغلب کمتر از آن چیزی است که ما تصور می کنیم. در بسیاری از موارد، پشتیبانی از حدود 95٪ به 98٪ افزایش می یابد در حالی که هزینه قابل توجهی را متحمل می شود:

  • جاوا اسکریپت قدیمی معمولاً حدود 20٪ بزرگتر و کندتر از کدهای مدرن معادل است. کمبودهای ابزار و پیکربندی نادرست اغلب این شکاف را بیشتر می کند.

  • کتابخانه های نصب شده 90 درصد از کدهای جاوا اسکریپت تولیدی معمولی را تشکیل می دهند. کد کتابخانه به دلیل پلی پر کردن و تکرار کمکی که با انتشار کدهای مدرن می توان از آن جلوگیری کرد، سربار جاوا اسکریپت قدیمی بالاتری را متحمل می شود.

جاوا اسکریپت مدرن در npm

اخیرا، Node.js یک فیلد "exports" را برای تعریف نقاط ورودی برای یک بسته استاندارد کرده است:

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

ماژول هایی که توسط فیلد "exports" به آنها ارجاع داده می شود، دارای نسخه Node حداقل 12.8 است که از ES2019 پشتیبانی می کند. این بدان معناست که هر ماژولی که با استفاده از فیلد "exports" به آن ارجاع داده می شود، می تواند در جاوا اسکریپت مدرن نوشته شود. مصرف کنندگان بسته باید فرض کنند که ماژول های دارای فیلد "exports" حاوی کد مدرن هستند و در صورت لزوم ترانسپایل می شوند.

فقط مدرن

اگر می‌خواهید بسته‌ای را با کد مدرن منتشر کنید و آن را به مصرف‌کننده بسپارید تا زمانی که از آن به‌عنوان یک وابستگی استفاده می‌کند، از آن استفاده کند، فقط از قسمت "exports" استفاده کنید.

{
  "name": "foo",
  "exports": "./modern.js"
}

مدرن با جایگزینی قدیمی

از فیلد "exports" به همراه "main" استفاده کنید تا بسته خود را با استفاده از کدهای مدرن منتشر کنید، اما برای مرورگرهای قدیمی یک ES5 + CommonJS را نیز اضافه کنید.

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs"
}

مدرن با بهینه سازی های قدیمی و ESM باندلر

علاوه بر تعریف نقطه ورودی CommonJS بازگشتی، از فیلد "module" می توان برای اشاره به یک بسته بازگشتی قدیمی مشابه استفاده کرد، اما بسته ای که از نحو ماژول جاوا اسکریپت ( import و export ) استفاده می کند.

{
  "name": "foo",
  "exports": "./modern.js",
  "main": "./legacy.cjs",
  "module": "./module.js"
}

بسیاری از بسته‌ها، مانند webpack و Rollup، برای بهره‌گیری از ویژگی‌های ماژول و فعال کردن تکان دادن درخت، به این زمینه تکیه می‌کنند. این هنوز یک بسته قدیمی است که شامل هیچ کد مدرنی به غیر از نحو import / export نیست، بنابراین از این رویکرد برای ارسال کد مدرن با یک نسخه بازگشتی قدیمی استفاده کنید که هنوز برای بسته‌بندی بهینه شده است.

جاوا اسکریپت مدرن در برنامه ها

وابستگی های شخص ثالث اکثریت قریب به اتفاق کدهای جاوا اسکریپت تولید معمولی در برنامه های وب را تشکیل می دهند. در حالی که وابستگی‌های npm از لحاظ تاریخی به‌عنوان نحو ES5 قدیمی منتشر شده‌اند، این دیگر یک فرض امن نیست و خطر می‌کند که به‌روزرسانی‌های وابستگی پشتیبانی مرورگر را در برنامه شما قطع کند.

با افزایش تعداد بسته‌های npm که به جاوا اسکریپت مدرن منتقل می‌شوند، مهم است که اطمینان حاصل شود که ابزار ساخت برای مدیریت آنها تنظیم شده است. به احتمال زیاد برخی از بسته‌های npm که به آن‌ها وابسته هستید، از ویژگی‌های زبان مدرن استفاده می‌کنند. تعدادی گزینه برای استفاده از کدهای مدرن از npm بدون شکستن برنامه شما در مرورگرهای قدیمی‌تر وجود دارد، اما ایده کلی این است که سیستم ساخت، وابستگی‌ها را به همان هدف نحوی که کد منبع شما دارد، تغییر دهد.

بسته وب

از وب‌پک 5، اکنون می‌توانید پیکربندی کنید که بسته وب از چه نحوی هنگام تولید کد برای بسته‌ها و ماژول‌ها استفاده کند. این کد یا وابستگی‌های شما را تغییر نمی‌دهد، بلکه فقط بر روی کد «چسب» تولید شده توسط وب‌پک تأثیر می‌گذارد. برای مشخص کردن هدف پشتیبانی مرورگر، یک پیکربندی فهرست مرورگرها را به پروژه خود اضافه کنید یا این کار را مستقیماً در پیکربندی بسته وب خود انجام دهید:

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

همچنین می‌توان بسته‌های وب را برای تولید بسته‌های بهینه‌سازی‌شده‌ای پیکربندی کرد که هنگام هدف قرار دادن یک محیط مدرن ES Modules، عملکردهای wrapper غیر ضروری را حذف می‌کنند. این همچنین بسته وب را برای بارگیری بسته‌های تقسیم کد با استفاده از <script type="module"> پیکربندی می‌کند.

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

تعدادی پلاگین بسته وب در دسترس هستند که امکان کامپایل و ارسال جاوا اسکریپت مدرن را فراهم می کنند و در عین حال از مرورگرهای قدیمی مانند Optimize Plugin و BabelEsmPlugin پشتیبانی می کنند.

بهینه سازی پلاگین

افزونه Optimize یک پلاگین بسته وب است که کدهای همراه نهایی را به جای هر فایل منبع جداگانه از جاوا اسکریپت مدرن به قدیمی تبدیل می کند. این یک راه‌اندازی مستقل است که به پیکربندی بسته وب شما اجازه می‌دهد همه چیز را جاوا اسکریپت مدرن بدون انشعاب خاص برای خروجی‌ها یا سینتکس‌ها فرض کند.

از آنجایی که Optimize Plugin به جای ماژول های جداگانه روی بسته ها کار می کند، کد برنامه شما و وابستگی های شما را به طور یکسان پردازش می کند. این کار استفاده از وابستگی‌های جاوا اسکریپت مدرن را از npm ایمن می‌کند، زیرا کد آن‌ها به صورت دسته‌بندی شده و به نحو صحیح منتقل می‌شود. همچنین می‌تواند سریع‌تر از راه‌حل‌های سنتی شامل دو مرحله تلفیقی باشد، در حالی که همچنان بسته‌های جداگانه برای مرورگرهای مدرن و قدیمی ایجاد می‌کند. دو مجموعه از بسته‌ها برای بارگذاری با استفاده از الگوی ماژول/نومول طراحی شده‌اند.

// 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 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 اجازه می دهد تا به طور یکپارچه در تنظیمات بسته وب موجود ادغام شود و آن را به یکی از راحت ترین گزینه های موجود تبدیل می کند.

babel-loader را برای transpile node_modules پیکربندی کنید

اگر babel-loader بدون یکی از دو افزونه قبلی استفاده می‌کنید، برای استفاده از ماژول‌های جاوا اسکریپت مدرن npm یک مرحله مهم لازم است. تعریف دو پیکربندی جداگانه babel-loader امکان کامپایل خودکار ویژگی‌های زبان مدرن موجود در node_modules را در ES2017 فراهم می‌کند، در حالی که همچنان کد شخص اول خود را با افزونه‌های Babel و تنظیمات از پیش تعیین شده در پیکربندی پروژه‌تان ترجمه می‌کنید. این بسته‌های مدرن و قدیمی را برای راه‌اندازی ماژول/نومول ایجاد نمی‌کند، اما نصب و استفاده از بسته‌های npm که حاوی جاوا اسکریپت مدرن هستند را بدون شکستن مرورگرهای قدیمی‌تر ممکن می‌سازد.

webpack-plugin-modern-npm از این تکنیک برای کامپایل وابستگی‌های npm استفاده می‌کند که دارای یک فیلد "exports" در package.json خود هستند، زیرا ممکن است حاوی نحو مدرن باشند:

// webpack.config.js
const ModernNpmPlugin = require('webpack-plugin-modern-npm');

module.exports = {
  plugins: [
    // auto-transpile modern stuff found in node_modules
    new ModernNpmPlugin(),
  ],
};

از طرف دیگر، می‌توانید با بررسی فیلد "exports" در package.json ماژول‌ها، این تکنیک را به‌صورت دستی در پیکربندی بسته وب خود پیاده‌سازی کنید. با حذف کش برای اختصار، یک پیاده سازی سفارشی ممکن است به شکل زیر باشد:

// 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 دارای پشتیبانی داخلی برای تولید مجموعه های متعدد از بسته ها به عنوان بخشی از یک بیلد واحد است و به طور پیش فرض کد مدرن تولید می کند. در نتیجه، Rollup را می‌توان برای ایجاد بسته‌های مدرن و قدیمی با افزونه‌های رسمی که احتمالاً از قبل استفاده می‌کنید، پیکربندی کرد.

@rollup/plugin-babel

اگر از Rollup استفاده می کنید، متد getBabelOutputPlugin() ( ارائه شده توسط پلاگین رسمی Babel Rollup ) کد را در بسته های تولید شده به جای ماژول های منبع منفرد تبدیل می کند. 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 بسیار قابل تنظیم هستند، که به طور کلی به این معنی است که هر پروژه باید پیکربندی خود را به روز کند تا سینتکس جاوا اسکریپت مدرن را در وابستگی ها فعال کند. ابزارهای ساخت سطح بالاتری نیز وجود دارند که از قراردادها و پیش‌فرض‌ها به جای پیکربندی استفاده می‌کنند، مانند Parcel ، Snowpack ، Vite و WMR . اکثر این ابزارها فرض می‌کنند که وابستگی‌های npm ممکن است حاوی نحو مدرن باشند و هنگام ساخت برای تولید، آن‌ها را به سطح (های) نحوی مناسب منتقل می‌کنند.

علاوه بر افزونه‌های اختصاصی برای بسته وب و جمع‌آوری، بسته‌های جاوا اسکریپت مدرن با بازگردانی قدیمی را می‌توان با استفاده از تفویض اختیار به هر پروژه اضافه کرد. Devolution یک ابزار مستقل است که خروجی را از یک سیستم ساخت به تولید انواع جاوا اسکریپت قدیمی تبدیل می‌کند و به بسته‌بندی و تبدیل‌ها اجازه می‌دهد تا یک هدف خروجی مدرن را در نظر بگیرند.