最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 解剖Babel —— 向前端架构师迈出一小步

    正文概述 掘金(卡颂)   2021-02-26   794

    解剖Babel —— 向前端架构师迈出一小步

    当聊到Babel的作用,很多人第一反应是:用来实现API polyfill

    事实上,Babel作为前端工程化的基石,作用远不止这些。

    作为一个庞大的家族,Babel生态中有很多概念,比如:presetpluginruntime等。

    这些概念使初学者对Babel望而生畏,对其理解也止步于webpackbabel-loader配置。

    本文会从Babel的核心功能出发,一步步揭开Babel大家族的神秘面纱,向前端架构师迈出一小步。

    Babel是什么

    作为JS编译器,Babel接收输入的JS代码,经过内部处理流程,最终输出修改后的JS代码。

    解剖Babel —— 向前端架构师迈出一小步

    Babel内部,会执行如下步骤:

    1. Input Code解析为AST(抽象语法树),这一步称为parsing

    2. 编辑AST,这一步称为transforming

    3. 将编辑后的AST输出为Output Code,这一步称为printing

    解剖Babel —— 向前端架构师迈出一小步

    从Babel仓库的源代码,可以发现:Babel是一个由几十个项目组成的Monorepo

    其中babel-core提供了以上提到的三个步骤的能力。

    babel-core内部,更细致的讲:

    • babel-parser实现第一步

    • babel-generator实现第三步

    要了解第二步,我们需要简单了解下AST

    AST的结构

    进入AST explorer,选择@babel/parser作为解析器,在左侧输入:

    const name = ['ka', 'song'];
    

    可以解析出如下结构的AST,他是JSON格式的树状结构:

    解剖Babel —— 向前端架构师迈出一小步

    babel-core内部:

    • babel-traverse可以通过深度优先的方式遍历AST

    • 对于遍历到的每条路径,babel-types提供用于修改AST节点的节点类型数据

    所以,整个Babel底层编译能力由如下部分构成:

    解剖Babel —— 向前端架构师迈出一小步

    当我们了解Babel的底层能力后,接下来看看基于这些能力,上层能实现什么功能?

    Babel的上层能力

    基于BabelJS代码的编译处理能力,Babel最常见的上层能力为:

    • polyfill

    • DSL转换(比如解析JSX

    • 语法转换(比如将高级语法解析为当前可用的实现)

    由于篇幅有限,这里仅介绍polyfill语法转换相关功能。

    polyfill

    作为前端,最常见的Babel生态的库想必是@babel/polyfill@babel/preset-env

    使用@babel/polyfill@babel/preset-env可以实现高级语法的降级实现以及APIpolyfill

    从上文我们知道,Babel本身只是JS的编译器,以上两者的转换功能是谁实现的呢?

    答案是:core-js

    core-js简介

    core-js是一套模块化的JS标准库,包括:

    • 一直到ES2021polyfill

    • promisesymbolsiterators等一些特性的实现

    • ES提案中的特性实现

    • 跨平台的WHATWG / W3C特性,比如URL

    解剖Babel —— 向前端架构师迈出一小步

    从core-js仓库看到,core-js也是由多个库组成的Monorepo,包括:

    • core-js-builder

    • core-js-bundle

    • core-js-compat

    • core-js-pure

    • core-js

    我们介绍其中几个库:

    core-js

    core-js提供了polyfill的核心实现。

    import 'core-js/features/array/from'; 
    import 'core-js/features/array/flat'; 
    import 'core-js/features/set';        
    import 'core-js/features/promise';    
    
    Array.from(new Set([1, 2, 3, 2, 1]));          // => [1, 2, 3]
    [1, [2, 3], [4, [5]]].flat(2);                 // => [1, 2, 3, 4, 5]
    Promise.resolve(32).then(x => console.log(x)); // => 32
    

    直接使用core-js会污染全局命名空间和对象原型。

    比如上例中修改了Array的原型以支持数组实例的flat方法。

    core-js-pure

    core-js-pure提供了独立的命名空间:

    import from from 'core-js-pure/features/array/from';
    import flat from 'core-js-pure/features/array/flat';
    import Set from 'core-js-pure/features/set';
    import Promise from 'core-js-pure/features/promise';
    
    from(new Set([1, 2, 3, 2, 1]));                // => [1, 2, 3]
    flat([1, [2, 3], [4, [5]]], 2);                // => [1, 2, 3, 4, 5]
    Promise.resolve(32).then(x => console.log(x)); // => 32
    

    这样使用不会污染全局命名空间与对象原型。

    core-js-compat

    core-js-compat根据Browserslist维护了不同宿主环境、不同版本下对应需要支持特性的集合。

    解剖Babel —— 向前端架构师迈出一小步

    比如:

    "browserslist": [
        "not IE 11",
        "maintained node versions"
      ]
    

    代表:非IE11的版本以及所有Node.js基金会维护的版本。

    @babel/polyfill与core-js关系

    @babel/polyfill可以看作是:core-jsregenerator-runtime

    单独使用@babel/polyfill会将core-js全量导入,造成项目打包体积过大。

    为了解决全量引入core-js造成打包体积过大的问题,我们需要配合使用@babel/preset-env

    preset的含义

    在介绍@babel/preset-env前,我们先来了解preset的意义。

    初始情况下,Babel没有任何额外能力,其工作流程可以描述为:

    const babel = code => code;
    

    其通过plugin对外提供介入babel-core的能力,类似webpackplugin对外提供介入webpack编译流程的能力。

    plugin分为几类:

    • @babel/plugin-syntax-*语法相关插件,用于新的语法支持。比如babel-plugin-syntax-decorators提供decorators的语法支持

    • @babel/plugin-proposal-*用于ES提案的特性支持,比如babel-plugin-proposal-optional-chaining可选链操作符特性支持

    • @babel/plugin-transform-*用于转换代码,transform插件内部会使用对应syntax插件

    多个plugin组合在一起形成的集合,被称为preset

    @babel/preset-env

    使用@babel/preset-env,可以按需core-js中的特性打包,这样可以显著减少最终打包的体积。

    这里的按需,分为两个粒度:

    • 宿主环境的粒度。根据不同宿主环境将该环境下所需的所有特性打包

    • 按使用情况的粒度。仅仅将使用了的特性打包

    我们来依次看下。

    宿主环境的粒度

    当我们按如下参数在项目目录下配置browserslist文件(或在@babel/preset-envtargets属性内设置,或在package.jsonbrowserslist属性中设置):

    not IE 11
    maintained node versions
    

    会将非IE11所有Node.js基金会维护的node版本下需要的特性打入最终的包。

    显然这是利用了刚才介绍的core-js这个Monorepo下的core-js-compat的能力。

    按使用情况的粒度

    更理想的情况是只打包我们使用过的特性。

    这时候可以设置@babel/preset-envuseBuiltIns属性为usage

    比如:

    a.js

    var a = new Promise();
    

    b.js

    var b = new Map();
    

    当宿主环境不支持promiseMap时,输出的文件为:

    a.js

    import "core-js/modules/es.promise";
    var a = new Promise();
    

    b.js

    import "core-js/modules/es.map";
    var b = new Map();
    

    当宿主环境支持这两个特性时,输出的文件为:

    a.js

    var a = new Promise();
    

    b.js

    var b = new Map();
    

    进一步优化打包体积

    打开babel playground,输入:

    class App {}
    

    会发现编译出的结果为:

    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var App = function App() {
      "use strict";
    
      _classCallCheck(this, App);
    };
    

    其中_classCallCheck为辅助方法。

    如果多个文件都使用了class特性,那么每个文件打包对应的module中都将包含_classCallCheck

    为了减少打包体积,更好的方式是:需要使用辅助方法module都从同一个地方引用,而不是自己维护一份。

    @babel/runtime包含了Babel所有辅助方法以及regenerator-runtime

    单纯引入@babel/runtime还不行,因为Babel不知道何时引用@babel/runtime中的辅助方法

    所以,还需要引入@babel/plugin-transform-runtime

    这个插件会在编译时将所有使用辅助方法的地方从自己维护一份改为从@babel/runtime中引入。

    所以我们需要将@babel/plugin-transform-runtime置为devDependence,因为他在编译时使用。

    @babel/runtime置为dependence,因为他在运行时使用。

    总结

    本文从底层向上介绍了前端日常业务开发会接触的Babel大家族成员。他们包括:

    底层

    @babel/core(由@babel/parser@babel/traverse@babel/types@babel/generator等组成)

    他们提供了Babel编译JS的能力。

    中层

    @babel/plugin-*

    Babel对外暴露的API,使开发者可以介入其编译JS的能力

    上层

    @babel/preset-*

    日常开发会使用的插件集合。

    对于立志成为前端架构师的同学,Babel是前端工程化的基石,学懂、会用他是很有必要的。

    能看到这里真不容易,给自己鼓鼓掌吧。

    解剖Babel —— 向前端架构师迈出一小步


    起源地下载网 » 解剖Babel —— 向前端架构师迈出一小步

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元