babel插件实践(一)babel编译原理分析


前言

我们都知道在前端编译构建工具出现之前,前端项目采用的基本都是es5浏览器识别的语法来实现的。例如(jquery,es5 .......)。随着前端技术的发展(es6甚至更新的语法问世),然而浏览器是不能识别这些新语法的。那么就出现了编译构建工具,其中babel扮演着举足轻重的角色。

babel是什么?

官方介绍

Babel 是一个 JavaScript 编译器,Babel 是一个工具链,主要用于将采用 ECMAScript 2015+ 语法编写的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。

简单来说就是为了保证javascript在浏览器上正常运行,需要把浏览器不识别的语法转换成浏览器识别的语法,其中转换这一部分就是babel做的事情,在计算机编程中这一步骤也被叫做编译。

其实编译涉及的东西很多,有兴趣的同学可以了解一下《编译原理》,编译原理主要包括词法分析,语法分析,语义分析,中间代码生成,代码优化,目标代码生成这几大步骤,这里就不做过多介绍了,此处省略一百万字...

其实babel的工作流程和编译原理中的编译流程相对简单。我们可以归纳如下几个步骤:

词法分析

语法分析

代码转换

代码生成

babel的整体工作流程如下:

其中分为词法分析和语法分析两步可以合并成解析(parse)过程

抽象语法树(AST)

抽象语法树是高级编程语言(Java、JavaScript等)转换成机器语言的桥梁。解析器会根据ECMAScript 标准「JavaScript语言规范」来对代码字符串进行词法分析,拆分成一个个词法单元,再遍历各个词法单元进行语法分析构造出AST。我们通过如下代码来分析原理:


词法分析

词法分析阶段是对源代码进行"分词",他接收一段源代码,然后执行一段tokenize函数,把代码分割成tokens的东西。tokens是一个数组,由一些代码碎片组成,比如:数字、标点符号、运算符号等等,例如:

这里我们利用在线工具把上述代码进行词法分析的结果如下:

词法分析工具


从词法分析结果可以看出,最终结果就是把代码解析成各个单词(let,age,+,=等等)

babel-tokenizer方法实现

语法分析

在词法分析之后,语法分析会把词法分析得到的tokens转化为AST,有兴趣的可以阅读一下babel源码

babel转换AST源码

AST抽象语法树是babel插件的核心概念,在编写自定义babel插件也会用到,因为在代码转换其实就是针对AST语法树各个节点进行的操作

下边推荐一个在线生成AST语法树工具

生成的AST太长,这里不展示了,有兴趣的可以在线尝试。

AST树,顾名思义数据结构中典型的一种数据类型-树,那么我们也知道,树都有一个根节点,也会有许多子节点。AST语法树是会有一个type值是Program的根节点,如下


经过观察子节点,其实子节点(包括根节点)都有相同的数据结构,如下


以上只是列举了几个不同类型的节点(注意:出于简化的目的移除了某些属性),其实AST语法树就是由这些节点组成的,它们组合在一起可以描述用于静态分析的程序语法。

从上边可以得出结论:每一个节点都有一个type字段代表节点的类型,还定义了一些附加属性用来进一步描述该节点类型。

babel编译

babel编译流程代码演示

上边我们也给出了babel编译代码的流程图,下边我们具体实践一下babel编译流程

这里先简单创建一个空项目,步骤如下:

创建一个文件夹,使用npm init -y创建package.json

然后在项目下创建src/index.js文件 


然后在项目下创建src/index.js文件

为方便我们后边打断点debug,这里我们利用vscode工具给我们生成一个launch.js文件,添加自己的launch配置

我的launch.js内容如下:


具体配置请小伙伴们搜一下...

然后我们点想要断点的地方打上断点,击上图debug按钮运行即可,如下


更多关于vscode调试工具请自行学习,这里不做过多讲述

接下来正式回到babel编译正题,我们需要安装3个babel官方提供的插件

npm install-D@babel/parser@babel/generator@babel/traverse

@babel/parser 官方地址

@babel/traverse 官方地址

@babel/generator 官方地址

接下来了解一下这3个包的简单用法,修改src/index.js代码如下


以上只是简单的用代码形式演示了babel是如何编译代码的。

编译生成的代码如下


这里和源代码比较一下发现没有什么差别,因为我们没有使用插件对代码进行操作(压缩,混淆,优化等等)

@babel/parser包的parse方法传入源代码,进行词法分析合语法分析,最终生成AST抽象语法树

@babel/traverse包traverse方法接收AST抽象语法树并对其进行遍历(深度遍历),在此过程中对节点进行添加、更新及移除等操作。 这是Babel或是其他编译器中最复杂的过程,同时也是插件将要介入工作的地方,插件部分我们后边在讲

@babel/generator包generator方法接收的AST抽象语法树转换成字符串形式的代码,同时还会创建源码映射(sourceMap,根据传入的参数控制是否生成sourceMap)

上边也提到了,@babel/traverse的traverse转换过程是深度遍历整颗树对节点进行操作,它会访问树中的所有节点。这时候该方法第二个参数就起到作用了。这个参数是一个对象,对象每个属性是一个钩子函数。这个对象的属性值除了支持AST语法树节点的type值外,还有enter,exit;也就是在遍历每个节点的时候会先进入enter钩子函数,如果存在该节点对应的钩子函数,还会执行该钩子函数,最后在访问该节点结束的时候执行exit钩子函数...

修改转换代码如下:


再次debug运行代码


从上边打印结果可以看出,遍历到每个节点时都有执行enter,exit函数。合AST抽象语法树对比,也能看出确实属于深度优先递归遍历

接下来我们在添加VariableDeclaration钩子函数代码如下,

再次debug运行代码,VariableDeclaration函数会执行一次,因为我们这个AST语法树只有一个VariableDeclaration类型的节点。

到这里,相信很多小伙伴注意到了,钩子函数path参数是做什么的?

path代表着在遍历AST的过程中连接两个节点的路径,你可以通过path.node获取当前的节点path.parent.node获得父节点,它也提供了path.replaceWith,path.remove等API,这样就能通过一定条件来获取特点的节点进行修改了。

到这里可能有的小伙伴还有一个问题,babel可能定义了很多节点类型,我们怎么知道不同类型的节点是什么呢?

官方给出了所有类型点我查看类型,这里类型太多了,现用现查文档吧!!!

@babel/types

这里小编也推荐一个插件@babel/types,该插件包含非常多api,官方文档。它的作用是创建、修改、删除、查找ast节点。另外从上边知道AST的节点也是分为多种类型,比如ExpressionStatement是表达式、ClassDeclaration是类声明、VariableDeclaration是变量声明等等,同样的这些类型都对应了其创建方法:t.expressionStatement、t.classDeclaration、t.variableDeclaration,也对应了判断方法:t.isExpressionStatement、t.isClassDeclaration、t.isVariableDeclaration。这个插件往往和traverse遍历插件一起使用,因为types只能对单一节点进行操作,一般是在对节点的深度遍历中使用。

相信到这里,小伙伴们对babel编译原理已经有了基本了解,并且对AST抽象语法树也有了了解。下一边文章我们来实践一下怎么编写一个babel插件

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容