Babel-handbook使用

/post/babel-basic article cover image

Babel作为构建工具广泛采取的Javascript编译器,其转换编译、静态类型检查、动态polyfill等功能通过编写自定义插件来满足特定业务需求是工程化进阶必学知识点,此文章主要记录babel内置功能包和插件的使用

预设

预设是指根据配置加载对应插件的特定配置集合,每个预设在进行语法解析时会分别加载对应的转换插件而不用手动安装很多插件并配置

babelpreset-react

react相关预设

babelpreset-flow

flow相关预设

babelpreset-typescript

typescript相关预设

babelpreset-env

此预设是在开发中最常用的,包含了 ES3-ES2022 的所需插件,通常设置presets > useBuiltIns: "usage"搭配core-js实现动态polyfill

json
{
  "targets": "> 0.25%, not dead",
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage",
        "corejs": "3"
      }
    ]
  ]
}

<Callout>useBuiltIns默认为false,可使用usageentry.usage时会根据当前浏览器targets支持注入polyfill,entry标识需要像老版本一样手动引入@babel/polyfill相对比较繁琐</Callout>

集成包

和工具包绑定使用的功能包,从Babel7以后所有单包的使用都需要提前安装@babel/core

babelcli

多用于命令行操作

shell
pnpm add @babel/core @babel/cli

// 文件内容输出到stdout
npx babel src/index.js

// 文件编译输出
npx babel src/index.js -o dist/index.js

// 整个目录编译输出(-d: --dir)
npx babel src -d dist

// 编译附带sourcemap(-s: --sourcemap)
npx babel src/index.js -o dist/index.js -s

附带参数如下:

  • <Primary>--extensions</Primary> .ts,.js,.tsx,.jsx,.cjs,.mjs 指定babel要处理的文件后缀
  • <Primary>--ignore</Primary> src/**/*.test.js 指定babel要忽略的文件后缀
  • <Primary>--copy-files</Primary> 复制文件,默认不复制被--ignore忽略的文件可以通过--no-copy-ignored禁止
  • <Primary>--no-babelrc</Primary> 指定不使用.babelrc或.babelrc.json配置文件
  • <Primary>--config-file</Primary> 指定配置文件路径
  • <Primary>--out-file-extension</Primary> .mjs指定输出文件后缀
  • <Primary>--keep-file-extension</Primary> 指定保留原有文件后缀
  • <Primary>--presets</Primary>=@babel/preset=typescript,... 指定babel要应用的预设
  • <Primary>--plugins</Primary>=@babel/proposal-class-properties,...指定babel要应用的插件

使用npm script

json
{
  "scripts": {
    "build:dir": "pnpm babel src -d dist"
  }
}

babelregister

结合require钩子将babel注册到Node模块系统中并自动编译文件,在最细版本可以使用实验性功能 experimental-worker 替换

shell
pnpm add @babel/core @babel/register
js
console.log('this is index.js')
js
require('@babel/register')({
  plugins: [],
  presets: []
})
// 后续所有的引入都将会被babel编译
require('./index.js')
shell
node register.js
// console.log('this is index.js')

babelpolfill

v7已弃用,推荐@babel/transform-runtime搭配@babel/runtime复用帮助函数减少代码体积

babelruntime

包含了babel运行时所需的帮助函数被@babel/transform-runtime按需注入

babeltransform-runtime

此转换插件主要做了3件事:

  • 使用core-js配置选项是否自动注入对应polyfill
  • 使用helpers配置选项是否自动注入对应@babel/runtime/helpers来替代行内帮助函数
  • 使用regenerator配置选项指定是否自动注入@babel/runtime/renerator来转换generator/async语法
json
{
	"targets": "> 0.25%, not dead",
  "plugins": [
    [
      "@babel/plugin-transform-runtime",
      {
        "corejs": 3,
        "helpers": true,
        "regenerator": true
      }
    ]
  ]
}
js
var sym = Symbol();
var promise = Promise.resolve();
var check = arr.includes("yeah!");
console.log(arr[Symbol.iterator]());

// 转换后
import _getIterator from "@babel/runtime-corejs3/core-js/get-iterator";
import _includesInstanceProperty from "@babel/runtime-corejs3/core-js-stable/instance/includes";
import _Promise from "@babel/runtime-corejs3/core-js-stable/promise";
import _Symbol from "@babel/runtime-corejs3/core-js-stable/symbol";

var sym = _Symbol();
var promise = _Promise.resolve();
var check = _includesInstanceProperty(arr).call(arr, "yeah!");
console.log(_getIterator(arr));

babelstandalone

独立适用于浏览器和非node环境,不推荐生产环境使用。暂略

工具包

babelcore

多用于函数式变成方式操作都包含同/异步形式,所有转换操作将使用 本地配置文件

js
const babel = require('@babel/core')

const codeStr = `
	function foo(a, b) {
		return a + b;
	}
`

代码转换

js
/**
* @param code: string, options?: Object, callback: Function
*/
babel.transform(codeStr, { babelrc: true }, (err, result) => {
	console.log(result); // { configOoptions, code, map, ast }
})

// 异步形式
babel.transformAsync(codeStr, { babelrc: true }).then(result => {
	console.log(result); // { configOoptions, code, map, ast }
})

文件转换

根据传入文件路径进行编译

js
const indexJs = path.resolve(__dirname, './index.js')

const fileResult = babel.transformFileSync(indexJs, {})

console.log(fileResult)

babel.transformFile(indexJs, {}, (err, result) => {
	//...
})

代码解析

根据传入代码字符串返回对应ast

js
// babel.parse
const astSyncRes = babel.parseSync(codeStr)

console.log(astSyncRes) // ast

babel.parse(codeStr, {}, (err, res) => {
	// do somthing with ast
})

ast转换

js
const parseAst = babel.parseSync(codeStr, {})

babel.transformFromAst(parseAst, codeStr, {}, (err, res) => {
  const { code, map, ast } = res;
})

const astTransform = babel.tansformFromAstSync(parseAst, codeStr, {})

console.log(astTransform)

babel.parseAsnyc(codeStr)
  .then(parseAst => {
		return transformFromAstAsync(parseAst, codeStr, {})
  })
  .then(({ code, map, ast }) => {
		//...
  })

babelparser

根据源码转换ast

js
import { parse } from "@babel/parser"
const source = `
	function foo() {
		console.log(222)
	}
`
console.log(parse(source, { sourceType: 'module' }))

babelgenerator

根据ast转换代码

js
import { parse } from "@babel/parser"
import generate from "@babel/generator"
const source = `
	function foo() {
		console.log(222)
	}
`
const ast = parse(source, {
	sourceType: 'module'
})
console.log(generate(ast, {}, source));

babelcode-frame

获取源码中的具体位置,一般用于错误抛出具体位置

js
import { codeFrameColumns } from "@babel/code-frame"

const rawLines = `class Foo {
  constructor() {
    console.log("hello");
  }
}`
const location = { start: { line: 2, column: 16 } }
const result = codeFrameColumns(rawLines, location, {
  /* options */
})

console.log(result)

babeltemplate

用于操作字符串模版和对应类型ast生成,暂略

babeltraverse

结合parser遍历和更新节点,暂略

babeltypes

各种访问及断言方法的工具库,暂略