Babel – Loose Mode

Front End Developer at Zup - Uberlândia - MG

Saudações, pessoal! Neste artigo, iremos abordar algo que não é muito utilizado nem muito conhecido pela comunidade. Babel possui features incríveis e devemos dar mais atenção a elas para aproveitarmos o máximo que essa ferramenta oferece.

Aqui na Zup, no projeto em que trabalho, precisei encontrar algumas formas para podermos diminuir o bundle size da nossa aplicação. Então, foi necessário pesquisar e esta foi uma das várias formas que encontramos.

Loose mode está contido em vários plugins do Babel e presets e, com uma simples configuração, você poderá desfrutar de um grande desempenho.

O Básico

Antes de entrarmos em mais detalhes, vamos dar uma descrição sobre o que é este modo.

Como já foi descrito anteriormente, loose mode é uma opção presente em alguns plugins do Babel para transpilar a nova geração de ES6 para ES5. O resultado poderá ser notado no código transpilado. 

Imagine a possibilidade de chegar em um mesmo resultado com uma quantidade menor de código. Isso impacta diretamente o size do seu bundler final e você ganha performance teoricamente.

Resultado

Com loose mode, o código também é transpilado para ES5, porém o resultado não é fiel semanticamente ao código de origem. Mais a frente, iremos exemplificar algumas das features mais utilizadas e demonstrar a saída dos dois modos, ES5 com e sem loose mode.

Resumo:

  • ES5: gera um código o mais fiel possível semanticamente ao código escrito em ES6.
  • ES5 Loose mode: gera um simples código ES5, porém não fiel ao código escrito.

Quais plugins possuem?

Muitos plugins babel possuem essa opção. Aqui estão alguns deles:

  • transform-es2015-template-literals
  • transform-es2015-classes
  • transform-es2015-computed-properties
  • transform-es2015-for-of
  • transform-es2015-spread
  • transform-es2015-destructuring
  • transform-es2015-modules-commonjs

Let’s GO!

Irei demonstrar exemplos de transformações que considerei ter um maior impacto no bundle size.

Transform-es2015-classes

Neste, vamos ver como será o efeito deste modo na transpilação utilizando uma classe que aqui nomeei como PersonClass. No Construtor, ele recebe um nome e temos um método que retorna o nome no console.

Entrada:

class PersonClass {
  constructor(name) {
    this.name = name
  }

  sayName() {
    console.log(this.name)
  }
}

Saída ES5:

var _createClass = (function() {
  function defineProperties(target, props) {
    for (var i = 0; i < props.length; i++) {
      var descriptor = props[i];
      descriptor.enumerable = descriptor.enumerable || false;
      descriptor.configurable = true;
      if ("value" in descriptor) descriptor.writable = true;
      Object.defineProperty(target, descriptor.key, descriptor);
    }
  }
  return function(Constructor, protoProps, staticProps) {
    if (protoProps) defineProperties(Constructor.prototype, protoProps);
    if (staticProps) defineProperties(Constructor, staticProps);
    return Constructor;
  };
})();

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var PersonClass = (function() {
  function PersonClass(name) {
    _classCallCheck(this, PersonClass);

    this.name = name;
  }

  _createClass(PersonClass, [
    {
      key: "sayName",
      value: function sayName() {
        console.log(this.name);
      }
    }
  ]);

  return PersonClass;
})();

Saída ES5 Loose mode:

function _classCallCheck(instance, Constructor) {
  if (!(instance instanceof Constructor)) {
    throw new TypeError("Cannot call a class as a function");
  }
}

var PersonClass = (function() {
  function PersonClass(name) {
    _classCallCheck(this, PersonClass);

    this.name = name;
  }

  PersonClass.prototype.sayName = function sayName() {
    console.log(this.name);
  };

  return PersonClass;
})();

Transform-es2015-template-literals Transform-es2015-computed-properties

Neste, vamos ver qual será o efeito deste modo na transpilação utilizando template literals e computed properties.

O exemplo é uma função que recebe dois parâmetros e o retorno é um object. O primeiro parâmetro é a propriedade e o outro o valor da propriedade. A segunda propriedade está concatenando uma string qualquer utilizando template literals.

Entrada:

const propertyWithObject = (prop, value) => ({
  [prop]: value,
  [`${prop}-string`]: `${value}-string`
})

Saída ES5:

function _defineProperty(obj, key, value) {
  if (key in obj) {
    Object.defineProperty(obj, key, {
      value: value,
      enumerable: true,
      configurable: true,
      writable: true
    });
  } else {
    obj[key] = value;
  }
  return obj;
}

var propertyWithObject = function propertyWithObject(prop, value) {
  var _ref;

  return (
    (_ref = {}),
    _defineProperty(_ref, prop, value),
    _defineProperty(_ref, prop + "-string", value + "-string"),
    _ref
  );
};

Saída ES5 Loose mode:

var propertyWithObject = function propertyWithObject(prop, value) {
  var _ref;

  return (
    (_ref = {}),
    (_ref[prop] = value),
    (_ref[prop + "-string"] = value + "-string"),
    _ref
  );
};

Como utilizar?

Em seu arquivo de configuração do Babel, você deverá mencionar que a sua transpilação irá utilizar o loose mode.

Caso você queira transpilar somente alguma feature, você pode optar por plugins e habilitar o loose somente em seu plugin.

Com plugin:

{
  "plugins": [
    ["transform-es2015-classes", {
      "loose": true
    }]
  ]
}

Com Preset: Assim como os plugins, não são todos os presets que possuem tal modo. É necessário verificar a documentação do preset para consultar a informação. Neste exemplo citei o preset es2015, mas o loose mode também está presente no env.

{
  "presets": [
    ["es2015", {
      loose: true
    }]
  ]
}

Pros / Con

Dr. Axel Rauschmayer em um artigo relatou que este modo não é recomendado e postou sua posição sobre prós em contras deste modo.

Pros: Gera um código potencialmente mais rápido e mais compatível com antigos projetos e tende a ser mais limpo.

Con: Você arrisca a obter problemas mais tarde, quando você mudar de ES6 transpilado para ES6 nativo.

Com relação ao que é relatado na contraposição, ele cita que você pode obter um efeito distinto, caso um dia você venha a parar de transpilar o seu código. Possivelmente acreditando que possa haver efeitos colaterais no comportamento das features que anteriormente eram transpilados.

Quem usa?

Fiz uma pequena pesquisa e encontrei várias libs conhecidas da comunidade que utilizam loose mode.

  • Preact Fast 3kB alternative to React with the same modern API.
  • Redux   Redux is a predictable state container for JavaScript apps.
  • React   A JavaScript library for building user interfaces
  • React-Router   Declarative routing for React

BrazilJS é uma iniciativa NASC