Babel - Loose Mode

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