目录

  • Babel简介
  • Babel运行原理
  • AST解析
  • AST转换
  • 写一个Babel插件

Babel简介

Babel 是一个 JavaScript 编译器,它能将es2015,react等低端浏览器无法识别的语言,进行编译。

上图的左边代码中有箭头函数,Babel将进行了源码转换,下面我们来看Babel的运行原理。

Babel运行原理

Babel 的三个主要处理步骤分别是:

解析(parse),转换(transform),生成(generate)。.

其过程分解用语言描述的话,就是下面这样:

解析

使用 <font color=Chocolate>babylon</font> 解析器对输入的源代码字符串进行解析并生成初始 AST(File.prototype.parse)

利用 <font color=Chocolate>babel-traverse</font> 这个独立的包对 AST 进行<font color=Chocolate>遍历</font>,并解析出整个树的 <font color=Chocolate>path</font>,通过挂载的 metadataVisitor 读取对应的元信息,这一步叫 set AST 过程

转换

transform 过程:遍历 AST 树并应用各 <font color=Chocolate>transformers(plugin)</font> 生成变换后的 AST 树

babel 中最核心的是 <font color=Chocolate>babel-core</font>,它向外暴露出 babel.transform 接口。

let result = babel.transform(code, {plugins: [arrayPlugin]
})

生成

利用 <font color=Chocolate>babel-generator</font> 将 <font color=Chocolate>AST</font> 树输出为转码后的代码字符串

AST解析

AST解析会把拿到的语法,进行树形遍历,对语法的每个节点进行响应的变化和改造再生产新的代码字符串

节点(node)

AST将开头提到的箭头函数转根据节点换为节点树

ES2015箭头函数

codes.map(code=>{return code.toUpperCase()
})

AST树形遍历转换后的结构

{type:"ExpressionStatement",expression:{type:"CallExpression"callee:{type:"MemberExpression",computed:falseobject:{type:"Identifier",name:"codes"}property:{type:"Identifier",name:"map"}range:[]}arguments:{{type:"ArrowFunctionExpression",id:null,params:{type:"Identifier",name:"code",range:[]}body:{type:"BlockStatement"body:{type:"ReturnStatement",argument:{type:"CallExpression",callee:{type:"MemberExpression"computed:falseobject:{type:"Identifier"name:"code"range:[]}property:{type:"Identifier"name:"toUpperCase"}range:[]}range:[]}}range:[]}generator:falseexpression:falseasync:falserange:[]}}}
}

我们从 ExpressionStatement开始往树形结构里面走,看到它的内部属性有callee,type,arguments,所以我们再依次访问每一个属性及它们的子节点。

于是就有了如下的顺序

进入  ExpressionStatement
进入  CallExpression
进入  MemberExpression
进入  Identifier
离开  Identifier
进入  Identifier
离开  Identifier
离开  MemberExpression
进入  ArrowFunctionExpression
进入  Identifier
离开  Identifier
进入  BlockStatement
进入  ReturnStatement
进入  CallExpression
进入  MemberExpression
进入  Identifier
离开  Identifier
进入  Identifier
离开  Identifier
离开  MemberExpression
离开  CallExpression
离开  ReturnStatement
离开  BlockStatement
离开  ArrowFunctionExpression
离开  CallExpression
离开  ExpressionStatement
离开  Program

Babel 的转换步骤全都是这样的遍历过程。(有点像koa的洋葱模型??)

AST转换

解析好树结构后,我们手动对箭头函数进行转换。

对比两张图,发现不一样的地方就是两个函数的arguments.type

解析代码
let babel = require('babel-core');//babel核心库
let types = require('babel-types');
let code = `codes.map(code=>{return code.toUpperCase()})`;//转换语句let visitor = {ArrowFunctionExpression(path) {//定义需要转换的节点let params = path.node.paramslet blockStatement = path.node.bodylet func = types.functionExpression(null, params, blockStatement, false, false)path.replaceWith(func) //}
}let arrayPlugin = { visitor }
let result = babel.transform(code, {plugins: [arrayPlugin]
})
console.log(result.code)

注意: ArrowFunctionExpression() { ... } 是 ArrowFunctionExpression: { enter() { ... } } 的简写形式。

<font color=Chocolate>Path 是一个对象,它表示两个节点之间的连接。</font>

解析步骤
  • 定义需要转换的节点
    ArrowFunctionExpression(path) {......}
  • 创建用来替换的节点
types.functionExpression(null, params, blockStatement, false, false)

babel-types文档链接

  • 在node节点上找到需要的参数
  • replaceWith(替换)

写一个Babel插件

从一个接收了 babel 对象作为参数的 function 开始。

export default function(babel) {// plugin contents
}

接着返回一个对象,其 visitor 属性是这个插件的主要节点访问者。

export default function({ types: t }) {return {visitor: {// visitor contents}};
};

我们日常引入依赖的时候,会将整个包引入,导致打包后的代码太冗余,加入了许多不需要的模块,比如index.js三行代码,打包后的文件大小就达到了483 KiB,

index.js

import { flatten, join } from "lodash";
let arr = [1, [2, 3], [4, [5]]];
let result = _.flatten(arr);

所以我们这次的目的是将

import { flatten, join } from "lodash";

转换为从而只引入两个lodash模块,减少打包体积

import flatten from "lodash/flatten";
import join from "lodash/join";

实现步骤如下:

  1. 在项目下的node_module中新建文件夹 <font color=Chocolate>babel-plugin-extraxt</font>

注意:babel插件文件夹的定义方式是 babel-plugin-插件名
我们可以在.babelrc的plugin中引入自定义插件 或者在webpack.config.js的loader options中加入自定义插件

  1. 在babel-plugin-extraxt新建index.js
module.exports = function ({types:t}) {return {// 对import转码visitor:{ImportDeclaration(path, _ref = { opts: {} }) {const specifiers = path.node.specifiers;const source = path.node.source;// 只有libraryName满足才会转码if (_ref.opts.library == source.value && (!t.isImportDefaultSpecifier(specifiers[0]))) { //_ref.opts是传进来的参数var declarations = specifiers.map((specifier) => {      //遍历  uniq extend flatten cloneDeepreturn t.ImportDeclaration(                         //创建importImportDeclaration节点[t.importDefaultSpecifier(specifier.local)],t.StringLiteral(`${source.value}/${specifier.local.name}`))})path.replaceWithMultiple(declarations)}}}};
}
  1. 修改<font color=Chocolate>webpack.prod.config.js</font>中babel-loader的配置项,在plugins中添加自定义的插件名
rules: [{test: /\.js$/,loader: 'babel-loader',options: {presets: ["env",'stage-0'],plugins: [["extract", { "library":"lodash"}],["transform-runtime", {}]]}
}]

注意:plugins 的插件使用顺序是顺序的,而 preset 则是逆序的。所以上面的执行方式是extract>transform-runtime>env>stage-0

  1. 运行引入了自定义插件的webpack.config.js

打包文件现在为21.4KiB,明显减小,自定义插件成功!~

插件文件目录

YUAN-PLUGINS
|
| - node_modules
|   |
|   | - babel-plugins-extract
|           |
|           index.js
|
| - src
|   | - index.js
|
| - webpack.config.js

觉得好玩就关注一下~欢迎大家收藏写评论~~~

babel插件入门-AST相关推荐

  1. Babel 插件通关秘籍

    作者介绍 某一线大厂某架构组前端工程师,公众号[神光的编程秘籍],维护公司的 builder 和 ide,对编译原理.前端工程化有一定的研究. 小册介绍 实战案例源码 babel 已经是前端领域的必备 ...

  2. babel 插件为react元素自动添加属性

    原文链接: babel 插件为react元素自动添加属性 上一篇: clip-path 绘制css常见图形 制作有趣的动画 下一篇: js 生成器 协程 参考 https://www.imliyan. ...

  3. babel从入门到入门

    博客讲解内容如下: 1.babel是什么 2.javascript制作规范 3.babel转译器 4.babel的使用 5.常见的几种babel转译器和插件 6.babel最常见配置选项 7.babe ...

  4. ES6 import代码智能转换Babel插件: babel-plugin-imports-transform

    babel-plugin-imports-transform ES6 import代码智能转换Babel插件,优化(webpack等)打包构建体积. Github地址: https://github. ...

  5. [转] 以 async/await 为例,说明 babel 插件怎么搭

    你一定碰到过这些库 babel-polyfill 项目地址:https://github.com/babel/babel/blob/master/packages/babel-polyfill 通过两 ...

  6. CALDERA入门(2)(构建插件入门)

    CALDERA入门(2)(构建插件入门) 一.编写插件代码 1.在caldera/plugins文件夹中创建文件夹test 2.在test文件夹中创建hook.py文件,以下是hook.py此次用的代 ...

  7. 在线CAD-webpack使用插件入门

    前言 webpack是用于现代 JavaScript 应用程序的静态模块打包工具,用以构建一个前端工程化项目,如vue-cli create-react-app等脚手架工具都是基于webpack的构建 ...

  8. 即时通首页html代码,Udesk即时通讯(IM)网页插件入门

    Udesk即时通讯(IM)网页插件入门 Udesk即时通讯(IM)网页插件入门 作者:张振琦 Udesk能够支持APP,微信,企业微信,微信小程序,微博,web页面,六大即时通讯渠道. web渠道只需 ...

  9. babel 插件编写

    一.开始 工具链接: 每一个节点都有如下所示的接口(Interface): interface Node {type: string; } 字符串形式的 type 字段表示节点的类型(如: " ...

  10. Babel 快速入门

    目录 一. 简介 二. 安装 babel-cli 三. 代码示例 1. 初始化项目 2. 编写es6代码 3. 编写babel配置文件 4. 安装转码器 5. 转码 方式一 方式二 一. 简介 ES6 ...

最新文章

  1. Angular property binding重复触发的问题讨论
  2. 清华大学出品:罚梯度范数提高深度学习模型泛化性
  3. Android开发笔记(十六)秋千摇摆动画SwingAnimation
  4. php5.6获取文件名,PHP 5.6:headers_sent间歇性地返回true,空文件名和第0行
  5. java 导出表 sql_java中把SQL数据库中的表导出到excel中.怎么实现
  6. php mysql搜索功能并分页_php实现搜索和分页效果-亲测有效
  7. Vue:数组的过滤排序显示客户端实现
  8. CSF 格式文件播放器 下载地址
  9. 计算机酷炫桌面,电脑桌面还能这么酷炫?!打破次元壁的桌面管理软件
  10. 晨光计算机里的铃声是什么歌曲,soul app里的音乐有哪些?soul app里的铃声有哪些?[图]...
  11. 喜报 | 谱尼测试获得零跑科技第三方试验室认可
  12. 基于蒙特卡洛法的规模化电动车有序充放电及负荷预测(PythonMatlab实现)
  13. php excel导入功能
  14. Windows10不用任何软件实现手机投屏到电脑
  15. 华为手机8.0.0怎么找到云相册_华为手机里的相册照片删除了怎么找回?
  16. vivo全球商城架构演进之路
  17. 充满希望的新的一年!
  18. 【MySQL基础知识】查询、过滤数据关键字
  19. 百度-视觉技术部招聘计算机视觉相关算法实习生
  20. Java高级---集合

热门文章

  1. 关于NDK及安装使用
  2. 【LeetCode】【数组】题号:*118,杨辉三角
  3. 模板题——质数、素数、约数
  4. 数据结构:实验三 二叉树操作实现
  5. error LNK2019: 无法解析的外部符号 _WinMain@16,该符号在函数 ___tmainCRTStartup 中被引用
  6. 【ArcGIS操作】4 空间分析篇
  7. Okhttp之RouteSelector简单解析
  8. 量子计算机具有天然的处理能力,新发现:光纤可用于构建具有强大计算与处理能力的超导量子计算机...
  9. Apache Flink 误用之痛
  10. 还想呆在公司养老?别做梦了