babel

babel

转移esnext, typescript, flow等到目标环境支持的js
一些特定用途的代码转换
代码的静态分析
linter工具分析ast,检查代码规范。api文档自动生成工具。type checker代码一致性。压缩混淆工具。js解释器

编译器和转移器

编译的定义就是从一种编程语言转换为另一种编程语言。
转移就是高级语言到低级语言的转换工具。

babel的编译流程

  • parse: 通过parser把源码转成抽象语法树
  • transform: 遍历ast,调用transform插件对ast进行增删改
  • generate: 把转换后的ast打印成目标代码,生成sourcemap

parse

parse把源码字符串转换成机器能够理解的ast。

transform

transform对parse生成的ast的处理,进行ast的遍历,遍历的过程中处理到不同的ast节点会调用注册的相应的visitor函数,visitor函数里可以对ast节点进行增删改,返回新的ast

generate

generate节点会把ast打印成目标代码字符串,生成sourcemap。不同的ast对应不同结构的字符串。

常见的ast节点

Literal

  • StringLiteral字符串字面量
  • NumericLiteral数字字面量
  • BooleanLiteral布尔字面量
  • RegExpLiteral正则表达式字面量
    在这里插入图片描述

Identifier

Identifer 是标识符的意思,变量名、属性名、参数名等各种声明和引用的名字,都是Identifer。

Statement

statement 是语句,它是可以独立执行的单位,比如 break、continue、debugger、return 或者 if 语句、while 语句、for 语句,还有声明语句,表达式语句等。

Declaration

声明语句是一种特殊的语句,它执行的逻辑是在作用域内声明一个变量、函数、class、import、export 等。

Expression

expression 是表达式,特点是执行完以后有返回值

Class

整个 class 的内容是 ClassBody,属性是 ClassProperty,方法是ClassMethod(通过 kind 属性来区分是 constructor 还是 method)。
在这里插入图片描述

Modules

import
import {c, d} from 'c'
import a from 'a'
import * as b from 'b'

在这里插入图片描述

export
export {b, d}
export default a 
export * from 'c'

在这里插入图片描述

Program & Directive

program 是代表整个程序的节点,它有 body 属性代表程序体,存放 statement 数组,就是具体执行的语句的集合。还有 directives 属性,存放 Directive 节点,比如"use strict" 这种指令会使用 Directive 节点表示。Program 是包裹具体执行语句的节点,而 Directive 则是代码中的指令部分。
在这里插入图片描述

File & Comment

babel 的 AST 最外层节点是 File,它有 program、comments、tokens 等属性,分别存放 Program 程序体、注释、token 等,是最外层节点。注释分为块注释和行内注释,对应 CommentBlock 和 CommentLine 节点。

在这里插入图片描述

AST的公共属性

  • type: AST节点的类型
  • start, end, loc: tart 和 end 代表该节点在源码中的开始和结束下标。而 loc 属性是一个对象,有 line 和 column 属性分别记录开始和结束的行列号。
  • leadinmgComments, innerCOmments, trailingComments: 开始的注释,中间的注释,结尾的注释

babel的api

parse

@babel/parser

transform

@babel/tranverse
traverse(ast, {
  FunctionDeclaration: {
      enter(path, state) {}, // 进入节点时调用
      exit(path, state) {} // 离开节点时调用
  }
})

同一个visitor函数可以用于多个ast节点的处理,方式是指定一系列的ast,用|连接

// 进入 FunctionDeclaration 和 VariableDeclaration 节点时调用
traverse(ast, {
    'FunctionDeclaration|VariableDeclaration'(path, state){}
})

每个visitor都有path和state的参数

path
  • path记录了遍历过程中的路径
  • path.node 指向当前 AST 节点
  • path.parent 指向父级 AST 节点
  • path.getSibling、path.getNextSibling、path.getPrevSibling 获取兄弟节点
  • path.find 从当前节点向上查找节点
  • path.get、path.set 获取 / 设置属性的 path
  • path.scope 获取当前节点的作用域信息
  • path.isXxx 判断当前节点是不是 xx 类型
  • path.assertXxx 判断当前节点是不是 xx 类型,不是则抛出异常
  • path.insertBefore、path.insertAfter 插入节点
  • path.replaceWith、path.replaceWithMultiple,replaceWithSourceString 替换节点
  • path.remove 删除节点
  • path.skip 跳过当前节点的子节点的遍历
  • path.stop 结束后续遍历
state

参数state 则是遍历过程中在不同节点之间传递数据的机制,插件会通过 state 传递 options 和 file 信息,我们也可以通过 state 存储一些遍历过程中的共享数据。

@babel/types

ast判断
isXxx 会返回 boolean,而 assertXxx 则会在类型不一致时抛异常。

t.ifStatement(test, consequent, alternate);
t.isIfStatement(node, opts);
t.assertIfStatement(node, opts);
t.isIdentifier(node, { name: "paths" })
@babel/template
const ast = template(code, [opts])(args);
const ast = template.ast(code, [opts]);
const ast = template.program(code, [opts]);
  • template.ast 返回的是整个 AST。
  • template.program 返回的是 Program 根节点。
  • template.expression 返回创建的 expression 的 AST。
  • template.statements 返回创建的 statems 数组的 AST。

generate

@babel/generator

AST 转换完之后就要打印成目标代码字符串,通过 @babel/generator 包的 generate api

function (ast: Object, opts: Object, code: string): {code, map} 

第一个参数是要打印的 AST。
第二个参数是 options,指定打印的一些细节,比如通过 comments 指定是否包含注释,通过 minified 指定是否包含空白字符。
options 中常用的是 sourceMaps,开启了这个选项才会生成 sourcemap。

@babel/code-frame

options 可以设置 highlighted (是否高亮)、message(展示啥错误信息)。

const result = codeFrameColumns(rawLines, location, {
  /* options */
});
@babel/core

babel 基于这些包来实现编译、插件、预设等功能的包就是 @babel/core。

transformSync(code, options); // => { code, map, ast }

transformFileSync(filename, options); // => { code, map, ast }

transformFromAstSync(
  parsedAst,
  sourceCode,
  options
); // => { code, map, ast }
source-map

生成source-map

  1. 创建一个SouceMapGenerator对象
  2. 通过addMapping方法添加映射
  3. 通过toString转换sourcemap字符串
var map = new SourceMapGenerator({
  file: "source-mapped.js"
});

map.addMapping({
  generated: {
    line: 10,
    column: 35
  },
  source: "foo.js",
  original: {
    line: 33,
    column: 2
  },
  name: "christopher"
});

console.log(map.toString());
// '{"version":3,"file":"source-mapped.js",
//   "sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'

plugin的格式

返回对象的函数

export default function(api, options, dirname) {
  return {
    inherits: parentPlugin,
    manipulateOptions(options, parserOptions) {
        options.xxx = '';
    },
    pre(file) {
      this.cache = new Map();
    },
    visitor: {
      StringLiteral(path, state) {
        this.cache.set(path.node.value, 1);
      }
    },
    post(file) {
      console.log(this.cache);
    }
  };
} 
  • api: babel的api
  • options:外面传入的参数
  • dirname: 目录名
  • inherits: 继承某个插件,和当前插件的options合并
  • visitor: 指定traverse调用的函数
  • pre和post: 遍历前后调用
  • manipulateOptions: 用于修改options
    插件做的事情就是通过api拿到types、template等,通过state.opts拿到参数,然后通过path来修改AST。可以通过 state 放一些遍历过程中共享的数据,通过file放一些整个插件都能访问到的一些数据,除了这两种之外,还可以通过this来传递本对象共享的数据。

对象

export default plugin = {
    pre(state) {
        this.cache = new Map()
    },
    visitor: {
        StringLiteral(path, state) {
            this.cache.set(path.node.value, 1)
        }
    },
    post(state) {
        console.log(this.cache)
    }
}

preset

plugin 是单个转换功能的实现,当 plugin 比较多或者 plugin 的 options 比较多的时候就会导致使用成本升高。这时候可以封装成一个 preset,用户可以通过 preset 来批量引入 plugin 并进行一些配置。preset 就是对 babel 配置的一层封装。

export default function(api, options) {
    return {
        plugins: ['pluginA'],
        presets: [['presetsB'], {options: 'bbb'}]
    }
}

preset 和 plugin 从形式上差不多,但是应用顺序不同。
babel 会按照如下顺序处理插件和 preset:先应用 plugin,再应用 preset。plugin 从前到后,preset 从后到前

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值