Babel的那些事

640?wx_fmt=gif

点击上方蓝字,关注我们哦!!

640?wx_fmt=png

韩钰佳,2013年加入Qunar,目前在大住宿事业部,曾参与QTA,Q+及去哪儿商旅项目等,对用户端交互优化和前端自动化有兴趣。


引言

Babel 是目前最流行的 JS 编译器,它可以将新一代的 JS(ES2015,ES2016 , ES2017...)代码转为 ES5 代码。这意味着,在 Babel 出现前,你需要依据用户的浏览器版本去确定自己的代码书写方式,而使用 Babel 之后,它允许你写你想写的 JavaScript 版本(这是不是很cool~),因为我们知道 Babel 会帮助我们的代码在所有的浏览器上正确的运行。下面是一个例子:

 
 
  1. // babel转码前

  2. input.map(item => item + 1);


  3. // babel转码后

  4. input.map(function (item) {

  5.  return item + 1;

  6. });

这个是 Babel 目前最被广泛使用的功能,但其实 Babel 的能力也许并不限于此。

从Babel诞生说起

2014年9月,ES6 的很多特性已经定稿,在 JSer 圈里早已讨论地火热,但当时还没有一个浏览器能够支持 ES6 的语法,这时候 6to5(Babel的前身)出现了。短短一个月之内,被下载超过50万次,并且由于 ES7 的到来,改名为一个霸气的名字:Babel(通天塔)。 这个词来自《旧约全书》中的一个故事,说的是人类产生不同语言的起源。在这个故事中,一群只说一种语言的人在“大洪水”之后从东方来到了示拿(希伯来语:שנער‎‎)地区,并决定在这修建一座城市和一座“能够通天的”高塔;上帝见此情形就把他们的语言打乱,让他们再也不能明白对方的意思,并把他们分散到了世界各地。——维基百科。Babel 也许想做这样的创始之举,帮助 JS 开发者统一语言建造通天塔改变世界。 发展到今天,Babel项目维护着@babel/core(Babel的转译API),@babel/generator(根据 AST 生成代码)等核心包,以及 babel-plugin-xxx,babel-preset-xxx 等语法解析插件,在 Github 收获 star 3w+(截止2018年11月5日,Github 上超过3w的只有116个),俨然成为了前端开发者必备的 JS 工具。 另一方面,Babel 作为一个语法转义工具可被用于提交语法(stage-2 ,stage-3)的实现给 TC39(ESMAScript标准制定委员会)。之前的 decorator 语法,Babel 结合其社区的力量对其成为标准语法起到很大的推动作用,因此 Babel 从某种程度已经能够影响 JavaScript 语言自身的未来。

怎么使用Babel

第一步:安装Babel核心包;

npm install--save-dev@babel/core@babel/cli

第二步:创建配置文件;

在工程根目录添加配置文件 babel.config.js(babel7 新提供的一种可支持动态配置的方式,也可使用之前的静态配置.babelrc)。

 
 
  1. const presets = [];

  2. const plugins = [];

  3. module.exports = { presets, plugins };

随意新建个需转义代码 test.js,来看一下效果:

 
 
  1. let log = () => {

  2.        alert('just log');

  3.    }

在控制台运行 /node_modules/.bin/babel test.js -o new.js,生成的 new.js 与 test.js 内容完全一致,并未对 test.js 做任何转义,这是因为未配置插件的 Babel 什么也没有做。

第三步:配置plugins或presets;

安装 npm install--save-dev @babel/plugin-transform-arrow-functions @babel/plugin-transform-block-scoping,随后修改配置文件中加入例子中使用到的转义插件至plug参数(@babel/plugin-transform-arrow-functions 对箭头函数进行转义,@babel/plugin-transform-block-scoping 对 let 进行转义)。

 
 
  1. const presets = [];

  2. const plugins = ['@babel/plugin-transform-arrow-functions', '@babel/plugin-transform-block-scoping']; //新增的插件配置

  3. module.exports = { presets, plugins };

再运行 /node_modules/.bin/babel test.js -o new.js ,new.js 内容被像期望那样转义为以下格式:

 
 
  1. var log = function () {

  2.  alert('just log');

  3. }

PS:上述配置只用于说明基础配置方式,实际工程化中需要注意以下几点: 1、除了上述命令行的方式执行,也可结合 webpack 下的 babel-loader 使用,配置内容与其基本相同。 2、前端项目中可配置 preset-env 插件集,完成对 ES 新版语法的解析,可不用示例中手动填入具体插件的方式,具体 preset-env 配置方式可参照官网。(env 是一个非常强大的插件集合,可以帮助我们依据浏览器环境的代码最小解析。)

“=>” 经历Babel发生了什么

为什么箭头函数经过@babel/plugin-transform-arrow-functions插件的处理转为了正确的形式?这需要先大致讲一下 Babel 的运行原理。 执行步骤分为三步:

640?wx_fmt=png

解析

Babel 第一步会使用 @babel/parser 解析代码,输入的 javascript 代码字符串根据 ESTree 规范生成 AST(抽象语法树)。Babel 使用的解析器是 babylon(fork 自 acorn 项目)。为了更直观的理解解析过程,可以使用 AST Explorer 这个工具对解析过程进行在线预览与编辑。比如 var log =( )=>{};这个代码片段通过解析函数解析的结果如下:

640?wx_fmt=png

该JSON中代码所对应的AST信息,包含节点类型(图中的ArrowFunctionExpression,Identifier 等,定义可参照 babylon 文档),位置,以及嵌套关系等。

转换

根据一定的规则转换、修改AST。Babel提供了@babel/traverse方法维护这AST树的整体状态,并且可完成对其的替换,删除或者增加节点,这个方法的参数为原始AST和自定义的转换规则,返回结果为转换后的AST。而这部分是插件开发的核心部分。插件@babel/plugin-transform-arrow-functions的index.js 文件中可以看到就是针对Babel解析第一步所识别出的ArrowFuncionExpress节点进行处理,完成转换。

生成

使用 @babel/generator 将修改后的 AST 转换成代码,生成过程可以对是否压缩以及是否删除注释等进行配置,并且支持 sourceMap。 简单来说,Babel对代码的处理分为三步: 1、code -> AST(解析 ) 2、AST -> new AST(转换) 3、new AST -> new Code(生成)

自定义Babel开发

Babel 提倡用户开发自己的插件集合(preset)和插件(plugin)。插件集开发并不复杂,可以查询官方资源或参考 babel-preset-airbnb 完成自己所需要的预设插件集合,在此不再赘述。在自定义开发插件功能前需要了解两个简单的概念: 1、visitor: Babel 使用 babel-traverse 进行树状的遍历,对于 AST 树上的每一个分支我们都会先向下遍历走到尽头,然后向上遍历退出遍历过的节点寻找下一个分支。Babel 提供我们一个 visitor 对象供我们获取 AST 里所需的具体节点来进行访问。比如我们创建一个visitor:

 
 
  1. const MyVisitor = {

  2.  Identifier(path) {

  3.    console.log("Called!");

  4.  }

  5. };

上面代码代表遍历时,每当在树中遇见一个 Identifier(标识符类型的节点)的时候会调用 Identifier() 方法。同理我们还可以配置其他针对别的节点类型的 visitor,比如针对 if 语句的 IfStatement ( ){ } 等等。 2、path: 它是访问 visitor 时被传入的参数,其中包含父元素,节点类型,上下文等信息等,我们在 visitor 方法中可以直接对 path 中的参数取值或者进行修改和其他复杂操作,完成语法的转换。


下面编写一个简单的插件:
第一步:

新建 npm 包,命名为 babel-plugin-reverse-identifier(官方建议以 babel-plugin- 开头) 包内新建 index.js 文件, 代码内容只需将刚才讲到的 visitor 对象导出。

 
 
  1. module.exports = function() {

  2.   return {

  3.    visitor: {

  4.      Identifier(path) {

  5.            path.node.name = path.node.name.split('').reverse().join(''); //节点名称逆序

  6.      }

  7.    }

  8.  };

  9.  };

第二步:

上传至 npm 服务器。

第三步:

然后在需要使用此插件的工程中 npm install 这个包之后,增加配置 plugins: ['reverse-identifier']。 通过 Babel 命令解析原代码后,运行结果如下图,log 方法所有的标识符被逆序排列:

 
 
  1. var gol = () => {};

这个示例只是一个最简单插件的开发,复杂的操作可以学习babel插件手册进行深度的学习和开发。 了解了 Babel 的自定义开发大致方式,其实 Babel 可以做或有趣或实用的拓展。在官方的 https://github.com/babel/awesome-babel 下可以看到很多有趣的插件开发示例,比如可以对代码进行优化,语法糖,或者对于报错信息增加更详细的信息等等,等着大家的进一步探索。640?wx_fmt=jpeg

640?wx_fmt=jpeg

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值