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