Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Babel的那些库 #29

Open
varHarrie opened this issue Mar 6, 2019 · 0 comments
Open

Babel的那些库 #29

varHarrie opened this issue Mar 6, 2019 · 0 comments

Comments

@varHarrie
Copy link
Owner

varHarrie commented Mar 6, 2019

自从接触ES2015开始,babel就一直在用,每个项目都在用。每次初始化项目后,.babelrc@babel/core@babel/cli@babel/preset-env@babel/transform-runtime等等信手拈来,但是每次想要区别core-js@babel/poly-fill@babel/runtime@babel/plugin-transform-runtime@babel/preset-env,却又含糊不清,索性来一次整理。

我们都知道,ES的每次标准公布,都会有许多新的特性,而这些特性无非分为两部分,一部分是新语法,如箭头函数解构赋值扩展运算符for...infor...ofclassasync/await等,另一部分是API扩展,如PromiseMapSetArray.fromString.prototype.includes等。

在低版本的浏览器中要使用高版本的ES特性,就需要对其代码进行转换。对于新语法,需要通过语法转换,比如将箭头函数转化为普通函数,将赋值结构转换成单独取值,将扩展运算符转换成Object.assign等实现,而这就是babel的核心工作。对于新API,我们是可以通过JavaScript原有的语法去实现的,实现类似功能的库叫做polyfill

当然,也不是所有API都可以通过polyfill实现,比如Proxy

core-js

以模块化方式实现了ES6+的标准API,包括PromiseSymbolMapSet等,还有各种原型链上的扩展,如String.prototype.incluesArray.prototype.find等。说白了就是polyfill,对于箭头函数async/await语法,自然是无能为力的。

我们可以通过引入直接使用:

// import 'core-js' // 引入所有
import 'core-js/features/array/find';
import 'core-js/features/promise';

[1, 2, 3].find((i) => i === 1);
Promise.resolve(64).then((x) => console.log(64));

当然,这么做会直接污染全局环境,例如Promise会挂在到window上,find会挂在到Array.prototype上,也可以通过命名空间方式引入:

import find from 'core-js-pure/features/array/from';
import Promise from 'core-js-pure/features/promise';

@babel/polyfill

源码中可以看到,@babel/polyfill就是简单地直接引用core-jsregenerator-runtime。因此,它会将扩展的API直接挂载到全局(window)和对应的原型(prototype)上,但是这样会导致一些问题:

  • 所有的API都会一并引入,极大增加了打包后的体积。假如只用到了Promise特性,但是整个包的特性都打包进去了。
  • 污染了全局环境,如果在一个工具库中引入,那么所有依赖这个库的项目都会引入这个polyfill。

使用方式很简单,写在入口文件开头:

import '@babel/polyfill';

regenerator-runtime,一个提供用于Generator和异步函数的运行时。

@babel/plugin-transform-runtime

可以在一定程度上替换@babel/polyfill来使用,主要解决了两个问题:

  1. 避免产生多次的helper函数。

    babel在对新语法转换过程当中,会借助一些helper函数来实现,比如class语法:

    // 输入:
    class Circle {}
    
    // 输出:
    function _classCallCheck(instance, Constructor) {
      //...
    }
    
    var Circle = function Circle() {
      _classCallCheck(this, Circle);
    };

    这里的_classCallCheck每次转换class语法都会产生,数量多起来极大影响最终的代码体积。为了解决这个问题,有个@babel/runtime专门封装了这些helper函数。通过@babel/plugin-transform-runtime将这些helper函数的引用替换为从@babel/runtime中引入。

  2. 解决@babel/polyfill中污染全局环境和体积过大的问题。

    @babel/plugin-transform-runtime中维护了一份API和core-js特性模块间的映射,可以根据具体使用情况,按需自动引入这些core-js中的特性模块。

    // 输入:
    const p = Promise.resolve()
    
    // 输出:
    var _promise = require("@babel/runtime-corejs2/core-js/promise");
    
    var _promise2 = _interopRequireDefault(_promise);
    
    var p = _promise2.default.resolve();

但是,为了防止污染现有的运行环境,对于实例函数(如Array.prototype.find)并不支持。

babel-runtime6.x版本中,还集成了[email protected]/library(相当于core-js-pure),在7.x版本这部分被移除了,如果有需要,可以使用@babel/runtime-corejs2代替。

@babel/preset-env

在了解@babel/preset-ev先说说babel的编译过程以及pluginpreset的概念。

babel-core作为的编译过程主要分为三个步骤:

  1. 解析:通过@babbel/parser将代码转换为AST
  2. 转换:babel本身不具备代码编译转换功能,而是依赖于一个个plugin来进行处理,转换过后的代码仍然是AST
  3. 生成:通过@babel/generatorAST转换为JS代码。

所以说,plugin中实现了具体的转换功能。比如要将箭头函数转化为普通函数,就需要@babel/plugin-transform-arrow-functions,要将class语法转化为函数实现,就需要@babel/plugin-transform-classes

preset则代表一组plugin。比如需要转换jsx的语法,只需要@babel/preset-react就行了,它已经包含了@babel/plugin-transform-react-jsx@babel/plugin-transform-react-display-name@babel/plugin-transform-react-jsx-source@babel/plugin-transform-react-jsx-self

同理@babel/preset-env也代表了一组plugin。通过默认的配置,我们就能够使用ES最新规范中的语法,当然也可以通过配置来达到你需要的效果。

总结

最后简单总结一下:

  • @babel/polyfill为运行环境提供能完整的ES API,但是体积较大,新的语法如环境不支持还是不支持。
  • @babel/plugin-transform-runtime能够按需自动引入ES API,能够有效控制体积,但是不支持函数实例方法,不对语法进行转换。
  • @babel/preset-env集成若干plugin,能将ES5+语法转换成ES5语法,不集成ES API的polyfill。
@varHarrie varHarrie changed the title Babel Babel的那些库 Mar 6, 2019
@varHarrie varHarrie added this to the Posts milestone Aug 5, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant