کدهای مدرن را به مرورگرهای مدرن برای بارگذاری سریعتر صفحه ارائه دهید

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

اسکرین شات برنامه

در برنامه نمونه، می‌توانید یک کلمه یا شکلک انتخاب کنید تا میزان علاقه‌تان به هر گربه را نشان دهد. وقتی روی دکمه ای کلیک می کنید، برنامه مقدار دکمه را در زیر تصویر گربه فعلی نمایش می دهد.

اندازه گیری کنید

همیشه ایده خوبی است که قبل از افزودن هر گونه بهینه سازی، یک وب سایت را بررسی کنید:

  1. برای پیش نمایش سایت، View App را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .
  2. «Control+Shift+J» (یا «Command+Option+J» در Mac) را فشار دهید تا DevTools باز شود.
  3. روی تب Network کلیک کنید.
  4. چک باکس Disable cache را انتخاب کنید.
  5. برنامه را دوباره بارگیری کنید.

درخواست اندازه باندل اصلی

بیش از 80 کیلوبایت برای این برنامه استفاده شده است! زمان آن است که بفهمیم آیا از قطعات بسته استفاده نمی شود:

  1. Control+Shift+P (یا Command+Shift+P در مک) را فشار دهید تا منوی Command باز شود. منوی فرمان

  2. وارد Show Coverage و Enter را بزنید تا تب Coverage نمایش داده شود.

  3. در تب Coverage ، روی Reload کلیک کنید تا برنامه در حین گرفتن پوشش مجدد بارگیری شود.

    بارگیری مجدد برنامه با پوشش کد

  4. به مقدار کد استفاده شده در مقابل مقدار بارگذاری شده برای بسته اصلی نگاهی بیندازید:

    پوشش کد بسته نرم افزاری

بیش از نیمی از بسته نرم افزاری (44 کیلوبایت) حتی استفاده نمی شود. این به این دلیل است که بسیاری از کدهای داخل شامل polyfills هستند تا اطمینان حاصل شود که برنامه در مرورگرهای قدیمی کار می کند.

از @babel/preset-env استفاده کنید

نحو زبان جاوا اسکریپت با استانداردی به نام ECMAScript یا ECMA-262 مطابقت دارد. نسخه های جدیدتر مشخصات هر سال منتشر می شود و شامل ویژگی های جدیدی است که پروسه پیشنهاد را پشت سر گذاشته است. هر مرورگر اصلی همیشه در مرحله متفاوتی از پشتیبانی از این ویژگی ها است.

ویژگی های ES2015 زیر در برنامه استفاده می شود:

از ویژگی ES2017 زیر نیز استفاده می شود:

با خیال راحت وارد کد منبع در src/index.js شوید تا ببینید چگونه از همه اینها استفاده می شود.

همه این ویژگی‌ها در آخرین نسخه کروم پشتیبانی می‌شوند، اما مرورگرهای دیگری که از آن‌ها پشتیبانی نمی‌کنند چطور؟ Babel ، که در برنامه گنجانده شده است، محبوب ترین کتابخانه ای است که برای کامپایل کدهایی استفاده می شود که حاوی نحو جدیدتر به کدهایی است که مرورگرها و محیط های قدیمی می توانند آن را درک کنند. این کار را به دو صورت انجام می دهد:

  • Polyfills برای تقلید توابع جدیدتر ES2015+ گنجانده شده است تا APIهای آنها حتی اگر توسط مرورگر پشتیبانی نشود قابل استفاده باشند. در اینجا یک مثال از یک polyfill متد Array.includes آورده شده است.
  • از پلاگین ها برای تبدیل کد ES2015 (یا جدیدتر) به نحو قدیمی تر ES5 استفاده می شود. از آنجایی که اینها تغییرات مربوط به نحو (مانند توابع پیکان) هستند، نمی توان آنها را با polyfills شبیه سازی کرد.

به package.json نگاه کنید تا ببینید کدام کتابخانه های Babel گنجانده شده است:

"dependencies": {
  "@babel/polyfill": "^7.0.0"
},
"devDependencies": {
  //...
  "babel-loader": "^8.0.2",
  "@babel/core": "^7.1.0",
  "@babel/preset-env": "^7.1.0",
  //...
}
  • @babel/core کامپایلر اصلی Babel است. با این کار، تمام تنظیمات Babel در یک .babelrc در ریشه پروژه تعریف می شوند.
  • babel-loader شامل Babel در فرآیند ساخت وب پک می شود.

اکنون به webpack.config.js نگاه کنید تا ببینید که چگونه babel-loader به عنوان یک قانون گنجانده شده است:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill تمام پلی‌فیل‌های لازم را برای هر ویژگی جدیدتر ECMAScript فراهم می‌کند تا بتوانند در محیط‌هایی کار کنند که از آن‌ها پشتیبانی نمی‌کنند. قبلاً در بالای src/index.js.
import "./style.css";
import "@babel/polyfill";
  • @babel/preset-env مشخص می‌کند که کدام تبدیل‌ها و polyfill‌ها برای هر مرورگر یا محیطی که به‌عنوان هدف انتخاب شده‌اند، ضروری هستند.

به فایل تنظیمات Babel، .babelrc نگاهی بیندازید تا ببینید چگونه شامل می شود:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions"
      }
    ]
  ]
}

این یک راه اندازی بابل و بسته وب است. در صورت استفاده از بسته‌بندی ماژول متفاوت از وب‌پک ، یاد بگیرید که چگونه Babel را در برنامه خود قرار دهید .

ویژگی targets در .babelrc مشخص می کند که کدام مرورگرها هدف قرار می گیرند. @babel/preset-env با فهرست مرورگرها ادغام می‌شود، به این معنی که می‌توانید فهرست کاملی از جستارهای سازگار را که می‌توان در این زمینه در مستندات فهرست مرورگر استفاده کرد، بیابید.

مقدار "last 2 versions" کد موجود در برنامه را برای دو نسخه آخر هر مرورگر انتقال می دهد.

اشکال زدایی

برای مشاهده کامل تمام اهداف Babel مرورگر و همچنین همه تبدیل‌ها و polyfill‌های موجود، یک فیلد debug را به .babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
      }
    ]
  ]
}
  • روی Tools کلیک کنید.
  • روی Logs کلیک کنید.

برنامه را دوباره بارگیری کنید و به گزارش های وضعیت اشکال در پایین ویرایشگر نگاهی بیندازید.

مرورگرهای هدفمند

Babel تعدادی از جزئیات را در مورد فرآیند کامپایل در کنسول ثبت می کند، از جمله تمام محیط های هدف که کد برای آنها کامپایل شده است.

مرورگرهای هدفمند

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

Babel همچنین لیستی از افزونه های تبدیل استفاده شده را ثبت می کند:

لیست پلاگین های استفاده شده

این یک لیست نسبتا طولانی است! اینها همه افزونه‌هایی هستند که Babel برای تبدیل هر نحو ES2015+ به نحو قدیمی‌تر برای همه مرورگرهای مورد نظر باید استفاده کند.

با این حال، Babel هیچ پلی پر خاصی را که استفاده می شود نشان نمی دهد:

هیچ پلی فیل اضافه نشده است

این به این دلیل است که کل @babel/polyfill مستقیماً وارد می‌شود.

پلی فیل ها را به صورت جداگانه بارگیری کنید

به‌طور پیش‌فرض، Babel شامل تمام پلی‌فیل‌های مورد نیاز برای یک محیط کامل ES2015+ می‌شود که @babel/polyfill به یک فایل وارد می‌شود. برای وارد کردن polyfill های خاص مورد نیاز برای مرورگرهای هدف، یک useBuiltIns: 'entry' را به پیکربندی اضافه کنید.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
        "useBuiltIns": "entry"
      }
    ]
  ]
}

برنامه را دوباره بارگیری کنید. اکنون می توانید تمام پلی فیل های خاص را مشاهده کنید:

لیست پلی فیل های وارد شده

اگرچه اکنون فقط پلی‌فیل‌های مورد نیاز برای "last 2 versions" گنجانده شده است، اما هنوز یک لیست فوق العاده طولانی است! این به این دلیل است که polyfill های مورد نیاز برای مرورگرهای هدف برای هر ویژگی جدیدتر هنوز هم گنجانده شده است. مقدار مشخصه را به usage تغییر دهید تا فقط موارد مورد نیاز برای ویژگی هایی را که در کد استفاده می شود شامل شود.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true,
        "useBuiltIns": "entry"
        "useBuiltIns": "usage"
      }
    ]
  ]
}

با این کار، پلی‌فیل‌ها به‌طور خودکار در صورت نیاز گنجانده می‌شوند. این به این معنی است که می توانید @babel/polyfill import را در src/index.js.

import "./style.css";
import "@babel/polyfill";

اکنون فقط پلی پرهای مورد نیاز برای برنامه گنجانده شده است.

لیست پلی فیل ها به طور خودکار گنجانده شده است

اندازه بسته نرم افزاری به طور قابل توجهی کاهش می یابد.

حجم بسته به 30.1 کیلوبایت کاهش یافت

محدود کردن لیست مرورگرهای پشتیبانی شده

تعداد اهداف مرورگر گنجانده شده هنوز بسیار زیاد است و کاربران زیادی از مرورگرهای متوقف شده مانند اینترنت اکسپلورر استفاده نمی کنند. تنظیمات را به موارد زیر به روز کنید:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "targets": [">0.25%", "not ie 11"],
        "debug": true,
        "useBuiltIns": "usage",
      }
    ]
  ]
}

به جزئیات بسته واکشی شده نگاهی بیندازید.

حجم بسته 30.0 کیلوبایت

از آنجایی که برنامه بسیار کوچک است، واقعاً تفاوت زیادی با این تغییرات وجود ندارد. با این حال، استفاده از درصد سهم بازار مرورگر (مانند ">0.25%" ) همراه با حذف مرورگرهای خاصی که مطمئن هستید کاربران شما از آنها استفاده نمی کنند، رویکرد توصیه شده است. برای کسب اطلاعات بیشتر در مورد این، به مقاله "2 نسخه آخر" که توسط جیمز کایل مضر در نظر گرفته شده است، نگاهی بیندازید.

از <script type="module"> استفاده کنید

هنوز جای پیشرفت بیشتری وجود دارد. اگرچه تعدادی از پلی پرهای استفاده نشده حذف شده اند، اما تعداد زیادی از آنها در حال ارسال هستند که برای برخی از مرورگرها مورد نیاز نیستند. با استفاده از ماژول‌ها، می‌توان سینتکس جدیدتر را مستقیماً بدون استفاده از پلی‌فیلدهای غیرضروری نوشت و به مرورگرها ارسال کرد.

ماژول های جاوا اسکریپت یک ویژگی نسبتا جدید هستند که در همه مرورگرهای اصلی پشتیبانی می شوند. ماژول ها را می توان با استفاده از ویژگی type="module" برای تعریف اسکریپت هایی که از ماژول های دیگر وارد و صادر می کنند ایجاد کرد. به عنوان مثال:

// math.mjs
export const add = (x, y) => x + y;

<!-- index.html -->
<script type="module">
  import { add } from './math.mjs';

  add(5, 2); // 7
</script>

بسیاری از ویژگی‌های جدیدتر ECMAScript قبلاً در محیط‌هایی پشتیبانی می‌شوند که از ماژول‌های جاوا اسکریپت پشتیبانی می‌کنند (به‌جای نیاز به Babel).

  • نسخه‌ای که در مرورگرهای جدیدتر که از ماژول‌ها پشتیبانی می‌کنند کار می‌کند و شامل ماژولی است که تا حد زیادی ترجمه نشده است اما اندازه فایل کوچک‌تری دارد.
  • نسخه ای که شامل یک اسکریپت بزرگتر و ترجمه شده است که در هر مرورگر قدیمی کار می کند

استفاده از ماژول های ES با Babel

برای داشتن تنظیمات جداگانه @babel/preset-env برای دو نسخه برنامه، فایل .babelrc . را حذف کنید. تنظیمات Babel را می توان با تعیین دو فرمت کامپایل متفاوت برای هر نسخه از برنامه به پیکربندی بسته وب اضافه کرد.

با افزودن یک پیکربندی برای اسکریپت قدیمی به webpack.config.js شروع کنید:

const legacyConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: false
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

توجه داشته باشید که به جای استفاده از مقدار targets برای "@babel/preset-env" ، esmodules با مقدار false استفاده می شود. این بدان معناست که Babel شامل تمام تبدیل‌ها و polyfill‌های لازم برای هدف قرار دادن هر مرورگری است که هنوز از ماژول‌های ES پشتیبانی نمی‌کند.

اشیاء entry ، cssRule و corePlugins را به ابتدای فایل webpack.config.js اضافه کنید. همه اینها بین ماژول و اسکریپت های قدیمی ارائه شده به مرورگر به اشتراک گذاشته شده است.

const entry = {
  main: "./src"
};

const cssRule = {
  test: /\.css$/,
  use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
  })
};

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"})
];

اکنون به طور مشابه، یک شیء پیکربندی برای اسکریپت ماژول زیر که در آن legacyConfig تعریف شده است ایجاد کنید:

const moduleConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].mjs"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: true
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

تفاوت اصلی در اینجا این است که پسوند فایل .mjs برای نام فایل خروجی استفاده می شود. مقدار esmodules در اینجا روی true تنظیم شده است، به این معنی که کدی که به این ماژول خروجی می‌شود یک اسکریپت کوچکتر و کمتر کامپایل شده است که در این مثال هیچ تغییری ایجاد نمی‌کند، زیرا همه ویژگی‌های استفاده شده قبلاً در مرورگرهایی که از ماژول‌ها پشتیبانی می‌کنند پشتیبانی می‌شوند.

در انتهای فایل، هر دو پیکربندی را در یک آرایه واحد صادر کنید.

module.exports = [
  legacyConfig, moduleConfig
];

اکنون هم یک ماژول کوچکتر برای مرورگرهایی که از آن پشتیبانی می کنند و هم یک اسکریپت ترجمه شده بزرگتر برای مرورگرهای قدیمی می سازد.

مرورگرهایی که از ماژول‌ها پشتیبانی می‌کنند، اسکریپت‌های دارای ویژگی nomodule را نادیده می‌گیرند. برعکس، مرورگرهایی که از ماژول ها پشتیبانی نمی کنند، عناصر اسکریپت با type="module" را نادیده می گیرند. این بدان معنی است که شما می توانید یک ماژول و همچنین یک بازگشت کامپایل شده را اضافه کنید. در حالت ایده‌آل، دو نسخه برنامه باید در index.html مانند این باشند:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>

مرورگرهایی که از ماژول ها پشتیبانی می کنند main.mjs واکشی و اجرا می کنند و main.bundle.js. مرورگرهایی که از ماژول ها پشتیبانی نمی کنند برعکس عمل می کنند.

توجه به این نکته مهم است که برخلاف اسکریپت های معمولی، اسکریپت های ماژول همیشه به طور پیش فرض به تعویق افتاده اند. اگر می خواهید اسکریپت nomodule معادل نیز به تعویق بیفتد و فقط پس از تجزیه اجرا شود، باید ویژگی defer را اضافه کنید:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>

آخرین کاری که باید در اینجا انجام شود این است که ویژگی های module و nomodule را به ترتیب به ماژول و اسکریپت قدیمی اضافه کنید، ScriptExtHtmlWebpackPlugin را در بالای webpack.config.js وارد کنید:

const path = require("path");

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

اکنون آرایه plugins را در پیکربندی ها به روز کنید تا این افزونه را در خود داشته باشد:

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"}),
  new ScriptExtHtmlWebpackPlugin({
    module: /\.mjs$/,
    custom: [
      {
        test: /\.js$/,
        attribute: 'nomodule',
        value: ''
    },
    ]
  })
];

این تنظیمات افزونه یک ویژگی type="module" برای همه عناصر اسکریپت .mjs و همچنین یک ویژگی nomodule برای همه ماژول های اسکریپت .js اضافه می کند.

ارائه ماژول ها در سند HTML

آخرین کاری که باید انجام شود خروجی هر دو عنصر قدیمی و مدرن اسکریپت در فایل HTML است. متأسفانه، افزونه ای که فایل HTML نهایی را ایجاد می کند، HTMLWebpackPlugin ، در حال حاضر خروجی اسکریپت های ماژول و نومول را پشتیبانی نمی کند . اگرچه راه‌حل‌ها و پلاگین‌های جداگانه‌ای برای حل این مشکل ایجاد شده‌اند، مانند BabelMultiTargetPlugin و HTMLWebpackMultiBuildPlugin ، یک رویکرد ساده‌تر برای افزودن عنصر اسکریپت ماژول به صورت دستی برای هدف این آموزش استفاده می‌شود.

موارد زیر را در انتهای فایل به src/index.js اضافه کنید:

    ...
    </form>
    <script type="module" src="main.mjs"></script>
  </body>
</html>

اکنون برنامه را در مرورگری بارگذاری کنید که از ماژول ها پشتیبانی می کند، مانند آخرین نسخه کروم.

ماژول 5.2 کیلوبایتی از طریق شبکه برای مرورگرهای جدیدتر واکشی شد

فقط ماژول واکشی شده است، با اندازه بسته نرم افزاری بسیار کوچکتر به دلیل اینکه تا حد زیادی ترانسفورم نشده است! عنصر دیگر اسکریپت به طور کامل توسط مرورگر نادیده گرفته می شود.

اگر برنامه را بر روی یک مرورگر قدیمی بارگیری کنید، فقط اسکریپت بزرگتر و ترجمه شده با تمام پلی پرها و تبدیل های مورد نیاز واکشی می شود. در اینجا یک اسکرین شات برای همه درخواست‌های انجام شده در نسخه قدیمی کروم (نسخه 38) آمده است.

اسکریپت 30 کیلوبایتی برای مرورگرهای قدیمی‌تر واکشی شد

نتیجه گیری

اکنون می‌دانید که چگونه از @babel/preset-env استفاده کنید تا فقط پلی‌فیل‌های لازم برای مرورگرهای هدف را فراهم کنید. همچنین می‌دانید که چگونه ماژول‌های جاوا اسکریپت می‌توانند با ارسال دو نسخه مختلف از یک برنامه کاربردی، عملکرد را بیشتر بهبود بخشند. با درک درستی از اینکه چگونه هر دو این تکنیک ها می توانند اندازه بسته شما را به میزان قابل توجهی کاهش دهند، پیش بروید و بهینه سازی کنید!

،

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

اسکرین شات برنامه

در برنامه نمونه، می‌توانید یک کلمه یا شکلک انتخاب کنید تا میزان علاقه‌تان به هر گربه را نشان دهد. وقتی روی دکمه ای کلیک می کنید، برنامه مقدار دکمه را در زیر تصویر گربه فعلی نمایش می دهد.

اندازه گیری کنید

همیشه ایده خوبی است که قبل از افزودن هر گونه بهینه سازی، یک وب سایت را بررسی کنید:

  1. برای پیش نمایش سایت، View App را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .
  2. «Control+Shift+J» (یا «Command+Option+J» در Mac) را فشار دهید تا DevTools باز شود.
  3. روی تب Network کلیک کنید.
  4. چک باکس Disable cache را انتخاب کنید.
  5. برنامه را دوباره بارگیری کنید.

درخواست اندازه باندل اصلی

بیش از 80 کیلوبایت برای این برنامه استفاده شده است! زمان آن است که بفهمیم آیا از قطعات بسته استفاده نمی شود:

  1. Control+Shift+P (یا Command+Shift+P در مک) را فشار دهید تا منوی Command باز شود. منوی فرمان

  2. وارد Show Coverage و Enter را بزنید تا تب Coverage نمایش داده شود.

  3. در تب Coverage ، روی Reload کلیک کنید تا برنامه در حین گرفتن پوشش مجدد بارگیری شود.

    بارگیری مجدد برنامه با پوشش کد

  4. به مقدار کد استفاده شده در مقابل مقدار بارگذاری شده برای بسته اصلی نگاهی بیندازید:

    پوشش کد بسته نرم افزاری

بیش از نیمی از بسته نرم افزاری (44 کیلوبایت) حتی استفاده نمی شود. این به این دلیل است که بسیاری از کدهای داخل شامل polyfills هستند تا اطمینان حاصل شود که برنامه در مرورگرهای قدیمی کار می کند.

از @babel/preset-env استفاده کنید

نحو زبان جاوا اسکریپت با استانداردی به نام ECMAScript یا ECMA-262 مطابقت دارد. نسخه های جدیدتر مشخصات هر سال منتشر می شود و شامل ویژگی های جدیدی است که پروسه پیشنهاد را پشت سر گذاشته است. هر مرورگر اصلی همیشه در مرحله متفاوتی از پشتیبانی از این ویژگی ها است.

ویژگی های ES2015 زیر در برنامه استفاده می شود:

از ویژگی ES2017 زیر نیز استفاده می شود:

با خیال راحت وارد کد منبع در src/index.js شوید تا ببینید چگونه از همه اینها استفاده می شود.

همه این ویژگی‌ها در آخرین نسخه کروم پشتیبانی می‌شوند، اما مرورگرهای دیگری که از آن‌ها پشتیبانی نمی‌کنند چطور؟ Babel ، که در برنامه گنجانده شده است، محبوب ترین کتابخانه ای است که برای کامپایل کدهایی استفاده می شود که حاوی نحو جدیدتر به کدهایی است که مرورگرها و محیط های قدیمی می توانند آن را درک کنند. این کار را به دو صورت انجام می دهد:

  • Polyfills برای تقلید توابع جدیدتر ES2015+ گنجانده شده است تا APIهای آنها حتی اگر توسط مرورگر پشتیبانی نشود قابل استفاده باشند. در اینجا یک مثال از یک polyfill متد Array.includes آورده شده است.
  • از پلاگین ها برای تبدیل کد ES2015 (یا جدیدتر) به نحو قدیمی تر ES5 استفاده می شود. از آنجایی که اینها تغییرات مربوط به نحو (مانند توابع پیکان) هستند، نمی توان آنها را با polyfills شبیه سازی کرد.

به package.json نگاه کنید تا ببینید کدام کتابخانه های Babel گنجانده شده است:

"dependencies": {
  "@babel/polyfill": "^7.0.0"
},
"devDependencies": {
  //...
  "babel-loader": "^8.0.2",
  "@babel/core": "^7.1.0",
  "@babel/preset-env": "^7.1.0",
  //...
}
  • @babel/core کامپایلر اصلی Babel است. با این کار، تمام تنظیمات Babel در یک .babelrc در ریشه پروژه تعریف می شوند.
  • babel-loader شامل Babel در فرآیند ساخت وب پک می شود.

اکنون به webpack.config.js نگاه کنید تا ببینید که چگونه babel-loader به عنوان یک قانون گنجانده شده است:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill تمام پلی‌فیل‌های لازم را برای هر ویژگی جدیدتر ECMAScript فراهم می‌کند تا بتوانند در محیط‌هایی کار کنند که از آن‌ها پشتیبانی نمی‌کنند. قبلاً در بالای src/index.js.
import "./style.css";
import "@babel/polyfill";
  • @babel/preset-env مشخص می‌کند که کدام تبدیل‌ها و polyfill‌ها برای هر مرورگر یا محیطی که به‌عنوان هدف انتخاب شده‌اند، ضروری هستند.

به فایل تنظیمات Babel، .babelrc نگاهی بیندازید تا ببینید چگونه شامل می شود:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions"
      }
    ]
  ]
}

این یک راه اندازی بابل و بسته وب است. در صورت استفاده از بسته‌بندی ماژول متفاوت از وب‌پک ، یاد بگیرید که چگونه Babel را در برنامه خود قرار دهید .

ویژگی targets در .babelrc مشخص می کند که کدام مرورگرها هدف قرار می گیرند. @babel/preset-env با فهرست مرورگرها ادغام می‌شود، به این معنی که می‌توانید فهرست کاملی از جستارهای سازگار را که می‌توان در این زمینه در مستندات فهرست مرورگر استفاده کرد، بیابید.

مقدار "last 2 versions" کد موجود در برنامه را برای دو نسخه آخر هر مرورگر انتقال می دهد.

اشکال زدایی

برای مشاهده کامل تمام اهداف Babel مرورگر و همچنین همه تبدیل‌ها و polyfill‌های موجود، یک فیلد debug را به .babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
      }
    ]
  ]
}
  • روی Tools کلیک کنید.
  • روی Logs کلیک کنید.

برنامه را دوباره بارگیری کنید و به گزارش های وضعیت اشکال در پایین ویرایشگر نگاهی بیندازید.

مرورگرهای هدفمند

Babel تعدادی از جزئیات را در مورد فرآیند کامپایل در کنسول ثبت می کند، از جمله تمام محیط های هدف که کد برای آنها کامپایل شده است.

مرورگرهای هدفمند

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

Babel همچنین لیستی از افزونه های تبدیل استفاده شده را ثبت می کند:

لیست پلاگین های استفاده شده

این یک لیست نسبتا طولانی است! اینها همه افزونه‌هایی هستند که Babel برای تبدیل هر نحو ES2015+ به نحو قدیمی‌تر برای همه مرورگرهای مورد نظر باید استفاده کند.

با این حال، Babel هیچ پلی پر خاصی را که استفاده می شود نشان نمی دهد:

هیچ پلی فیل اضافه نشده است

این به این دلیل است که کل @babel/polyfill مستقیماً وارد می‌شود.

پلی فیل ها را به صورت جداگانه بارگیری کنید

به‌طور پیش‌فرض، Babel شامل تمام پلی‌فیل‌های مورد نیاز برای یک محیط کامل ES2015+ می‌شود که @babel/polyfill به یک فایل وارد می‌شود. برای وارد کردن polyfill های خاص مورد نیاز برای مرورگرهای هدف، یک useBuiltIns: 'entry' را به پیکربندی اضافه کنید.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
        "useBuiltIns": "entry"
      }
    ]
  ]
}

برنامه را دوباره بارگیری کنید. اکنون می توانید تمام پلی فیل های خاص را مشاهده کنید:

لیست پلی فیل های وارد شده

اگرچه اکنون فقط پلی‌فیل‌های مورد نیاز برای "last 2 versions" گنجانده شده است، اما هنوز یک لیست فوق العاده طولانی است! این به این دلیل است که polyfill های مورد نیاز برای مرورگرهای هدف برای هر ویژگی جدیدتر هنوز هم گنجانده شده است. مقدار مشخصه را به usage تغییر دهید تا فقط موارد مورد نیاز برای ویژگی هایی را که در کد استفاده می شود شامل شود.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true,
        "useBuiltIns": "entry"
        "useBuiltIns": "usage"
      }
    ]
  ]
}

با این کار، پلی‌فیل‌ها به‌طور خودکار در صورت نیاز گنجانده می‌شوند. این به این معنی است که می توانید @babel/polyfill import را در src/index.js.

import "./style.css";
import "@babel/polyfill";

اکنون فقط پلی پرهای مورد نیاز برای برنامه گنجانده شده است.

لیست پلی فیل ها به طور خودکار گنجانده شده است

اندازه بسته نرم افزاری به طور قابل توجهی کاهش می یابد.

حجم بسته به 30.1 کیلوبایت کاهش یافت

محدود کردن لیست مرورگرهای پشتیبانی شده

تعداد اهداف مرورگر گنجانده شده هنوز بسیار زیاد است و کاربران زیادی از مرورگرهای متوقف شده مانند اینترنت اکسپلورر استفاده نمی کنند. تنظیمات را به موارد زیر به روز کنید:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "targets": [">0.25%", "not ie 11"],
        "debug": true,
        "useBuiltIns": "usage",
      }
    ]
  ]
}

به جزئیات بسته واکشی شده نگاهی بیندازید.

حجم بسته 30.0 کیلوبایت

از آنجایی که برنامه بسیار کوچک است، واقعاً تفاوت زیادی با این تغییرات وجود ندارد. با این حال، استفاده از درصد سهم بازار مرورگر (مانند ">0.25%" ) همراه با حذف مرورگرهای خاصی که مطمئن هستید کاربران شما از آنها استفاده نمی کنند، رویکرد توصیه شده است. برای کسب اطلاعات بیشتر در مورد این، به مقاله "2 نسخه آخر" که توسط جیمز کایل مضر در نظر گرفته شده است، نگاهی بیندازید.

از <script type="module"> استفاده کنید

هنوز جای پیشرفت بیشتری وجود دارد. اگرچه تعدادی از پلی پرهای استفاده نشده حذف شده اند، اما تعداد زیادی از آنها در حال ارسال هستند که برای برخی از مرورگرها مورد نیاز نیستند. با استفاده از ماژول‌ها، می‌توان سینتکس جدیدتر را مستقیماً بدون استفاده از پلی‌فیلدهای غیرضروری نوشت و به مرورگرها ارسال کرد.

ماژول های جاوا اسکریپت یک ویژگی نسبتا جدید هستند که در همه مرورگرهای اصلی پشتیبانی می شوند. ماژول ها را می توان با استفاده از ویژگی type="module" برای تعریف اسکریپت هایی که از ماژول های دیگر وارد و صادر می کنند ایجاد کرد. به عنوان مثال:

// math.mjs
export const add = (x, y) => x + y;

<!-- index.html -->
<script type="module">
  import { add } from './math.mjs';

  add(5, 2); // 7
</script>

بسیاری از ویژگی‌های جدیدتر ECMAScript قبلاً در محیط‌هایی پشتیبانی می‌شوند که از ماژول‌های جاوا اسکریپت پشتیبانی می‌کنند (به‌جای نیاز به Babel).

  • نسخه‌ای که در مرورگرهای جدیدتر که از ماژول‌ها پشتیبانی می‌کنند کار می‌کند و شامل ماژولی است که تا حد زیادی ترجمه نشده است اما اندازه فایل کوچک‌تری دارد.
  • نسخه ای که شامل یک اسکریپت بزرگتر و ترجمه شده است که در هر مرورگر قدیمی کار می کند

استفاده از ماژول های ES با Babel

برای داشتن تنظیمات جداگانه @babel/preset-env برای دو نسخه برنامه، فایل .babelrc . را حذف کنید. تنظیمات Babel را می توان با تعیین دو فرمت کامپایل متفاوت برای هر نسخه از برنامه به پیکربندی بسته وب اضافه کرد.

با افزودن یک پیکربندی برای اسکریپت قدیمی به webpack.config.js شروع کنید:

const legacyConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: false
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

توجه داشته باشید که به جای استفاده از مقدار targets برای "@babel/preset-env" ، esmodules با مقدار false استفاده می شود. این بدان معناست که Babel شامل تمام تبدیل‌ها و polyfill‌های لازم برای هدف قرار دادن هر مرورگری است که هنوز از ماژول‌های ES پشتیبانی نمی‌کند.

اشیاء entry ، cssRule و corePlugins را به ابتدای فایل webpack.config.js اضافه کنید. همه اینها بین ماژول و اسکریپت های قدیمی ارائه شده به مرورگر به اشتراک گذاشته شده است.

const entry = {
  main: "./src"
};

const cssRule = {
  test: /\.css$/,
  use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
  })
};

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"})
];

اکنون به طور مشابه، یک شیء پیکربندی برای اسکریپت ماژول زیر که در آن legacyConfig تعریف شده است ایجاد کنید:

const moduleConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].mjs"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: true
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

تفاوت اصلی در اینجا این است که پسوند فایل .mjs برای نام فایل خروجی استفاده می شود. مقدار esmodules در اینجا روی true تنظیم شده است، به این معنی که کدی که به این ماژول خروجی می‌شود یک اسکریپت کوچکتر و کمتر کامپایل شده است که در این مثال هیچ تغییری ایجاد نمی‌کند، زیرا همه ویژگی‌های استفاده شده قبلاً در مرورگرهایی که از ماژول‌ها پشتیبانی می‌کنند پشتیبانی می‌شوند.

در انتهای فایل، هر دو پیکربندی را در یک آرایه واحد صادر کنید.

module.exports = [
  legacyConfig, moduleConfig
];

اکنون هم یک ماژول کوچکتر برای مرورگرهایی که از آن پشتیبانی می کنند و هم یک اسکریپت ترجمه شده بزرگتر برای مرورگرهای قدیمی می سازد.

مرورگرهایی که از ماژول‌ها پشتیبانی می‌کنند، اسکریپت‌های دارای ویژگی nomodule را نادیده می‌گیرند. برعکس، مرورگرهایی که از ماژول ها پشتیبانی نمی کنند، عناصر اسکریپت با type="module" را نادیده می گیرند. این بدان معنی است که شما می توانید یک ماژول و همچنین یک بازگشت کامپایل شده را اضافه کنید. در حالت ایده‌آل، دو نسخه برنامه باید در index.html مانند این باشند:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>

مرورگرهایی که از ماژول ها پشتیبانی می کنند main.mjs واکشی و اجرا می کنند و main.bundle.js. مرورگرهایی که از ماژول ها پشتیبانی نمی کنند برعکس عمل می کنند.

توجه به این نکته مهم است که برخلاف اسکریپت های معمولی، اسکریپت های ماژول همیشه به طور پیش فرض به تعویق افتاده اند. اگر می خواهید اسکریپت nomodule معادل نیز به تعویق بیفتد و فقط پس از تجزیه اجرا شود، باید ویژگی defer را اضافه کنید:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>

آخرین کاری که باید در اینجا انجام شود این است که ویژگی های module و nomodule را به ترتیب به ماژول و اسکریپت قدیمی اضافه کنید، ScriptExtHtmlWebpackPlugin را در بالای webpack.config.js وارد کنید:

const path = require("path");

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

اکنون آرایه plugins را در پیکربندی ها به روز کنید تا این افزونه را در خود داشته باشد:

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"}),
  new ScriptExtHtmlWebpackPlugin({
    module: /\.mjs$/,
    custom: [
      {
        test: /\.js$/,
        attribute: 'nomodule',
        value: ''
    },
    ]
  })
];

این تنظیمات افزونه یک ویژگی type="module" برای همه عناصر اسکریپت .mjs و همچنین یک ویژگی nomodule برای همه ماژول های اسکریپت .js اضافه می کند.

ارائه ماژول ها در سند HTML

آخرین کاری که باید انجام شود خروجی هر دو عنصر قدیمی و مدرن اسکریپت در فایل HTML است. متأسفانه، افزونه ای که فایل HTML نهایی را ایجاد می کند، HTMLWebpackPlugin ، در حال حاضر خروجی اسکریپت های ماژول و نومول را پشتیبانی نمی کند . اگرچه راه‌حل‌ها و پلاگین‌های جداگانه‌ای برای حل این مشکل ایجاد شده‌اند، مانند BabelMultiTargetPlugin و HTMLWebpackMultiBuildPlugin ، یک رویکرد ساده‌تر برای افزودن عنصر اسکریپت ماژول به صورت دستی برای هدف این آموزش استفاده می‌شود.

موارد زیر را در انتهای فایل به src/index.js اضافه کنید:

    ...
    </form>
    <script type="module" src="main.mjs"></script>
  </body>
</html>

اکنون برنامه را در مرورگری بارگذاری کنید که از ماژول ها پشتیبانی می کند، مانند آخرین نسخه کروم.

ماژول 5.2 کیلوبایتی از طریق شبکه برای مرورگرهای جدیدتر واکشی شد

فقط ماژول واکشی شده است، با اندازه بسته نرم افزاری بسیار کوچکتر به دلیل اینکه تا حد زیادی ترانسفورم نشده است! عنصر دیگر اسکریپت به طور کامل توسط مرورگر نادیده گرفته می شود.

اگر برنامه را بر روی یک مرورگر قدیمی بارگیری کنید، فقط اسکریپت بزرگتر و ترجمه شده با تمام پلی پرها و تبدیل های مورد نیاز واکشی می شود. در اینجا یک اسکرین شات برای همه درخواست‌های انجام شده در نسخه قدیمی کروم (نسخه 38) آمده است.

اسکریپت 30 کیلوبایتی برای مرورگرهای قدیمی‌تر واکشی شد

نتیجه گیری

اکنون می‌دانید که چگونه از @babel/preset-env استفاده کنید تا فقط پلی‌فیل‌های لازم برای مرورگرهای هدف را فراهم کنید. همچنین می‌دانید که چگونه ماژول‌های جاوا اسکریپت می‌توانند با ارسال دو نسخه مختلف از یک برنامه کاربردی، عملکرد را بیشتر بهبود بخشند. با درک درستی از اینکه چگونه هر دو این تکنیک ها می توانند اندازه بسته شما را به میزان قابل توجهی کاهش دهند، پیش بروید و بهینه سازی کنید!

،

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

اسکرین شات برنامه

در برنامه نمونه، می‌توانید یک کلمه یا شکلک انتخاب کنید تا میزان علاقه‌تان به هر گربه را نشان دهد. وقتی روی دکمه ای کلیک می کنید، برنامه مقدار دکمه را در زیر تصویر گربه فعلی نمایش می دهد.

اندازه گیری کنید

همیشه ایده خوبی است که قبل از افزودن هر گونه بهینه سازی، یک وب سایت را بررسی کنید:

  1. برای پیش نمایش سایت، View App را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .
  2. «Control+Shift+J» (یا «Command+Option+J» در Mac) را فشار دهید تا DevTools باز شود.
  3. روی تب Network کلیک کنید.
  4. چک باکس Disable cache را انتخاب کنید.
  5. برنامه را دوباره بارگیری کنید.

درخواست اندازه باندل اصلی

بیش از 80 کیلوبایت برای این برنامه استفاده شده است! زمان آن است که بفهمیم آیا از قطعات بسته استفاده نمی شود:

  1. Control+Shift+P (یا Command+Shift+P در مک) را فشار دهید تا منوی Command باز شود. منوی فرمان

  2. وارد Show Coverage و Enter را بزنید تا تب Coverage نمایش داده شود.

  3. در تب Coverage ، روی Reload کلیک کنید تا برنامه در حین گرفتن پوشش مجدد بارگیری شود.

    بارگیری مجدد برنامه با پوشش کد

  4. به مقدار کد استفاده شده در مقابل مقدار بارگذاری شده برای بسته اصلی نگاهی بیندازید:

    پوشش کد بسته نرم افزاری

بیش از نیمی از بسته نرم افزاری (44 کیلوبایت) حتی استفاده نمی شود. این به این دلیل است که بسیاری از کدهای داخل شامل polyfills هستند تا اطمینان حاصل شود که برنامه در مرورگرهای قدیمی کار می کند.

از @babel/preset-env استفاده کنید

نحو زبان جاوا اسکریپت با استانداردی به نام ECMAScript یا ECMA-262 مطابقت دارد. نسخه های جدیدتر مشخصات هر سال منتشر می شود و شامل ویژگی های جدیدی است که پروسه پیشنهاد را پشت سر گذاشته است. هر مرورگر اصلی همیشه در مرحله متفاوتی از پشتیبانی از این ویژگی ها است.

ویژگی های ES2015 زیر در برنامه استفاده می شود:

از ویژگی ES2017 زیر نیز استفاده می شود:

با خیال راحت وارد کد منبع در src/index.js شوید تا ببینید چگونه از همه اینها استفاده می شود.

همه این ویژگی‌ها در آخرین نسخه کروم پشتیبانی می‌شوند، اما مرورگرهای دیگری که از آن‌ها پشتیبانی نمی‌کنند چطور؟ Babel ، که در برنامه گنجانده شده است، محبوب ترین کتابخانه ای است که برای کامپایل کدهایی استفاده می شود که حاوی نحو جدیدتر به کدهایی است که مرورگرها و محیط های قدیمی می توانند آن را درک کنند. این کار را به دو صورت انجام می دهد:

  • Polyfills برای تقلید توابع جدیدتر ES2015+ گنجانده شده است تا APIهای آنها حتی اگر توسط مرورگر پشتیبانی نشود قابل استفاده باشند. در اینجا یک مثال از یک polyfill متد Array.includes آورده شده است.
  • از پلاگین ها برای تبدیل کد ES2015 (یا جدیدتر) به نحو قدیمی تر ES5 استفاده می شود. از آنجایی که اینها تغییرات مربوط به نحو (مانند توابع پیکان) هستند، نمی توان آنها را با polyfills شبیه سازی کرد.

به package.json نگاه کنید تا ببینید کدام کتابخانه های Babel گنجانده شده است:

"dependencies": {
  "@babel/polyfill": "^7.0.0"
},
"devDependencies": {
  //...
  "babel-loader": "^8.0.2",
  "@babel/core": "^7.1.0",
  "@babel/preset-env": "^7.1.0",
  //...
}
  • @babel/core کامپایلر اصلی Babel است. با این کار، تمام تنظیمات Babel در یک .babelrc در ریشه پروژه تعریف می شوند.
  • babel-loader شامل Babel در فرآیند ساخت وب پک می شود.

اکنون به webpack.config.js نگاه کنید تا ببینید که چگونه babel-loader به عنوان یک قانون گنجانده شده است:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill تمام پلی‌فیل‌های لازم را برای هر ویژگی جدیدتر ECMAScript فراهم می‌کند تا بتوانند در محیط‌هایی کار کنند که از آن‌ها پشتیبانی نمی‌کنند. قبلاً در بالای src/index.js.
import "./style.css";
import "@babel/polyfill";
  • @babel/preset-env مشخص می‌کند که کدام تبدیل‌ها و polyfill‌ها برای هر مرورگر یا محیطی که به‌عنوان هدف انتخاب شده‌اند، ضروری هستند.

به فایل تنظیمات Babel، .babelrc نگاهی بیندازید تا ببینید چگونه شامل می شود:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions"
      }
    ]
  ]
}

این یک راه اندازی بابل و بسته وب است. در صورت استفاده از بسته‌بندی ماژول متفاوت از وب‌پک ، یاد بگیرید که چگونه Babel را در برنامه خود قرار دهید .

ویژگی targets در .babelrc مشخص می کند که کدام مرورگرها هدف قرار می گیرند. @babel/preset-env با فهرست مرورگرها ادغام می‌شود، به این معنی که می‌توانید فهرست کاملی از جستارهای سازگار را که می‌توان در این زمینه در مستندات فهرست مرورگر استفاده کرد، بیابید.

مقدار "last 2 versions" کد موجود در برنامه را برای دو نسخه آخر هر مرورگر انتقال می دهد.

اشکال زدایی

برای مشاهده کامل تمام اهداف Babel مرورگر و همچنین همه تبدیل‌ها و polyfill‌های موجود، یک فیلد debug را به .babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
      }
    ]
  ]
}
  • روی Tools کلیک کنید.
  • روی Logs کلیک کنید.

برنامه را دوباره بارگیری کنید و به گزارش های وضعیت اشکال در پایین ویرایشگر نگاهی بیندازید.

مرورگرهای هدفمند

Babel تعدادی از جزئیات را در مورد فرآیند کامپایل در کنسول ثبت می کند، از جمله تمام محیط های هدف که کد برای آنها کامپایل شده است.

مرورگرهای هدفمند

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

Babel همچنین لیستی از افزونه های تبدیل استفاده شده را ثبت می کند:

لیست پلاگین های استفاده شده

این یک لیست نسبتا طولانی است! اینها همه افزونه‌هایی هستند که Babel برای تبدیل هر نحو ES2015+ به نحو قدیمی‌تر برای همه مرورگرهای مورد نظر باید استفاده کند.

با این حال، Babel هیچ پلی پر خاصی را که استفاده می شود نشان نمی دهد:

هیچ پلی فیل اضافه نشده است

این به این دلیل است که کل @babel/polyfill مستقیماً وارد می‌شود.

پلی فیل ها را به صورت جداگانه بارگیری کنید

به‌طور پیش‌فرض، Babel شامل تمام پلی‌فیل‌های مورد نیاز برای یک محیط کامل ES2015+ می‌شود که @babel/polyfill به یک فایل وارد می‌شود. برای وارد کردن polyfill های خاص مورد نیاز برای مرورگرهای هدف، یک useBuiltIns: 'entry' را به پیکربندی اضافه کنید.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
        "useBuiltIns": "entry"
      }
    ]
  ]
}

برنامه را دوباره بارگیری کنید. اکنون می توانید تمام پلی فیل های خاص را مشاهده کنید:

لیست پلی فیل های وارد شده

اگرچه اکنون فقط پلی‌فیل‌های مورد نیاز برای "last 2 versions" گنجانده شده است، اما هنوز یک لیست فوق العاده طولانی است! این به این دلیل است که polyfill های مورد نیاز برای مرورگرهای هدف برای هر ویژگی جدیدتر هنوز هم گنجانده شده است. مقدار مشخصه را به usage تغییر دهید تا فقط موارد مورد نیاز برای ویژگی هایی را که در کد استفاده می شود شامل شود.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true,
        "useBuiltIns": "entry"
        "useBuiltIns": "usage"
      }
    ]
  ]
}

با این کار، پلی‌فیل‌ها به‌طور خودکار در صورت نیاز گنجانده می‌شوند. این به این معنی است که می توانید @babel/polyfill import را در src/index.js.

import "./style.css";
import "@babel/polyfill";

اکنون فقط پلی پلی های مورد نیاز برای برنامه گنجانده شده است.

لیست Polyfills به طور خودکار گنجانده شده است

اندازه بسته نرم افزاری به میزان قابل توجهی کاهش می یابد.

اندازه بسته به 30.1 کیلوبایت کاهش یافته است

محدود کردن لیست مرورگرهای پشتیبانی شده

تعداد اهداف مرورگر موجود هنوز هم بسیار بزرگ است ، و بسیاری از کاربران از مرورگرهای قطع شده مانند اینترنت اکسپلورر استفاده نمی کنند. تنظیمات را به موارد زیر به روز کنید:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "targets": [">0.25%", "not ie 11"],
        "debug": true,
        "useBuiltIns": "usage",
      }
    ]
  ]
}

نگاهی به جزئیات بسته نرم افزاری واکشی بیندازید.

اندازه بسته 30.0 کیلوبایت

از آنجا که برنامه بسیار کوچک است ، واقعاً تفاوت زیادی با این تغییرات وجود ندارد. با این حال ، استفاده از درصد سهم بازار مرورگر (مانند ">0.25%" ) به همراه حذف مرورگرهای خاص که اطمینان دارید کاربران شما از آن استفاده نمی کنند ، رویکرد توصیه شده است. نگاهی به "2 نسخه آخر" که مقاله مضر توسط جیمز کایل در نظر گرفته شده است تا اطلاعات بیشتری در مورد این موضوع کسب کند.

از <script type = "module"> استفاده کنید

هنوز فضای بیشتری برای پیشرفت وجود دارد. اگرچه تعدادی از پلی فیل های بلااستفاده برداشته شده است ، اما بسیاری از آنها ارسال می شوند که برای برخی از مرورگرها لازم نیست. با استفاده از ماژول ها ، نحو جدیدتر را می توان مستقیماً بدون استفاده از هرگونه پلی پلی غیر ضروری به مرورگرها ارسال و ارسال کرد.

ماژول های JavaScript یک ویژگی نسبتاً جدید است که در کلیه مرورگرهای اصلی پشتیبانی می شود. ماژول ها را می توان با استفاده از یک ویژگی type="module" ایجاد کرد تا اسکریپت هایی را که واردات و صادرات از سایر ماژول ها را دارند ، تعریف کنند. به عنوان مثال:

// math.mjs
export const add = (x, y) => x + y;

<!-- index.html -->
<script type="module">
  import { add } from './math.mjs';

  add(5, 2); // 7
</script>

بسیاری از ویژگی های جدید ECMAScript قبلاً در محیط هایی پشتیبانی می شوند که از ماژول های JavaScript پشتیبانی می کنند (به جای نیاز به بابل.) این بدان معنی است که می توان پیکربندی Babel را اصلاح کرد تا دو نسخه مختلف برنامه شما را به مرورگر ارسال کند:

  • نسخه ای که در مرورگرهای جدیدتر که از ماژول ها پشتیبانی می کنند کار می کند و شامل یک ماژول است که تا حد زیادی بدون انتقال است اما اندازه پرونده کوچکتر دارد
  • نسخه ای که شامل یک اسکریپت بزرگتر و پیچیده است که در هر مرورگر میراث کار می کند

با استفاده از ماژول های ES با بابل

برای داشتن تنظیمات جداگانه @babel/preset-env برای دو نسخه برنامه ، پرونده .babelrc را حذف کنید. تنظیمات Babel را می توان با مشخص کردن دو قالب مختلف تدوین برای هر نسخه از برنامه به پیکربندی Webpack اضافه کرد.

با اضافه کردن پیکربندی برای اسکریپت Legacy به webpack.config.js شروع کنید:

const legacyConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: false
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

توجه کنید که به جای استفاده از مقدار targets برای "@babel/preset-env" ، به جای آن esmodules با مقدار false استفاده می شود. این بدان معنی است که بابل شامل تمام تبدیل ها و پلی های لازم برای هدف قرار دادن هر مرورگر است که هنوز از ماژول های ES پشتیبانی نمی کند.

اشیاء entry ، cssRule و corePlugins را به ابتدای پرونده webpack.config.js اضافه کنید. اینها بین اسکریپت های ماژول و میراث که به مرورگر خدمت می کنند ، مشترک هستند.

const entry = {
  main: "./src"
};

const cssRule = {
  test: /\.css$/,
  use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
  })
};

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"})
];

اکنون به طور مشابه ، یک شیء پیکربندی را برای اسکریپت ماژول در زیر که legacyConfig تعریف شده است ایجاد کنید:

const moduleConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].mjs"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: true
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

تفاوت اصلی در اینجا در این است که از پسوند پرونده .mjs برای نام پرونده خروجی استفاده می شود. مقدار esmodules در اینجا درست تنظیم شده است به این معنی که کدی که در این ماژول وارد می شود یک اسکریپت کوچکتر و کمتری است که در این مثال از طریق تحول پیش نمی رود زیرا تمام ویژگی های مورد استفاده در مرورگرهایی که از ماژول ها پشتیبانی می کنند پشتیبانی می شوند.

در پایان پرونده ، هر دو تنظیم را در یک آرایه واحد صادر کنید.

module.exports = [
  legacyConfig, moduleConfig
];

اکنون این هم یک ماژول کوچکتر برای مرورگرهایی که از آن پشتیبانی می کنند و یک اسکریپت بزرگتر برای مرورگرهای قدیمی تر می سازد.

مرورگرهایی که از ماژول ها پشتیبانی می کنند ، اسکریپت ها را با یک ویژگی nomodule نادیده می گیرند. در مقابل ، مرورگرهایی که از ماژول ها پشتیبانی نمی کنند ، عناصر اسکریپت را با type="module" نادیده می گیرند. این بدان معنی است که شما می توانید یک ماژول و همچنین یک بازپرداخت کامپایل شده را درج کنید. در حالت ایده آل ، دو نسخه برنامه باید در index.html مانند این باشد:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>

مرورگرهایی که از ماژول ها پشتیبانی می کنند و main.mjs را اجرا می کنند و main.bundle.js. مرورگرهایی که از ماژول ها پشتیبانی نمی کنند برعکس عمل می کنند.

توجه به این نکته حائز اهمیت است که برخلاف اسکریپت های معمولی ، اسکریپت های ماژول همیشه به طور پیش فرض به تعویق می افتند. اگر می خواهید اسکریپت معادل nomodule نیز به تعویق بیفتد و فقط پس از تجزیه اجرا شود ، پس باید ویژگی defer را اضافه کنید:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>

آخرین کاری که باید در اینجا انجام شود اضافه کردن ویژگی های module و nomodule به ترتیب به ماژول و اسکریپت میراث است ، به ترتیب ScriptExTHTMlWebPackPlugin را در بالای صفحه webpack.config.js وارد کنید:

const path = require("path");

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

اکنون آرایه plugins را در تنظیمات به روز کنید تا این افزونه را شامل شود:

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"}),
  new ScriptExtHtmlWebpackPlugin({
    module: /\.mjs$/,
    custom: [
      {
        test: /\.js$/,
        attribute: 'nomodule',
        value: ''
    },
    ]
  })
];

این تنظیمات افزونه یک ویژگی type="module" برای همه عناصر اسکریپت .mjs و همچنین یک ویژگی nomodule برای همه ماژول های اسکریپت .js اضافه می کند.

خدمت به ماژول ها در سند HTML

آخرین کاری که باید انجام شود این است که هم میراث و هم عناصر اسکریپت مدرن را به پرونده HTML منتقل کنید. متأسفانه ، افزونه ای که پرونده HTML نهایی ، HTMLWebpackPlugin را ایجاد می کند ، در حال حاضر از خروجی هر دو اسکریپت ماژول و nomodule پشتیبانی نمی کند . اگرچه راه حل ها و افزونه های جداگانه ای برای حل این مشکل وجود دارد ، مانند BabelMultitargetPlugin و HTMLWebPackMultibuildPlugin ، یک رویکرد ساده تر برای افزودن عنصر اسکریپت ماژول به صورت دستی به منظور این آموزش استفاده می شود.

موارد زیر را در انتهای پرونده به src/index.js اضافه کنید:

    ...
    </form>
    <script type="module" src="main.mjs"></script>
  </body>
</html>

اکنون برنامه را در یک مرورگر که از ماژول ها پشتیبانی می کند ، مانند آخرین نسخه Chrome بارگذاری کنید.

5.2 KB ماژول برای مرورگرهای جدیدتر از طریق شبکه واگذار شد

فقط ماژول به دست می آید ، با اندازه بسته نرم افزاری بسیار کوچکتر به دلیل عدم انتقال عمدتاً! عنصر اسکریپت دیگر کاملاً توسط مرورگر نادیده گرفته می شود.

اگر برنامه را روی یک مرورگر قدیمی بارگذاری کنید ، فقط اسکریپت بزرگتر و انتقال یافته با تمام پلی پلی و تبدیل های مورد نیاز به دست می آید. در اینجا یک تصویر برای کلیه درخواست های ارائه شده در نسخه قدیمی Chrome (نسخه 38) آورده شده است.

اسکریپت 30 کیلوبایت برای مرورگرهای قدیمی تر

نتیجه گیری

شما اکنون می دانید که چگونه از @babel/preset-env استفاده کنید تا فقط پلی پلی های لازم را برای مرورگرهای هدفمند فراهم کنید. شما همچنین می دانید که چگونه ماژول های JavaScript می توانند با حمل دو نسخه مختلف از یک برنامه ، عملکرد را بیشتر بهبود بخشند. با درک مناسبی از اینکه چگونه هر دو تکنیک می توانند اندازه بسته نرم افزاری شما را به میزان قابل توجهی کاهش دهند ، بیرون بروید و بهینه سازی کنید!

،

در این CodeLab ، عملکرد این برنامه ساده را که به کاربران امکان می دهد گربه های تصادفی را ارزیابی کنند ، بهبود بخشید. بیاموزید که چگونه می توانید بسته نرم افزاری JavaScript را با استفاده از Minizming چقدر کد بهینه کنید.

عکس برنامه

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

اندازه گیری کنید

همیشه ایده خوبی است که قبل از اضافه کردن هرگونه بهینه سازی ، با بازرسی از وب سایت شروع کنید:

  1. برای پیش نمایش سایت ، برنامه View را فشار دهید. سپس تمام صفحه را فشار دهید تمام صفحه .
  2. برای باز کردن DevTools ، `Control+Shift+J` (یا` دستور+J` را در Mac) فشار دهید.
  3. روی تب Network کلیک کنید.
  4. کادر تأیید حافظه نهان را انتخاب کنید.
  5. بارگذاری مجدد برنامه

درخواست اندازه بسته نرم افزاری اصلی

بیش از 80 کیلوبایت برای این برنامه استفاده می شود! زمان برای فهمیدن اینکه آیا از قسمت هایی از بسته نرم افزاری استفاده نمی شود:

  1. Control+Shift+P (یا Command+Shift+P در Mac) فشار دهید تا منوی Command باز شود. منوی فرمان

  2. Show Coverage وارد کنید و Enter را وارد کنید تا برگه Coverage را نمایش دهید.

  3. در برگه Coverage ، برای بارگیری مجدد برنامه هنگام ضبط پوشش ، روی بارگذاری مجدد کلیک کنید.

    بارگیری مجدد برنامه با پوشش کد

  4. نگاهی بیندازید که چه مقدار کد استفاده شده است در مقابل چقدر برای بسته اصلی بارگذاری شده است:

    پوشش کد بسته نرم افزاری

بیش از نیمی از بسته نرم افزاری (44 کیلوبایت) حتی استفاده نمی شود. این امر به این دلیل است که بسیاری از کد موجود در آن شامل Polyfills است تا اطمینان حاصل شود که این برنامه در مرورگرهای قدیمی تر کار می کند.

از @babel/pretet-env استفاده کنید

نحو زبان JavaScript با استاندارد معروف به ECMAScript یا ECMA-262 مطابقت دارد. نسخه های جدیدتر از مشخصات هر ساله منتشر می شود و شامل ویژگی های جدیدی است که روند پیشنهاد را تصویب کرده اند. هر مرورگر اصلی همیشه در مرحله متفاوت پشتیبانی از این ویژگی ها قرار دارد.

ویژگی های زیر ES2015 در برنامه استفاده می شود:

از ویژگی ES2017 زیر نیز استفاده می شود:

برای دیدن نحوه استفاده از همه این موارد ، به کد منبع در src/index.js شیرجه بزنید.

همه این ویژگی ها در آخرین نسخه Chrome پشتیبانی می شوند ، اما در مورد مرورگرهای دیگر که از آنها پشتیبانی نمی کنند چیست؟ بابل ، که در برنامه گنجانده شده است ، محبوب ترین کتابخانه ای است که برای تهیه کدی که حاوی نحو جدیدتر به کدی است که مرورگرها و محیط های قدیمی تر می توانند درک کنند. این کار را از دو طریق انجام می دهد:

  • Polyfills برای تقلید از توابع جدید ES2015+ گنجانده شده است تا از API آنها استفاده شود حتی اگر توسط مرورگر پشتیبانی نشود. در اینجا نمونه ای از روش polyfill از Array.includes آورده شده است.
  • از افزونه ها برای تبدیل کد ES2015 (یا بعد از آن) به نحو ES5 قدیمی تر استفاده می شود. از آنجا که این تغییرات مرتبط با نحو (مانند توابع فلش) هستند ، نمی توان آنها را با Polyfills تقلید کرد.

به package.json نگاه کنید تا ببینید کدام یک از کتابخانه های BABEL شامل می شوند:

"dependencies": {
  "@babel/polyfill": "^7.0.0"
},
"devDependencies": {
  //...
  "babel-loader": "^8.0.2",
  "@babel/core": "^7.1.0",
  "@babel/preset-env": "^7.1.0",
  //...
}
  • @babel/core کامپایلر اصلی بابل است. با این کار ، تمام تنظیمات بابل در یک .babelrc در ریشه پروژه تعریف شده است.
  • babel-loader شامل Babel در فرایند ساخت وب است.

اکنون به webpack.config.js نگاه کنید تا ببینید که چگونه babel-loader به عنوان یک قانون گنجانده شده است:

module: {
  rules: [
    //...
    {
      test: /\.js$/,
      exclude: /node_modules/,
      loader: "babel-loader"
    }
  ]
},
  • @babel/polyfill تمام پلی پلی های لازم را برای هرگونه ویژگی جدید ECMAScript فراهم می کند تا بتوانند در محیط هایی کار کنند که از آنها پشتیبانی نمی کنند. در حال حاضر در بالای src/index.js.
import "./style.css";
import "@babel/polyfill";
  • @babel/preset-env مشخص می کند که برای هر مرورگرها یا محیط انتخاب شده به عنوان اهداف ، چه مواردی را تغییر می دهد.

نگاهی به پرونده تنظیمات بابل ، .babelrc ، برای دیدن چگونگی شامل آن:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions"
      }
    ]
  ]
}

این یک تنظیم بابل و وب است. اگر اتفاق می افتد که از یک دسته از ماژول های مختلف نسبت به وب استفاده می کنید ، چگونه Babel را در برنامه خود بگنجانید .

ویژگی targets در .babelrc مشخص می کند که کدام مرورگرها مورد هدف قرار می گیرند. @babel/preset-env با مرورگرهای لیست ادغام می شود ، به این معنی که می توانید لیست کاملی از نمایش داده های سازگار را پیدا کنید که در این زمینه در مستندات مرورگر استفاده می شود.

مقدار "last 2 versions" کد را در برنامه برای دو نسخه آخر هر مرورگر تغییر می دهد.

اشکال زدایی

برای نگاهی کامل به همه اهداف بابل مرورگر و همچنین تمام تبدیل ها و پلی فیل های موجود در آن ، یک زمینه debug را به .babelrc:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
      }
    ]
  ]
}
  • روی Tools کلیک کنید.
  • روی سیاههها کلیک کنید.

بارگذاری مجدد برنامه و نگاهی به سیاهههای مربوط به وضعیت Glitch در پایین ویرایشگر.

مرورگرهای هدفمند

بابل تعدادی از جزئیات را در مورد فرآیند تدوین ، از جمله تمام محیط های هدف که کد برای آن تهیه شده است ، به کنسول وارد می کند.

مرورگرهای هدفمند

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

بابل همچنین لیستی از افزونه های تبدیل مورد استفاده را ثبت می کند:

لیست افزونه های مورد استفاده

این یک لیست بسیار طولانی است! اینها همه افزونه هایی است که بابل برای تبدیل هرگونه نحو ES2015+ به نحو قدیمی تر برای همه مرورگرهای هدفمند استفاده می کند.

با این حال ، بابل هیچ پلی پلی خاص مورد استفاده را نشان نمی دهد:

هیچ پلی فیلی اضافه نشده است

این امر به این دلیل است که کل @babel/polyfill مستقیم وارد می شود.

Polyfills را به صورت جداگانه بارگذاری کنید

به طور پیش فرض ، Babel شامل هرگونه Polyfill مورد نیاز برای یک محیط کامل ES2015+ هنگامی است که @babel/polyfill به یک پرونده وارد می شود. برای وارد کردن پلی فیل های خاص مورد نیاز برای مرورگرهای هدف ، یک useBuiltIns: 'entry' به پیکربندی.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true
        "useBuiltIns": "entry"
      }
    ]
  ]
}

بارگذاری مجدد برنامه اکنون می توانید تمام پلی پلی های خاص را مشاهده کنید:

لیست پلی فیل های وارداتی

اگرچه اکنون فقط برای "last 2 versions" مورد نیاز Polyfills لازم است ، اما هنوز یک لیست فوق العاده طولانی است! این امر به این دلیل است که پلی فیل های مورد نیاز برای مرورگرهای هدف برای هر ویژگی جدید هنوز درج شده است. مقدار ویژگی را به usage تغییر دهید تا فقط موارد مورد نیاز برای ویژگی هایی را که در کد استفاده می شود شامل شود.

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "debug": true,
        "useBuiltIns": "entry"
        "useBuiltIns": "usage"
      }
    ]
  ]
}

با این کار ، Polyfills به طور خودکار در صورت لزوم گنجانده می شود. این بدان معنی است که می توانید واردات @babel/polyfill را در src/index.js.

import "./style.css";
import "@babel/polyfill";

اکنون فقط پلی پلی های مورد نیاز برای برنامه گنجانده شده است.

لیست Polyfills به طور خودکار گنجانده شده است

اندازه بسته نرم افزاری به میزان قابل توجهی کاهش می یابد.

اندازه بسته به 30.1 کیلوبایت کاهش یافته است

محدود کردن لیست مرورگرهای پشتیبانی شده

تعداد اهداف مرورگر موجود هنوز هم بسیار بزرگ است ، و بسیاری از کاربران از مرورگرهای قطع شده مانند اینترنت اکسپلورر استفاده نمی کنند. تنظیمات را به موارد زیر به روز کنید:

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "targets": "last 2 versions",
        "targets": [">0.25%", "not ie 11"],
        "debug": true,
        "useBuiltIns": "usage",
      }
    ]
  ]
}

نگاهی به جزئیات بسته نرم افزاری واکشی بیندازید.

اندازه بسته 30.0 کیلوبایت

از آنجا که برنامه بسیار کوچک است ، واقعاً تفاوت زیادی با این تغییرات وجود ندارد. با این حال ، استفاده از درصد سهم بازار مرورگر (مانند ">0.25%" ) به همراه حذف مرورگرهای خاص که اطمینان دارید کاربران شما از آن استفاده نمی کنند ، رویکرد توصیه شده است. نگاهی به "2 نسخه آخر" که مقاله مضر توسط جیمز کایل در نظر گرفته شده است تا اطلاعات بیشتری در مورد این موضوع کسب کند.

از <script type = "module"> استفاده کنید

هنوز فضای بیشتری برای پیشرفت وجود دارد. اگرچه تعدادی از پلی فیل های بلااستفاده برداشته شده است ، اما بسیاری از آنها ارسال می شوند که برای برخی از مرورگرها لازم نیست. با استفاده از ماژول ها ، نحو جدیدتر را می توان مستقیماً بدون استفاده از هرگونه پلی پلی غیر ضروری به مرورگرها ارسال و ارسال کرد.

ماژول های JavaScript یک ویژگی نسبتاً جدید است که در کلیه مرورگرهای اصلی پشتیبانی می شود. ماژول ها را می توان با استفاده از یک ویژگی type="module" ایجاد کرد تا اسکریپت هایی را که واردات و صادرات از سایر ماژول ها را دارند ، تعریف کنند. به عنوان مثال:

// math.mjs
export const add = (x, y) => x + y;

<!-- index.html -->
<script type="module">
  import { add } from './math.mjs';

  add(5, 2); // 7
</script>

بسیاری از ویژگی های جدید ECMAScript قبلاً در محیط هایی پشتیبانی می شوند که از ماژول های JavaScript پشتیبانی می کنند (به جای نیاز به بابل.) این بدان معنی است که می توان پیکربندی Babel را اصلاح کرد تا دو نسخه مختلف برنامه شما را به مرورگر ارسال کند:

  • نسخه ای که در مرورگرهای جدیدتر که از ماژول ها پشتیبانی می کنند کار می کند و شامل یک ماژول است که تا حد زیادی بدون انتقال است اما اندازه پرونده کوچکتر دارد
  • نسخه ای که شامل یک اسکریپت بزرگتر و پیچیده است که در هر مرورگر میراث کار می کند

با استفاده از ماژول های ES با بابل

برای داشتن تنظیمات جداگانه @babel/preset-env برای دو نسخه برنامه ، پرونده .babelrc را حذف کنید. تنظیمات Babel را می توان با مشخص کردن دو قالب مختلف تدوین برای هر نسخه از برنامه به پیکربندی Webpack اضافه کرد.

با اضافه کردن پیکربندی برای اسکریپت Legacy به webpack.config.js شروع کنید:

const legacyConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].bundle.js"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: false
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

توجه کنید که به جای استفاده از مقدار targets برای "@babel/preset-env" ، به جای آن esmodules با مقدار false استفاده می شود. این بدان معنی است که بابل شامل تمام تبدیل ها و پلی های لازم برای هدف قرار دادن هر مرورگر است که هنوز از ماژول های ES پشتیبانی نمی کند.

اشیاء entry ، cssRule و corePlugins را به ابتدای پرونده webpack.config.js اضافه کنید. اینها بین اسکریپت های ماژول و میراث که به مرورگر خدمت می کنند ، مشترک هستند.

const entry = {
  main: "./src"
};

const cssRule = {
  test: /\.css$/,
  use: ExtractTextPlugin.extract({
    fallback: "style-loader",
    use: "css-loader"
  })
};

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"})
];

اکنون به طور مشابه ، یک شیء پیکربندی را برای اسکریپت ماژول در زیر که legacyConfig تعریف شده است ایجاد کنید:

const moduleConfig = {
  entry,
  output: {
    path: path.resolve(__dirname, "public"),
    filename: "[name].mjs"
  },
  module: {
    rules: [
      {
        test: /\.js$/,
        exclude: /node_modules/,
        loader: "babel-loader",
        options: {
          presets: [
            ["@babel/preset-env", {
              useBuiltIns: "usage",
              targets: {
                esmodules: true
              }
            }]
          ]
        }
      },
      cssRule
    ]
  },
  plugins
}

تفاوت اصلی در اینجا در این است که از پسوند پرونده .mjs برای نام پرونده خروجی استفاده می شود. مقدار esmodules در اینجا درست تنظیم شده است به این معنی که کدی که در این ماژول وارد می شود یک اسکریپت کوچکتر و کمتری است که در این مثال از طریق تحول پیش نمی رود زیرا تمام ویژگی های مورد استفاده در مرورگرهایی که از ماژول ها پشتیبانی می کنند پشتیبانی می شوند.

در پایان پرونده ، هر دو تنظیم را در یک آرایه واحد صادر کنید.

module.exports = [
  legacyConfig, moduleConfig
];

اکنون این هم یک ماژول کوچکتر برای مرورگرهایی که از آن پشتیبانی می کنند و یک اسکریپت بزرگتر برای مرورگرهای قدیمی تر می سازد.

مرورگرهایی که از ماژول ها پشتیبانی می کنند ، اسکریپت ها را با یک ویژگی nomodule نادیده می گیرند. در مقابل ، مرورگرهایی که از ماژول ها پشتیبانی نمی کنند ، عناصر اسکریپت را با type="module" نادیده می گیرند. این بدان معنی است که شما می توانید یک ماژول و همچنین یک بازپرداخت کامپایل شده را درج کنید. در حالت ایده آل ، دو نسخه برنامه باید در index.html مانند این باشد:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js"></script>

مرورگرهایی که از ماژول ها پشتیبانی می کنند و main.mjs را اجرا می کنند و main.bundle.js. مرورگرهایی که از ماژول ها پشتیبانی نمی کنند برعکس عمل می کنند.

توجه به این نکته حائز اهمیت است که برخلاف اسکریپت های معمولی ، اسکریپت های ماژول همیشه به طور پیش فرض به تعویق می افتند. اگر می خواهید اسکریپت معادل nomodule نیز به تعویق بیفتد و فقط پس از تجزیه اجرا شود ، پس باید ویژگی defer را اضافه کنید:

<script type="module" src="main.mjs"></script>
<script nomodule src="main.bundle.js" defer></script>

آخرین کاری که باید در اینجا انجام شود اضافه کردن ویژگی های module و nomodule به ترتیب به ماژول و اسکریپت میراث است ، به ترتیب ScriptExTHTMlWebPackPlugin را در بالای صفحه webpack.config.js وارد کنید:

const path = require("path");

const webpack = require("webpack");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const ScriptExtHtmlWebpackPlugin = require("script-ext-html-webpack-plugin");

اکنون آرایه plugins را در تنظیمات به روز کنید تا این افزونه را شامل شود:

const plugins = [
  new ExtractTextPlugin({filename: "[name].css", allChunks: true}),
  new HtmlWebpackPlugin({template: "./src/index.html"}),
  new ScriptExtHtmlWebpackPlugin({
    module: /\.mjs$/,
    custom: [
      {
        test: /\.js$/,
        attribute: 'nomodule',
        value: ''
    },
    ]
  })
];

این تنظیمات افزونه یک ویژگی type="module" برای همه عناصر اسکریپت .mjs و همچنین یک ویژگی nomodule برای همه ماژول های اسکریپت .js اضافه می کند.

خدمت به ماژول ها در سند HTML

آخرین کاری که باید انجام شود این است که هم میراث و هم عناصر اسکریپت مدرن را به پرونده HTML منتقل کنید. متأسفانه ، افزونه ای که پرونده HTML نهایی ، HTMLWebpackPlugin را ایجاد می کند ، در حال حاضر از خروجی هر دو اسکریپت ماژول و nomodule پشتیبانی نمی کند . اگرچه راه حل ها و افزونه های جداگانه ای برای حل این مشکل وجود دارد ، مانند BabelMultitargetPlugin و HTMLWebPackMultibuildPlugin ، یک رویکرد ساده تر برای افزودن عنصر اسکریپت ماژول به صورت دستی به منظور این آموزش استفاده می شود.

موارد زیر را در انتهای پرونده به src/index.js اضافه کنید:

    ...
    </form>
    <script type="module" src="main.mjs"></script>
  </body>
</html>

اکنون برنامه را در یک مرورگر که از ماژول ها پشتیبانی می کند ، مانند آخرین نسخه Chrome بارگذاری کنید.

5.2 KB ماژول برای مرورگرهای جدیدتر از طریق شبکه واگذار شد

فقط ماژول به دست می آید ، با اندازه بسته نرم افزاری بسیار کوچکتر به دلیل عدم انتقال عمدتاً! عنصر اسکریپت دیگر کاملاً توسط مرورگر نادیده گرفته می شود.

اگر برنامه را روی یک مرورگر قدیمی بارگذاری کنید ، فقط اسکریپت بزرگتر و انتقال یافته با تمام پلی پلی و تبدیل های مورد نیاز به دست می آید. در اینجا یک تصویر برای کلیه درخواست های ارائه شده در نسخه قدیمی Chrome (نسخه 38) آورده شده است.

اسکریپت 30 کیلوبایت برای مرورگرهای قدیمی تر

نتیجه گیری

شما اکنون می دانید که چگونه از @babel/preset-env استفاده کنید تا فقط پلی پلی های لازم را برای مرورگرهای هدفمند فراهم کنید. شما همچنین می دانید که چگونه ماژول های JavaScript می توانند با حمل دو نسخه مختلف از یک برنامه ، عملکرد را بیشتر بهبود بخشند. با درک مناسبی از اینکه چگونه هر دو تکنیک می توانند اندازه بسته نرم افزاری شما را به میزان قابل توجهی کاهش دهند ، بیرون بروید و بهینه سازی کنید!