Покажите мне код, лучшие практики Babel 7!

внешний интерфейс JavaScript браузер Webpack Babel
Покажите мне код, лучшие практики Babel 7!

предисловие

Эта статья была впервые опубликована вблог на гитхабе
Я рад помочь вам,твоя звездаЭто моя самая большая поддержка!

Всем известно, что babel — это компилятор преобразования, совместимый с браузерами с низкими версиями, которые не полностью поддерживают ES6.

И Babel на самом деле в основном делает две вещи:

  • Преобразование синтаксиса
  • Совместимость с новым полифиллом API

Итак, без лишних слов, давайте просто поговорим о решениях, совместимых со старыми браузерами в нескольких распространенных сценариях.

Перейти к заключению

программа практики

polyfill.io

Если в вашем проекте используется синтаксис ES5, но используются некоторые функции API ES6+, вы можете напрямую импортировать:

<script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>

для совместимости с API, не поддерживаемыми веб-приложениями.

Принцип наверноеpolyfill.ioЗаголовок User-Agent каждого запроса считывается, и возвращается полифилл, подходящий для запрашивающего браузера. В частности, вы также можете указать, какие полифиллы загружать самостоятельно.Если вы хотите узнать об этом больше, вы можете проверить это.официальная документация.

Преимущества: полифиллы, загружаемые каждым устройством браузера, разные.Последние полностью совместимые браузеры ES6 в основном загружают полифиллы с размером 0.

недостаток:

  1. Сначала необходимо выполнить преобразование синтаксиса. Использование асинхронного синтаксиса работает в новых браузерах, но вызывает ошибки непосредственно в старых браузерах.
  2. Вы не можете полифилить по требованию новые функции, используемые вашим кодом, то есть даже если ваше веб-приложение использует толькоes6.array.fromхарактеристика,polyfill.ioВсе еще возможно загрузить все неподдерживаемые функции браузера (такие как: es6.promise, es6.string.includes и т. д.).

@babel/preset-env загружается по запросу

упомянутый вышеpolyfill.ioОдним из недостатков является то, что его нельзя ввести по запросу, поэтому давайте представим babel7 сейчас.@babel/preset-env

@babel/preset-envПо умолчанию необходимое преобразование синтаксиса кода и полифилл выполняются в соответствии с совместимыми браузерами, заполненными .browserslist.

// .babelrc.js
module.exports = {
    presets: [
        [
            "@babel/preset-env",
            
            {
                "modules": false, // 模块使用 es modules ,不使用 commonJS 规范,具体看文末附录
                "useBuiltIns": 'usage', // 默认 false, 可选 entry , usage
            }
        ]
    ]
}

Вот посмотрите на его новую опцию useBuiltIns:

  1. false : не включать полифилл, если в бизнес-записиimport '@babel/polyfill', будет игнорировать.browserslistЗагрузите все полифиллы.
    image

    Все полифиллы загружены 284 пакетами функций.
  2. запись: включить, нужно вручнуюimport '@babel/polyfill'вступить в силу (иначе будет выдана ошибка: regeneratorRuntime undefined), согласно.browserslistотфильтруйте то, что вам нужноpolyfill(аналогичныйpolyfill.ioстроить планы)
    image

    Есть 238 пакетов функций, загруженных в соответствии со списком браузеров (т.е.> 10), используя запись
  3. использование: нет необходимости вручнуюimport '@babel/polyfill'(не важно, добавите ли вы его, он автоматически удалится при компиляции), и будет основываться на.browserslist+ Polyfill новые API, используемые бизнес-кодом по мере необходимости.
    image

    Используя использование в соответствии со списком браузеров (т.е.> 10) + код, загружается только 51 пакет функций.

    элемент риска использования: поскольку мы обычно используем много пакетов зависимостей npm для развития бизнеса, babel по умолчанию не обнаружит код зависимых пакетов.

    То есть, если зависимый пакет используетArray.from, но ваш бизнес-код не использует этот API, а встроенный полифилл не будет иметь Array.from, поэтому у некоторых пользователей, использующих младшие версии браузеров, могут быть баги.

    Поэтому, чтобы избежать этой ситуации, обычно сторонние библиотеки с открытым исходным кодом конвертируются в ES5, когда они выпускаются в Интернете.


упомянутый вышеuseBuiltIns:'usage'Вроде бы он отлично решил наши задачи, но когда мы его построили, то обнаружили:

// es6+ 源码:
const asyncFun = async ()=>{
  await new Promise(setTimeout, 2000)
  
  return '2s 延时后返回字符串'
}
export default asyncFun

Согласно вышеизложенномуuseBuiltIns:'usage'После настройки и компиляции:

import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { Promise.resolve(value).then(_next, _throw); } }

function _asyncToGenerator(fn) { return function () { var self = this, args = arguments; return new Promise(function (resolve, reject) { var gen = fn.apply(self, args); function _next(value) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value); } function _throw(err) { asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err); } _next(undefined); }); }; }

var asyncFun =
/*#__PURE__*/
function () {
  var _ref = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return new Promise(setTimeout, 2000);

          case 2:
            return _context.abrupt("return", '2s 延时后返回字符串');

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function asyncFun() {
    return _ref.apply(this, arguments);
  };
}();

export default asyncFun;

В приведенном выше коде мы видим,asyncGeneratorStep, _asyncToGeneratorЭти две функции встроены, а не импортированы.

То есть,Если у вас есть несколько файлов, использующих асинхронность, каждый файл будет встроенasyncGeneratorStep, _asyncToGeneratorфункция.

Этот код явно дублируется, так есть ли способ его оптимизировать? ответ@babel/plugin-transform-runtime

@babel/plugin-transform-runtime

babel вставляет некоторый вспомогательный код в начало каждого требуемого файла, что может привести к дублированию вспомогательного кода в нескольких файлах.@babel/plugin-transform-runtimeОпции хелперов можно вытащить из этих модулей

// .babelrc.js
module.exports = {
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": false, // 默认值,可以不写
                "helpers": true, // 默认,可以不写
                "regenerator": false, // 通过 preset-env 已经使用了全局的 regeneratorRuntime, 不再需要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
                "useESModules": true, // 使用 es modules helpers, 减少 commonJS 语法代码
            }
        ]
    ],
    presets: [
        [
            "@babel/preset-env",
            
            {
                "modules": false, // 模块使用 es modules ,不使用 commonJS 规范 
                "useBuiltIns": 'usage', // 默认 false, 可选 entry , usage
            }
        ]
    ]
}
// 添加新配置后编译出来的代码
import "core-js/modules/es6.promise";
import "regenerator-runtime/runtime";
import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";

var asyncFun =
/*#__PURE__*/
function () {
  var _ref = _asyncToGenerator(
  /*#__PURE__*/
  regeneratorRuntime.mark(function _callee() {
    return regeneratorRuntime.wrap(function _callee$(_context) {
      while (1) {
        switch (_context.prev = _context.next) {
          case 0:
            _context.next = 2;
            return new Promise(setTimeout, 2000);

          case 2:
            return _context.abrupt("return", '2s 延时后返回字符串');

          case 3:
          case "end":
            return _context.stop();
        }
      }
    }, _callee, this);
  }));

  return function asyncFun() {
    return _ref.apply(this, arguments);
  };
}();

export default asyncFun;

Как видите, встроенного вспомогательного кода нет, и все готово.

Суммировать

Если особых потребностей нет, лучшая конфигурация для использования babel 7:

  1. Сначала установите зависимости:npm i -S @babel/polyfill @babel/runtime && npm i -D @babel/preset-env @babel/plugin-transform-runtime

  2. настроить.babelrc.js

// .babelrc.js
module.exports = {
    "plugins": [
        [
            "@babel/plugin-transform-runtime",
            {
                "corejs": false, // 默认值,可以不写
                "helpers": true, // 默认,可以不写
                "regenerator": false, // 通过 preset-env 已经使用了全局的 regeneratorRuntime, 不再需要 transform-runtime 提供的 不污染全局的 regeneratorRuntime
                "useESModules": true, // 使用 es modules helpers, 减少 commonJS 语法代码
            }
        ]
    ],
    presets: [
        [
            "@babel/preset-env",
            {
                "modules": false, // 模块使用 es modules ,不使用 commonJS 规范 
                "useBuiltIns": 'usage', // 默认 false, 可选 entry , usage
            }
        ]
    ]
}

ПС: если хочешьЧтобы узнать больше об использовании конфигурации параметров @babel/preset-env и @babel/plugin-transform-runtime, вы можете обратиться к моему личному резюме.

Думай и исследуй (современная сборка)

На самом деле вышеприведенное решение всегда скрывало проблему, которая не является проблемой, то есть, если вы используете последний браузер, вам не нужно никакого преобразования синтаксиса и полифилла.

Затем обратитесь к приведенной выше схеме полифилла, можно ли реализовать, что если браузер более низкой версии, то схема использования может быть использована для преобразования + полифилла кода по запросу, а если это более новый браузер, без преобразования синтаксиса и полифилла будет выполняться?

Должен уметь!

обратитесь к этой статьеdeploying es2015 code in production today, который предлагает сценарий на основе теговtype="module"а такжеnomoduleАтрибут отличает поддержку ES6 текущим браузером.

Конкретный принцип отражен в следующем коде:

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

Браузеры, поддерживающие модуль ES, могут распознаватьtype="module"а такжеnomodule, загрузитсяmain.jsхалатное отношениеmain.legacy.js,

Браузеры, которые еще не поддерживают модули ES, как раз наоборот и будут загружать толькоmain.legacy.js.

Итак, как добиться оптимизации, очень ясно:

  1. Настроив приведенные выше передовые методы работы с Babel, добавьте теги script в такие файлы кода.nomoduleАтрибуты
  2. по конфигурации@babel/preset-envОпцииtarget.esmodules = true, не преобразовывать все грамматики и не добавлять полифиллы, генерировать код ES6+, который может быть распознан и проанализирован современными браузерами, и добавлять теги script в такие файлы кода.type="module"

vue-cli 3.0 официально предоставляет современную функцию сборки

Ожидается, что приложение create-react-app будет реализовано в следующей итерации версии 3.0. На этом этапе вам нужно написать свой собственный плагин веб-пакета для реализации вставки модуля/номодуля.